21st.dev 원본
Human Review Panel · Extend UI
섹션 · MIT
갤러리 안에서는 화면을 벗어나거나 움직임을 멈추면 예제도 닫힙니다. 다시 재생할 때는 처음부터 시작해요.
저장한 HTML에서는 갤러리의 재생 설정이 적용되지 않아요.
SOURCE FILES
원본 코드 읽기
수집한 원본 소스와 실행 안내를 함께 제공합니다. 실행 HTML에는 미리보기를 위한 실행 환경이 들어 있습니다.
baseline-example.tsx
"use client";
import { HumanReviewPanel } from "@/components/ui/bounding-box-citations";
export default function Default() {
return (
<div className="mx-auto flex w-full max-w-2xl items-center justify-center p-6">
<div className="w-full overflow-hidden rounded-lg border bg-background shadow-sm">
<HumanReviewPanel className="!h-[680px]" />
</div>
</div>
);
}
LICENSE
MIT License
Copyright (c) 2026 CrowdView Inc, dba Extend
Portions Copyright (c) 2023 shadcn
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
함께 쓰는 파일 25개 보기
author/apps/v4/components/extend/bounding-box-citations.tsx
"use client"
import * as React from "react"
import {
DataEditor,
emptyGridSelection,
GridCellKind,
TextCellEntry,
type EditableGridCell,
type GridCell,
type GridColumn,
type GridMouseEventArgs,
type GridSelection,
type Item,
type NumberCell,
type ProvideEditorComponent,
type Rectangle,
type TextCell,
type Theme,
} from "@glideapps/glide-data-grid"
import { Virtualizer as DiffsVirtualizer } from "@pierre/diffs"
import {
File,
MultiFileDiff,
VirtualizerContext,
WorkerPoolContextProvider,
type VirtualFileMetrics,
type WorkerInitializationRenderOptions,
type WorkerPoolOptions,
} from "@pierre/diffs/react"
import { flushSync } from "react-dom"
import type { RegistryIconProps } from "@/lib/registry-icon-props"
import { cn } from "@/lib/utils"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { ScrollArea } from "@/components/ui/scroll-area"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip"
import { IconPlaceholder } from "@/components/icon-placeholder"
import "@glideapps/glide-data-grid/dist/index.css"
function InputNumericGlyph(props: RegistryIconProps) {
return (
<IconPlaceholder
lucide="Hash"
tabler="IconNumber123"
hugeicons="InputNumericIcon"
phosphor="NumpadIcon"
remixicon="RiHashtag"
{...props}
/>
)
}
function InputTextGlyph(props: RegistryIconProps) {
return (
<IconPlaceholder
lucide="TextCursorInput"
tabler="IconForms"
hugeicons="InputTextIcon"
phosphor="TextboxIcon"
remixicon="RiInputField"
{...props}
/>
)
}
function SecondBracketGlyph(props: RegistryIconProps) {
return (
<IconPlaceholder
lucide="Brackets"
tabler="IconBrackets"
hugeicons="SecondBracketIcon"
phosphor="BracketsSquareIcon"
remixicon="RiBracketsLine"
{...props}
/>
)
}
function SourceCodeSquareGlyph(props: RegistryIconProps) {
return (
<IconPlaceholder
lucide="SquareCode"
tabler="IconSourceCode"
hugeicons="SourceCodeSquareIcon"
phosphor="CodeIcon"
remixicon="RiCodeBlock"
{...props}
/>
)
}
function TextCheckGlyph(props: RegistryIconProps) {
return (
<IconPlaceholder
lucide="CaptionsIcon"
tabler="IconTextCaption"
hugeicons="TextCheckIcon"
phosphor="TextTIcon"
remixicon="RiTextWrap"
{...props}
/>
)
}
export type JsonPrimitive = string | number | boolean | null
export type JsonValue = JsonPrimitive | JsonObject | JsonArray
export type JsonObject = { [key: string]: JsonValue }
export type JsonArray = JsonValue[]
export type SchemaPropertyType =
| "string"
| "number"
| "integer"
| "boolean"
| "object"
| "array"
export type ReviewFieldSchema = {
type: SchemaPropertyType
title?: string
description?: string
enum?: Array<string | number>
properties?: Record<string, ReviewFieldSchema>
items?: ReviewFieldSchema
}
export type HighlightArea = {
left: number
top: number
width: number
height: number
}
export type ReviewField = {
key: string
schema: ReviewFieldSchema
actual: JsonValue
expected: JsonValue
location?: ReviewLocation
metadataPath?: string
}
export type ReviewLocation = {
page: number
area: HighlightArea
}
export type ReviewCitation = {
page: number
polygon?: Array<{ x: number; y: number }>
pageWidth: number
pageHeight: number
}
export type ReviewMetadataEntry = {
citations?: ReviewCitation[]
}
function ScrollAreaVirtualizer({
children,
className,
contentClassName,
contentStyle,
scrollFade = true,
}: {
children: React.ReactNode
className?: string
contentClassName?: string
contentStyle?: React.CSSProperties
scrollFade?: boolean
}) {
const [virtualizer] = React.useState(() =>
typeof window !== "undefined" ? new DiffsVirtualizer() : undefined
)
const viewportRef = React.useRef<HTMLDivElement | null>(null)
const contentRef = React.useRef<HTMLDivElement | null>(null)
const syncVirtualizer = React.useCallback(() => {
if (!virtualizer) return
const viewport = viewportRef.current
const content = contentRef.current
if (viewport && content) {
virtualizer.setup(viewport, content)
return
}
virtualizer.cleanUp()
}, [virtualizer])
const setViewportRef = React.useCallback(
(node: HTMLDivElement | null) => {
viewportRef.current = node
syncVirtualizer()
},
[syncVirtualizer]
)
const setContentRef = React.useCallback(
(node: HTMLDivElement | null) => {
contentRef.current = node
syncVirtualizer()
},
[syncVirtualizer]
)
React.useEffect(() => {
return () => virtualizer?.cleanUp()
}, [virtualizer])
return (
<VirtualizerContext.Provider value={virtualizer}>
<ScrollArea
className={className}
scrollFade={scrollFade}
scrollbarOverflowOnly
viewportRef={setViewportRef}
>
<div
ref={setContentRef}
className={contentClassName}
style={contentStyle}
>
{children}
</div>
</ScrollArea>
</VirtualizerContext.Provider>
)
}
const REVIEW_HIGHLIGHT_STYLE =
"border-blue-500/70 bg-blue-500/12 shadow-[0_4px_16px_rgb(59_130_246_/_10%)]"
const CODE_FILE_THEME = {
"--diffs-light-bg": "var(--color-code)",
"--diffs-dark-bg": "var(--color-code)",
"--diffs-light": "var(--color-code-foreground)",
"--diffs-dark": "var(--color-code-foreground)",
"--diffs-bg-context-override": "var(--color-code)",
"--diffs-bg-context-gutter-override": "var(--color-code)",
"--diffs-bg-buffer-override": "var(--color-code)",
"--diffs-fg-number-override": "var(--color-muted-foreground)",
"--diffs-font-size": "0.8rem",
"--diffs-line-height": "1.625",
} as React.CSSProperties
const CODE_FONT_SIZE_PX = 12.8
const CODE_LINE_HEIGHT_PX = CODE_FONT_SIZE_PX * 1.625
const CODE_VIRTUAL_FILE_METRICS = {
hunkLineCount: 50,
lineHeight: CODE_LINE_HEIGHT_PX,
diffHeaderHeight: 44,
spacing: 8,
paddingTop: 0,
paddingBottom: 8,
} satisfies VirtualFileMetrics
const CODE_HIGHLIGHTER_OPTIONS = {
theme: {
light: "pierre-light-soft",
dark: "pierre-dark-soft",
},
langs: ["json"],
} satisfies WorkerInitializationRenderOptions
const CODE_WORKER_POOL_OPTIONS = {
workerFactory: () =>
new Worker(new URL("@pierre/diffs/worker/worker.js", import.meta.url), {
type: "module",
}),
} satisfies WorkerPoolOptions
function readIsDarkTheme() {
return (
typeof document !== "undefined" &&
document.documentElement.classList.contains("dark")
)
}
// A single shared MutationObserver backs every consumer. Each grid previously
// created its own observer (two, via useHumanReviewGridTheme), so opening a
// nested array view spun up and tore down several observers at once.
const darkThemeListeners = new Set<(isDark: boolean) => void>()
let darkThemeObserver: MutationObserver | null = null
let sharedIsDarkTheme = false
function ensureDarkThemeObserver() {
if (
darkThemeObserver ||
typeof document === "undefined" ||
typeof MutationObserver === "undefined"
) {
return
}
sharedIsDarkTheme = readIsDarkTheme()
darkThemeObserver = new MutationObserver(() => {
const nextIsDark = readIsDarkTheme()
if (nextIsDark === sharedIsDarkTheme) return
sharedIsDarkTheme = nextIsDark
darkThemeListeners.forEach((listener) => listener(nextIsDark))
})
darkThemeObserver.observe(document.documentElement, {
attributes: true,
attributeFilter: ["class"],
})
}
function subscribeToDarkTheme(listener: () => void) {
ensureDarkThemeObserver()
darkThemeListeners.add(listener)
return () => {
darkThemeListeners.delete(listener)
if (darkThemeListeners.size === 0 && darkThemeObserver) {
darkThemeObserver.disconnect()
darkThemeObserver = null
}
}
}
function useIsDarkTheme() {
return React.useSyncExternalStore(
subscribeToDarkTheme,
readIsDarkTheme,
() => false
)
}
function useHumanReviewGridTheme() {
const isDark = useIsDarkTheme()
return React.useMemo<Partial<Theme>>(
() => ({
accentColor: isDark ? "rgb(96, 165, 250)" : "rgb(37, 99, 235)",
accentLight: isDark ? "rgba(29, 78, 216, 0.15)" : "rgb(219, 234, 254)",
accentFg: "rgb(255, 255, 255)",
textDark: isDark ? "rgb(229, 229, 229)" : "rgb(23, 23, 23)",
textMedium: isDark ? "rgb(163, 163, 163)" : "rgb(82, 82, 82)",
textLight: isDark ? "rgb(115, 115, 115)" : "rgb(163, 163, 163)",
textBubble: isDark ? "rgb(245, 245, 245)" : "rgb(23, 23, 23)",
textHeader: isDark ? "rgb(245, 245, 245)" : "rgb(23, 23, 23)",
textGroupHeader: isDark ? "rgb(163, 163, 163)" : "rgb(82, 82, 82)",
bgCell: isDark ? "rgb(10, 10, 10)" : "rgb(255, 255, 255)",
bgCellMedium: isDark ? "rgb(23, 23, 23)" : "rgb(250, 250, 250)",
bgHeader: isDark ? "rgb(23, 23, 23)" : "rgb(250, 250, 250)",
bgHeaderHasFocus: isDark ? "rgb(38, 38, 38)" : "rgb(245, 245, 245)",
bgHeaderHovered: isDark ? "rgb(38, 38, 38)" : "rgb(245, 245, 245)",
borderColor: isDark ? "rgb(38, 38, 38)" : "rgb(229, 229, 229)",
horizontalBorderColor: isDark ? "rgb(38, 38, 38)" : "rgb(229, 229, 229)",
cellHorizontalPadding: 8,
cellVerticalPadding: 3,
headerIconSize: 18,
baseFontStyle: "13px",
headerFontStyle: "600 13px",
markerFontStyle: "11px",
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
editorFontSize: "13px",
}),
[isDark]
)
}
export const REVIEW_FIELDS: ReviewField[] = [
{
key: "statement_period",
schema: {
type: "string",
title: "Statement period",
description: "Date range covered by the bank statement.",
},
actual: "Jan 1-31, 2026",
expected: "January 1-31, 2026",
location: {
page: 1,
area: { left: 31, top: 30, width: 40, height: 5.8 },
},
},
{
key: "transactions",
schema: {
type: "array",
title: "Transactions",
description: "Posted account activity during the statement period.",
items: {
type: "object",
properties: {
date: {
type: "string",
title: "Date",
},
description: {
type: "string",
title: "Description",
},
amount: {
type: "number",
title: "Amount",
},
category: {
type: "string",
title: "Category",
},
merchant: {
type: "object",
title: "Merchant",
properties: {
name: {
type: "string",
title: "Name",
},
city: {
type: "string",
title: "City",
},
risk_level: {
type: "string",
title: "Risk level",
},
},
},
tags: {
type: "array",
title: "Tags",
items: {
type: "string",
title: "Tag",
},
},
},
},
},
actual: [
{
date: "2026-01-03",
description: "ACH CREDIT PAYROLL",
amount: 4250,
category: "Deposit",
merchant: {
name: "Acme Payroll",
city: "New York",
risk_level: "low",
},
tags: ["payroll", "recurring"],
},
{
date: "2026-01-12",
description: "POS PURCHASE GROCERY MART",
amount: -86.42,
category: "Debit",
merchant: {
name: "Grocery Mart",
city: "Brooklyn",
risk_level: "medium",
},
tags: ["card", "groceries"],
},
],
expected: [
{
date: "2026-01-03",
description: "ACH CREDIT PAYROLL",
amount: 4250,
category: "Deposit",
merchant: {
name: "Acme Payroll",
city: "New York",
risk_level: "low",
},
tags: ["payroll", "recurring"],
},
{
date: "2026-01-12",
description: "POS PURCHASE GROCERY MART",
amount: -68.42,
category: "Debit",
merchant: {
name: "Grocery Mart",
city: "Brooklyn",
risk_level: "low",
},
tags: ["card", "groceries", "needs_review"],
},
],
location: {
page: 1,
area: { left: 13.5, top: 66, width: 73.5, height: 7.5 },
},
},
{
key: "ending_balance",
schema: {
type: "number",
title: "Ending balance",
description: "Final account balance at the end of the statement period.",
},
actual: 12840.18,
expected: 12858.18,
location: {
page: 1,
area: { left: 13.5, top: 66, width: 73.5, height: 7.5 },
},
},
{
key: "overdraft_protection_enabled",
schema: {
type: "boolean",
title: "Overdraft protection enabled",
description: "Whether overdraft protection is enabled for the account.",
},
actual: false,
expected: true,
location: {
page: 2,
area: { left: 9.5, top: 12, width: 81, height: 11.5 },
},
},
{
key: "account_details",
schema: {
type: "object",
title: "Account details",
description: "Account owner and identifying details from the statement.",
properties: {
holder_name: {
type: "string",
title: "Holder name",
},
account_last_four: {
type: "string",
title: "Account last four",
},
account_type: {
type: "string",
title: "Account type",
},
mailing_address: {
type: "object",
title: "Mailing address",
properties: {
line_1: {
type: "string",
title: "Line 1",
},
city: {
type: "string",
title: "City",
},
state: {
type: "string",
title: "State",
},
},
},
linked_accounts: {
type: "array",
title: "Linked accounts",
items: {
type: "object",
properties: {
nickname: {
type: "string",
title: "Nickname",
},
last_four: {
type: "string",
title: "Last four",
},
},
},
},
},
},
actual: {
holder_name: "Jordan Lee",
account_last_four: "4821",
account_type: "Checking",
mailing_address: {
line_1: "42 Market Street",
city: "Brooklyn",
state: "NY",
},
linked_accounts: [
{
nickname: "Operations reserve",
last_four: "1842",
},
],
},
expected: {
holder_name: "Jordan Lee",
account_last_four: "4821",
account_type: "Premier Checking",
mailing_address: {
line_1: "42 Market Street",
city: "New York",
state: "NY",
},
linked_accounts: [
{
nickname: "Operations reserve",
last_four: "1842",
},
{
nickname: "Payroll sweep",
last_four: "9174",
},
],
},
},
]
function getCitationLocation(
citation: ReviewCitation
): ReviewLocation | undefined {
const polygon = citation.polygon
if (!polygon?.length || !citation.pageWidth || !citation.pageHeight) {
return undefined
}
const xs = polygon.map((point) => point.x)
const ys = polygon.map((point) => point.y)
const left = Math.min(...xs)
const top = Math.min(...ys)
const right = Math.max(...xs)
const bottom = Math.max(...ys)
return {
page: citation.page,
area: {
left: (left / citation.pageWidth) * 100,
top: (top / citation.pageHeight) * 100,
width: ((right - left) / citation.pageWidth) * 100,
height: ((bottom - top) / citation.pageHeight) * 100,
},
}
}
export function getMetadataLocation(
metadata: Record<string, ReviewMetadataEntry> | undefined,
metadataPath: string | undefined
) {
if (!metadata || !metadataPath) return undefined
const citation = metadata[metadataPath]?.citations?.find(
(candidate) => candidate.polygon?.length
)
return citation ? getCitationLocation(citation) : undefined
}
export function getReviewFieldLocation(
field: ReviewField | undefined,
resolveLocation?: (metadataPath: string) => ReviewLocation | undefined
) {
if (!field) return undefined
return (
field.location ??
resolveLocation?.(field.metadataPath ?? field.key) ??
undefined
)
}
export function getReviewLocationKey(location: ReviewLocation | undefined) {
if (!location) return null
const { area } = location
return [location.page, area.left, area.top, area.width, area.height].join(":")
}
function valuesFromFields(
fields: ReviewField[],
valueKey: "actual" | "expected"
) {
return fields.reduce<JsonObject>((values, field) => {
values[field.key] = field[valueKey]
return values
}, {})
}
function formatJson(value: unknown) {
return JSON.stringify(value, null, 2)
}
function isJsonObject(value: JsonValue): value is JsonObject {
return typeof value === "object" && value !== null && !Array.isArray(value)
}
function isJsonArray(value: JsonValue): value is JsonArray {
return Array.isArray(value)
}
function getObjectValue(value: JsonValue, key: string): JsonValue {
if (!isJsonObject(value)) return null
return value[key] ?? null
}
function setObjectValue(
value: JsonValue,
key: string,
childValue: JsonValue
): JsonObject {
return {
...(isJsonObject(value) ? value : {}),
[key]: childValue,
}
}
function getArrayValue(value: JsonValue): JsonArray {
return isJsonArray(value) ? value : []
}
function setArrayItemValue(
value: JsonValue,
index: number,
childValue: JsonValue
): JsonArray {
const nextValue = getArrayValue(value).slice()
nextValue[index] = childValue
return nextValue
}
function getPrimitiveValue(value: JsonValue): JsonPrimitive {
return isJsonObject(value) || isJsonArray(value) ? null : value
}
function jsonValuesEqual(left: JsonValue, right: JsonValue) {
return formatJson(left) === formatJson(right)
}
export function findReviewField(
fields: ReviewField[],
fieldKey: string | undefined
): ReviewField | undefined {
if (!fieldKey) return undefined
for (const field of fields) {
if (field.key === fieldKey) return field
if (field.schema.type === "object") {
const childFields = Object.entries(field.schema.properties ?? {}).map(
([key, schema]): ReviewField => ({
key: `${field.key}.${key}`,
schema,
actual: getObjectValue(field.actual, key),
expected: getObjectValue(field.expected, key),
metadataPath: `${field.metadataPath ?? field.key}.${key}`,
})
)
const childField = findReviewField(childFields, fieldKey)
if (childField) return childField
}
}
return undefined
}
function formatValue(value: JsonValue) {
if (value === null) return "NULL"
if (isJsonObject(value) || isJsonArray(value)) return formatJson(value)
if (typeof value === "boolean") return value ? "true" : "false"
return String(value)
}
function areGridRangesEqual(
left: Readonly<Rectangle> | undefined,
right: Readonly<Rectangle> | undefined
) {
return (
left === right ||
(left !== undefined &&
right !== undefined &&
left.x === right.x &&
left.y === right.y &&
left.width === right.width &&
left.height === right.height)
)
}
function areGridRangeStacksEqual(
left: readonly Readonly<Rectangle>[] | undefined,
right: readonly Readonly<Rectangle>[] | undefined
) {
if (left === right) return true
if (!left || !right || left.length !== right.length) return false
return left.every((range, index) => areGridRangesEqual(range, right[index]))
}
function areGridSelectionsEqual(left: GridSelection, right: GridSelection) {
const leftCurrent = left.current
const rightCurrent = right.current
return (
leftCurrent?.cell[0] === rightCurrent?.cell[0] &&
leftCurrent?.cell[1] === rightCurrent?.cell[1] &&
areGridRangesEqual(leftCurrent?.range, rightCurrent?.range) &&
areGridRangeStacksEqual(
leftCurrent?.rangeStack,
rightCurrent?.rangeStack
) &&
left.columns.equals(right.columns) &&
left.rows.equals(right.rows)
)
}
function getGridSelectionRanges(selection: GridSelection | undefined) {
const current = selection?.current
if (!current) return []
return [...(current.rangeStack ?? []), current.range]
}
function areArrayNestedViewsEqual(
left: ArrayNestedView[],
right: ArrayNestedView[]
) {
if (left === right) return true
if (left.length !== right.length) return false
return left.every((view, index) => {
const other = right[index]
return (
view.rowIndex === other?.rowIndex &&
view.columnId === other.columnId &&
view.title === other.title &&
view.schema === other.schema &&
Object.is(view.value, other.value)
)
})
}
type HumanReviewOverlayCell = TextCell | NumberCell
type HumanReviewTextOverlayEditorProps = React.ComponentProps<
ProvideEditorComponent<HumanReviewOverlayCell>
> & {
overlayOpenRef: React.RefObject<boolean>
readOnly?: boolean
}
function HumanReviewTextOverlayEditor({
isHighlighted,
onFinishedEditing,
overlayOpenRef,
readOnly = false,
validatedSelection,
value,
}: HumanReviewTextOverlayEditorProps) {
const initialValue =
value.kind === GridCellKind.Number ? value.displayData : value.data
const [entryValue, setEntryValue] = React.useState(initialValue)
const latestValueRef = React.useRef(initialValue)
const finishedRef = React.useRef(false)
const finishEditing = React.useCallback(
(
shouldSave: boolean,
movement: readonly [-1 | 0 | 1, -1 | 0 | 1] = [0, 0]
) => {
if (finishedRef.current) return
finishedRef.current = true
overlayOpenRef.current = false
if (!shouldSave || readOnly) {
onFinishedEditing(undefined, movement)
return
}
if (value.kind === GridCellKind.Number) {
const numericValue = Number(latestValueRef.current)
onFinishedEditing(
{
...value,
data: Number.isFinite(numericValue) ? numericValue : value.data,
displayData: latestValueRef.current,
},
movement
)
return
}
onFinishedEditing(
{
...value,
data: latestValueRef.current,
displayData: latestValueRef.current,
},
movement
)
},
[onFinishedEditing, overlayOpenRef, readOnly, value]
)
const handleEntryChange = React.useCallback(
(event: React.ChangeEvent<HTMLTextAreaElement>) => {
event.stopPropagation()
latestValueRef.current = event.target.value
setEntryValue(event.target.value)
},
[]
)
const handleKeyDown = React.useCallback(
(event: React.KeyboardEvent<HTMLTextAreaElement>) => {
event.stopPropagation()
if (event.key === "Escape") {
event.preventDefault()
finishEditing(false)
return
}
if (event.key === "Tab") {
event.preventDefault()
finishEditing(true, [event.shiftKey ? -1 : 1, 0])
return
}
if (event.key === "Enter" && !event.shiftKey) {
event.preventDefault()
finishEditing(true, [0, 1])
}
},
[finishEditing]
)
React.useEffect(() => {
overlayOpenRef.current = true
return () => {
overlayOpenRef.current = false
}
}, [overlayOpenRef])
React.useEffect(() => {
const handlePointerOutside = (event: PointerEvent | MouseEvent) => {
const input = document.querySelector<HTMLTextAreaElement>(".gdg-input")
const overlayRoot = input?.closest(".gdg-clip-region")
if (!overlayRoot || overlayRoot.contains(event.target as Node | null)) {
return
}
finishEditing(true)
}
document.addEventListener("pointerdown", handlePointerOutside, true)
document.addEventListener("contextmenu", handlePointerOutside, true)
return () => {
document.removeEventListener("pointerdown", handlePointerOutside, true)
document.removeEventListener("contextmenu", handlePointerOutside, true)
}
}, [finishEditing])
return (
<TextCellEntry
autoFocus={!readOnly}
disabled={readOnly}
highlight={isHighlighted}
value={entryValue}
validatedSelection={validatedSelection}
altNewline
onChange={handleEntryChange}
onKeyDown={handleKeyDown}
/>
)
}
function getFieldIcon(type: SchemaPropertyType) {
if (type === "number" || type === "integer") return InputNumericGlyph
if (type === "boolean") return TextCheckGlyph
if (type === "array") return SecondBracketGlyph
if (type === "object") return SourceCodeSquareGlyph
return InputTextGlyph
}
function HumanReviewValueInput({
readOnly = false,
schema,
value,
onChange,
}: {
readOnly?: boolean
schema: ReviewFieldSchema
value: JsonPrimitive
onChange: (value: JsonPrimitive) => void
}) {
if (schema.enum?.length) {
return (
<span className="relative inline-flex w-full rounded-lg border border-input bg-background text-sm text-foreground shadow-xs/5 dark:bg-input/32">
<select
disabled={readOnly}
value={value === null ? "" : String(value)}
onChange={(event) => onChange(event.target.value)}
className="h-8.5 w-full appearance-none rounded-[inherit] bg-transparent px-3 text-sm outline-none sm:h-7.5"
>
{schema.enum.map((option) => (
<option key={String(option)} value={String(option)}>
{String(option)}
</option>
))}
</select>
</span>
)
}
if (schema.type === "number" || schema.type === "integer") {
return (
<Input
nativeInput
readOnly={readOnly}
type="number"
value={value === null ? "" : String(value)}
onChange={(event) => {
const nextValue = event.currentTarget.value
onChange(nextValue === "" ? null : Number(nextValue))
}}
/>
)
}
if (schema.type === "boolean") {
return (
<div className="grid grid-cols-2 gap-1 rounded-lg bg-muted p-0.5">
{[true, false].map((option) => (
<Button
key={String(option)}
type="button"
size="sm"
variant={value === option ? "outline" : "ghost"}
className={cn(
"h-7 shadow-none",
value === option && "bg-background dark:bg-input"
)}
disabled={readOnly}
onClick={() => onChange(option)}
>
{option ? "True" : "False"}
</Button>
))}
</div>
)
}
return (
<Input
nativeInput
readOnly={readOnly}
value={value === null ? "" : String(value)}
onChange={(event) => onChange(event.currentTarget.value)}
/>
)
}
function getArrayItemSchema(schema: ReviewFieldSchema): ReviewFieldSchema {
return schema.items ?? { type: "string" }
}
function isComplexSchema(schema: ReviewFieldSchema) {
return schema.type === "object" || schema.type === "array"
}
function summarizeComplexValue(value: JsonValue) {
if (isJsonArray(value)) return `${value.length} items`
if (isJsonObject(value)) return `${Object.keys(value).length} fields`
return formatValue(value)
}
function getCellValueForArrayColumn(
rowValue: JsonValue,
itemSchema: ReviewFieldSchema,
columnId: string
) {
if (itemSchema.type === "object") {
return getObjectValue(rowValue, columnId)
}
return rowValue
}
function getCellSchemaForArrayColumn(
itemSchema: ReviewFieldSchema,
columnId: string
) {
if (itemSchema.type === "object") {
return itemSchema.properties?.[columnId] ?? { type: "string" }
}
return itemSchema
}
function applyPrimitiveEdit(
schema: ReviewFieldSchema,
value: EditableGridCell
): JsonValue | undefined {
if (schema.type === "boolean" && value.kind === GridCellKind.Boolean) {
return value.data
}
if (
(schema.type === "number" || schema.type === "integer") &&
value.kind === GridCellKind.Number
) {
return value.data ?? null
}
if (value.kind === GridCellKind.Text) {
if (schema.type === "number" || schema.type === "integer") {
return value.data.trim() === "" ? null : Number(value.data)
}
return value.data
}
return undefined
}
type ArrayNestedView = {
rowIndex: number
columnId: string
title: string
schema: ReviewFieldSchema
value: JsonValue
}
type ArrayReviewSide = "actual" | "expected"
type SyncedArrayNestedView = {
activeSide: ArrayReviewSide | null
stack: ArrayNestedView[]
}
type SyncedArraySelection = {
activeSide: ArrayReviewSide | null
depth: number
gridSelection: GridSelection
}
export type HumanReviewTheme = "light" | "dark"
const EMPTY_SYNCED_ARRAY_NESTED_VIEW: SyncedArrayNestedView = {
activeSide: null,
stack: [],
}
const EMPTY_SYNCED_ARRAY_SELECTION: SyncedArraySelection = {
activeSide: null,
depth: 0,
gridSelection: emptyGridSelection,
}
function setNestedArrayValue({
value,
schema,
nestedStack,
nextNestedValue,
}: {
value: JsonValue
schema: ReviewFieldSchema
nestedStack: ArrayNestedView[]
nextNestedValue: JsonValue
}): JsonValue {
const [currentView, ...remainingViews] = nestedStack
if (!currentView) return nextNestedValue
const itemSchema = getArrayItemSchema(schema)
const rowValue = getArrayValue(value)[currentView.rowIndex] ?? null
const cellSchema = getCellSchemaForArrayColumn(
itemSchema,
currentView.columnId
)
const currentCellValue = getCellValueForArrayColumn(
rowValue,
itemSchema,
currentView.columnId
)
const nextCellValue: JsonValue = remainingViews.length
? setNestedArrayValue({
value: currentCellValue,
schema: cellSchema,
nestedStack: remainingViews,
nextNestedValue,
})
: nextNestedValue
const nextRowValue: JsonValue =
itemSchema.type === "object"
? setObjectValue(rowValue, currentView.columnId, nextCellValue)
: nextCellValue
return setArrayItemValue(value, currentView.rowIndex, nextRowValue)
}
function getNestedArrayValue({
value,
schema,
nestedStack,
}: {
value: JsonValue
schema: ReviewFieldSchema
nestedStack: ArrayNestedView[]
}): JsonValue {
const [currentView, ...remainingViews] = nestedStack
if (!currentView) return value
const itemSchema = getArrayItemSchema(schema)
const rowValue = getArrayValue(value)[currentView.rowIndex] ?? null
const cellSchema = getCellSchemaForArrayColumn(
itemSchema,
currentView.columnId
)
const cellValue = getCellValueForArrayColumn(
rowValue,
itemSchema,
currentView.columnId
)
if (!remainingViews.length) return cellValue
return getNestedArrayValue({
value: cellValue,
schema: cellSchema,
nestedStack: remainingViews,
})
}
function HumanReviewArrayValueGrid({
activeNestedSide = null,
activeSelectionSide = null,
label,
nestedStackBaseDepth = 0,
readOnly = false,
schema,
selectionDepth = 0,
sharedGridSelection,
sharedNestedStack,
value,
viewSide = "expected",
metadataPath,
onChange,
onGridSelectionChange,
onLocationHover,
onNestedStackChange,
resolveArrayItemMetadataPath,
resolveLocation,
}: {
activeNestedSide?: ArrayReviewSide | null
activeSelectionSide?: ArrayReviewSide | null
label: string
nestedStackBaseDepth?: number
readOnly?: boolean
schema: ReviewFieldSchema
selectionDepth?: number
sharedGridSelection?: GridSelection
sharedNestedStack?: ArrayNestedView[]
value: JsonValue
viewSide?: ArrayReviewSide
metadataPath?: string
onChange?: (value: JsonValue) => void
onGridSelectionChange?: (
selection: GridSelection,
side: ArrayReviewSide,
depth: number
) => void
onNestedStackChange?: (
stack: ArrayNestedView[],
side: ArrayReviewSide
) => void
onLocationHover?: (location?: ReviewLocation) => void
resolveArrayItemMetadataPath?: (
metadataPath: string,
rowIndex: number,
rowValue: JsonValue
) => string | undefined
resolveLocation?: (metadataPath: string) => ReviewLocation | undefined
}) {
const rows = getArrayValue(value)
const itemSchema = getArrayItemSchema(schema)
const gridTheme = useHumanReviewGridTheme()
const isDark = useIsDarkTheme()
const [localNestedStack, setLocalNestedStack] = React.useState<
ArrayNestedView[]
>([])
const [localGridSelection, setLocalGridSelection] =
React.useState<GridSelection>(emptyGridSelection)
const fastTextOverlayOpenRef = React.useRef(false)
const nestedStack = sharedNestedStack ?? localNestedStack
const visibleNestedStack = nestedStack.slice(nestedStackBaseDepth)
const activeNestedView = visibleNestedStack[0] ?? null
const activeNestedValue = activeNestedView
? getNestedArrayValue({
value,
schema,
nestedStack: [activeNestedView],
})
: null
const isActiveNestedSource =
Boolean(activeNestedView) && activeNestedSide === viewSide
const isMirroredNestedTarget =
Boolean(activeNestedView) &&
activeNestedSide !== null &&
activeNestedSide !== viewSide
const blueCellText = isDark ? "rgb(147, 197, 253)" : "rgb(37, 99, 235)"
const selectedCellBackground = isDark
? "rgba(37, 99, 235, 0.2)"
: "rgba(219, 234, 254, 0.85)"
const mirroredCellBackground = isDark
? "rgba(167, 139, 250, 0.18)"
: "rgba(237, 233, 254, 0.9)"
const mirroredHighlightRegions = React.useMemo<
React.ComponentProps<typeof DataEditor>["highlightRegions"]
>(() => {
if (
!activeSelectionSide ||
activeSelectionSide === viewSide ||
selectionDepth !== nestedStackBaseDepth
) {
return undefined
}
const ranges = getGridSelectionRanges(sharedGridSelection)
if (!ranges.length) return undefined
return ranges.map((range) => ({
color: mirroredCellBackground,
range,
style: "dashed" as const,
}))
}, [
activeSelectionSide,
mirroredCellBackground,
nestedStackBaseDepth,
selectionDepth,
sharedGridSelection,
viewSide,
])
const setNestedStack = React.useCallback(
(
updater:
| ArrayNestedView[]
| ((current: ArrayNestedView[]) => ArrayNestedView[])
) => {
if (onNestedStackChange) {
const nextStack =
typeof updater === "function" ? updater(nestedStack) : updater
onNestedStackChange(nextStack, viewSide)
return
}
setLocalNestedStack(updater)
},
[nestedStack, onNestedStackChange, viewSide]
)
const handleGridSelectionChange = React.useCallback(
(selection: GridSelection) => {
flushSync(() => {
setLocalGridSelection((current) =>
areGridSelectionsEqual(current, selection) ? current : selection
)
})
if (onGridSelectionChange) {
if (
activeSelectionSide === viewSide &&
sharedGridSelection &&
areGridSelectionsEqual(sharedGridSelection, selection)
) {
return
}
React.startTransition(() => {
onGridSelectionChange(selection, viewSide, nestedStackBaseDepth)
})
return
}
},
[
activeSelectionSide,
nestedStackBaseDepth,
onGridSelectionChange,
sharedGridSelection,
viewSide,
]
)
const columns = React.useMemo<GridColumn[]>(() => {
if (itemSchema.type === "object") {
const propertyEntries = Object.entries(itemSchema.properties ?? {})
if (propertyEntries.length) {
return propertyEntries.map(([key, propertySchema]) => ({
id: key,
title: propertySchema.title ?? key,
width: isComplexSchema(propertySchema) ? 148 : 132,
}))
}
}
return [
{
id: "value",
title: itemSchema.title ?? "Value",
width: isComplexSchema(itemSchema) ? 148 : 180,
},
]
}, [itemSchema])
const getCellContent = React.useCallback(
([col, row]: Item): GridCell => {
const column = columns[col]
const columnId = String(column?.id ?? "value")
const rowValue = rows[row] ?? null
const cellSchema = getCellSchemaForArrayColumn(itemSchema, columnId)
const cellValue = getCellValueForArrayColumn(
rowValue,
itemSchema,
columnId
)
const matchedNestedCell =
activeNestedView?.rowIndex === row &&
activeNestedView.columnId === columnId
const nestedCellTheme: Partial<Theme> | undefined = matchedNestedCell
? {
bgCell: isMirroredNestedTarget
? mirroredCellBackground
: selectedCellBackground,
textDark: blueCellText,
}
: undefined
if (isComplexSchema(cellSchema)) {
return {
kind: GridCellKind.Text,
data: summarizeComplexValue(cellValue),
displayData: summarizeComplexValue(cellValue),
allowOverlay: false,
readonly: true,
cursor: "pointer",
activationBehaviorOverride: "double-click",
themeOverride: {
textDark: blueCellText,
...(nestedCellTheme ?? {}),
},
}
}
if (cellSchema.type === "boolean") {
return {
kind: GridCellKind.Boolean,
data: typeof cellValue === "boolean" ? cellValue : false,
allowOverlay: false,
readonly: readOnly,
}
}
if (cellSchema.type === "number" || cellSchema.type === "integer") {
return {
kind: GridCellKind.Number,
data: typeof cellValue === "number" ? cellValue : undefined,
displayData: typeof cellValue === "number" ? String(cellValue) : "",
allowOverlay: true,
readonly: false,
}
}
return {
kind: GridCellKind.Text,
data:
cellValue === null ||
isJsonObject(cellValue) ||
isJsonArray(cellValue)
? ""
: String(cellValue),
displayData:
cellValue === null ||
isJsonObject(cellValue) ||
isJsonArray(cellValue)
? ""
: String(cellValue),
allowOverlay: true,
readonly: false,
}
},
[
activeNestedView,
blueCellText,
columns,
isMirroredNestedTarget,
itemSchema,
mirroredCellBackground,
readOnly,
rows,
selectedCellBackground,
]
)
const updateCellValue = React.useCallback(
([col, row]: Item, nextCell: EditableGridCell) => {
if (readOnly || !onChange) return
const column = columns[col]
const columnId = String(column?.id ?? "value")
const rowValue = rows[row] ?? null
const cellSchema = getCellSchemaForArrayColumn(itemSchema, columnId)
const nextValue = applyPrimitiveEdit(cellSchema, nextCell)
if (nextValue === undefined) return
const nextRowValue =
itemSchema.type === "object"
? setObjectValue(rowValue, columnId, nextValue)
: nextValue
onChange(setArrayItemValue(value, row, nextRowValue))
},
[columns, itemSchema, onChange, readOnly, rows, value]
)
const provideEditor = React.useCallback<
NonNullable<React.ComponentProps<typeof DataEditor>["provideEditor"]>
>(
(cell) => {
if (
cell.kind !== GridCellKind.Text &&
cell.kind !== GridCellKind.Number
) {
return undefined
}
return {
editor: (props) => {
if (
props.value.kind !== GridCellKind.Text &&
props.value.kind !== GridCellKind.Number
) {
return null
}
return (
<HumanReviewTextOverlayEditor
{...(props as React.ComponentProps<
ProvideEditorComponent<HumanReviewOverlayCell>
>)}
overlayOpenRef={fastTextOverlayOpenRef}
readOnly={readOnly}
/>
)
},
}
},
[readOnly]
)
const handleOutsideClick = React.useCallback(
() => !fastTextOverlayOpenRef.current,
[]
)
const handleItemHovered = React.useCallback(
(args: GridMouseEventArgs) => {
if (!onLocationHover || !resolveLocation || !metadataPath) return
if (args.kind !== "cell") {
onLocationHover(undefined)
return
}
const [col, row] = args.location
const column = columns[col]
const rowValue = rows[row]
if (!column || rowValue === undefined) {
onLocationHover(undefined)
return
}
const columnId = String(column.id ?? "value")
const rowMetadataPath = resolveArrayItemMetadataPath
? resolveArrayItemMetadataPath(metadataPath, row, rowValue)
: `${metadataPath}[${row}]`
if (!rowMetadataPath) {
onLocationHover(undefined)
return
}
const propertyMetadataPath =
itemSchema.type === "object"
? `${rowMetadataPath}.${columnId}`
: rowMetadataPath
onLocationHover(
resolveLocation(propertyMetadataPath) ??
resolveLocation(rowMetadataPath)
)
},
[
columns,
itemSchema.type,
metadataPath,
onLocationHover,
resolveArrayItemMetadataPath,
resolveLocation,
rows,
]
)
const openNestedCell = React.useCallback(
([col, row]: Item) => {
const column = columns[col]
const columnId = String(column?.id ?? "value")
const rowValue = rows[row] ?? null
const cellSchema = getCellSchemaForArrayColumn(itemSchema, columnId)
if (!isComplexSchema(cellSchema)) return
setNestedStack((current) => {
const nextView = {
rowIndex: row,
columnId,
title: `${column?.title ?? columnId} / row ${row + 1}`,
schema: cellSchema,
value: getCellValueForArrayColumn(rowValue, itemSchema, columnId),
}
const currentView = current[nestedStackBaseDepth]
if (
currentView?.rowIndex === nextView.rowIndex &&
currentView.columnId === nextView.columnId
) {
return current
}
return [...current.slice(0, nestedStackBaseDepth), nextView]
})
},
[columns, itemSchema, nestedStackBaseDepth, rows, setNestedStack]
)
const updateNestedValue = React.useCallback(
(nextNestedValue: JsonValue) => {
if (!activeNestedView || readOnly || !onChange) return
onChange(
setNestedArrayValue({
value,
schema,
nestedStack: visibleNestedStack.slice(0, 1),
nextNestedValue,
})
)
setNestedStack((current) =>
current.map((view, index) =>
index === nestedStackBaseDepth
? { ...view, value: nextNestedValue }
: view
)
)
},
[
activeNestedView,
nestedStackBaseDepth,
onChange,
readOnly,
schema,
setNestedStack,
value,
visibleNestedStack,
]
)
return (
<div
onMouseLeave={() => onLocationHover?.(undefined)}
className={cn(
"relative overflow-hidden rounded-md border bg-background transition-[border-color,background-color,box-shadow] focus-within:border-blue-500/50 focus-within:shadow-[0_0_0_1px_rgb(59_130_246_/_8%)] hover:border-blue-500/50",
isActiveNestedSource &&
"border-blue-500/60 bg-blue-500/5 shadow-[0_0_0_1px_rgb(59_130_246_/_10%)]"
)}
>
<div>
<div className="flex h-8 items-center justify-between gap-2 border-b px-2 text-[11px] font-medium text-muted-foreground">
<span>{label}</span>
<span>{rows.length} rows</span>
</div>
<div className="h-[220px]">
<DataEditor
columns={columns}
rows={rows.length}
getCellContent={getCellContent}
cellActivationBehavior="double-click"
gridSelection={localGridSelection}
highlightRegions={mirroredHighlightRegions}
onCellEdited={updateCellValue}
onCellActivated={openNestedCell}
onGridSelectionChange={handleGridSelectionChange}
onItemHovered={handleItemHovered}
provideEditor={provideEditor}
isOutsideClick={handleOutsideClick}
rowMarkers="number"
smoothScrollX
smoothScrollY
theme={gridTheme}
width="100%"
height="100%"
rowHeight={32}
headerHeight={34}
/>
</div>
</div>
{activeNestedView ? (
<div className="absolute inset-0 z-10 flex flex-col bg-background">
<div className="flex h-8 items-center gap-2 border-b px-2">
<Button
type="button"
variant="ghost"
size="icon-sm"
className="size-6 text-muted-foreground"
onClick={() =>
setNestedStack((current) =>
current.slice(
0,
Math.max(nestedStackBaseDepth, current.length - 1)
)
)
}
aria-label="Back to parent array"
>
<IconPlaceholder
lucide="ChevronLeft"
tabler="IconChevronLeft"
hugeicons="ArrowLeft01Icon"
phosphor="CaretLeftIcon"
remixicon="RiArrowLeftSLine"
className="size-3.5"
/>
</Button>
<div className="min-w-0 flex-1 truncate text-xs font-medium text-foreground">
{activeNestedView.title}
</div>
</div>
<div className="min-h-0 flex-1 overflow-auto p-2">
{activeNestedView.schema.type === "array" ? (
<HumanReviewArrayValueGrid
label={
activeNestedView.schema.title ?? activeNestedView.columnId
}
nestedStackBaseDepth={nestedStackBaseDepth + 1}
readOnly={readOnly}
schema={activeNestedView.schema}
sharedGridSelection={sharedGridSelection}
sharedNestedStack={sharedNestedStack}
value={activeNestedValue}
viewSide={viewSide}
activeNestedSide={activeNestedSide}
activeSelectionSide={activeSelectionSide}
selectionDepth={selectionDepth}
onChange={updateNestedValue}
onGridSelectionChange={onGridSelectionChange}
onNestedStackChange={onNestedStackChange}
resolveArrayItemMetadataPath={resolveArrayItemMetadataPath}
resolveLocation={resolveLocation}
/>
) : (
<HumanReviewObjectValueEditor
schema={activeNestedView.schema}
value={activeNestedValue}
originalValue={activeNestedValue}
readOnly={readOnly}
onChange={updateNestedValue}
/>
)}
</div>
</div>
) : null}
</div>
)
}
function HumanReviewObjectValueEditor({
schema,
value,
originalValue,
readOnly = false,
onChange,
}: {
schema: ReviewFieldSchema
value: JsonValue
originalValue: JsonValue
readOnly?: boolean
onChange: (value: JsonValue) => void
}) {
const propertyEntries = Object.entries(schema.properties ?? {})
if (!propertyEntries.length) {
return (
<div className="rounded-md bg-background px-2 py-1.5 text-sm text-muted-foreground">
No properties
</div>
)
}
return (
<div className="space-y-2">
{propertyEntries.map(([propertyKey, propertySchema]) => (
<HumanReviewFieldCard
key={propertyKey}
field={{
key: propertyKey,
schema: propertySchema,
actual: getObjectValue(originalValue, propertyKey),
expected: getObjectValue(originalValue, propertyKey),
}}
value={getObjectValue(value, propertyKey)}
originalValue={getObjectValue(originalValue, propertyKey)}
readOnly={readOnly}
onChange={(childValue) =>
!readOnly &&
onChange(setObjectValue(value, propertyKey, childValue))
}
onUndo={() =>
!readOnly &&
onChange(
setObjectValue(
value,
propertyKey,
getObjectValue(originalValue, propertyKey)
)
)
}
onSetNull={() =>
!readOnly && onChange(setObjectValue(value, propertyKey, null))
}
/>
))}
</div>
)
}
type HumanReviewFieldCardProps = {
field: ReviewField
value: JsonValue
originalValue: JsonValue
active?: boolean
activeFieldKey?: string
readOnly?: boolean
showExpected?: boolean
onChange: (value: JsonValue) => void
onFieldFocus?: (field: ReviewField) => void
onLocationHover?: (location?: ReviewLocation) => void
onUndo: () => void
onSetNull: () => void
resolveArrayItemMetadataPath?: (
metadataPath: string,
rowIndex: number,
rowValue: JsonValue
) => string | undefined
resolveLocation?: (metadataPath: string) => ReviewLocation | undefined
}
function areHumanReviewFieldCardPropsEqual(
previous: HumanReviewFieldCardProps,
next: HumanReviewFieldCardProps
) {
return (
previous.field === next.field &&
Object.is(previous.value, next.value) &&
Object.is(previous.originalValue, next.originalValue) &&
previous.active === next.active &&
previous.activeFieldKey === next.activeFieldKey &&
previous.readOnly === next.readOnly &&
previous.showExpected === next.showExpected &&
previous.onFieldFocus === next.onFieldFocus &&
previous.onLocationHover === next.onLocationHover &&
previous.resolveArrayItemMetadataPath ===
next.resolveArrayItemMetadataPath &&
previous.resolveLocation === next.resolveLocation
)
}
const HumanReviewFieldCard = React.memo(
HumanReviewFieldCardBase,
areHumanReviewFieldCardPropsEqual
)
function HumanReviewFieldCardBase({
field,
value,
originalValue,
active,
activeFieldKey,
readOnly = false,
showExpected = true,
onChange,
onFieldFocus,
onLocationHover,
onUndo,
onSetNull,
resolveArrayItemMetadataPath,
resolveLocation,
}: HumanReviewFieldCardProps) {
const isExpectedEditable = showExpected && !readOnly
const modified = showExpected && !jsonValuesEqual(value, originalValue)
const fieldTypeIcon = React.createElement(getFieldIcon(field.schema.type), {
className: "size-3.5",
})
const propertyEntries = Object.entries(field.schema.properties ?? {})
const [syncedArrayNestedView, setSyncedArrayNestedView] =
React.useState<SyncedArrayNestedView>(EMPTY_SYNCED_ARRAY_NESTED_VIEW)
const [syncedArraySelection, setSyncedArraySelection] =
React.useState<SyncedArraySelection>(EMPTY_SYNCED_ARRAY_SELECTION)
const updateSyncedArrayNestedView = React.useCallback(
(stack: ArrayNestedView[], side: ArrayReviewSide) => {
const activeSide = stack.length ? side : null
setSyncedArrayNestedView((current) =>
current.activeSide === activeSide &&
areArrayNestedViewsEqual(current.stack, stack)
? current
: {
activeSide,
stack,
}
)
},
[]
)
const updateSyncedArraySelection = React.useCallback(
(gridSelection: GridSelection, side: ArrayReviewSide, depth: number) => {
const activeSide = gridSelection.current ? side : null
setSyncedArraySelection((current) =>
current.activeSide === activeSide &&
current.depth === depth &&
areGridSelectionsEqual(current.gridSelection, gridSelection)
? current
: {
activeSide,
depth,
gridSelection,
}
)
},
[]
)
const focusAndHoverField = React.useCallback(() => {
onFieldFocus?.(field)
onLocationHover?.(getReviewFieldLocation(field, resolveLocation))
}, [field, onFieldFocus, onLocationHover, resolveLocation])
return (
<div
tabIndex={0}
onFocusCapture={focusAndHoverField}
onMouseEnter={focusAndHoverField}
onMouseLeave={() => onLocationHover?.(undefined)}
className={cn(
"rounded-lg border bg-background p-3 transition-[border-color,background-color,box-shadow] focus-within:border-blue-500/50 focus-within:bg-blue-500/5 hover:border-blue-500/50 hover:bg-blue-500/5 focus-visible:ring-2 focus-visible:ring-blue-500/20 focus-visible:outline-none",
active &&
"border-blue-500/60 bg-blue-500/5 shadow-[0_0_0_1px_rgb(59_130_246_/_8%)]"
)}
>
<div className="mb-3 flex min-h-8 items-start justify-between gap-3">
<div className="min-w-0">
<div className="flex min-w-0 items-center gap-2">
<div className="min-w-0">
<div className="truncate text-sm font-medium">
{field.schema.title ?? field.key}
</div>
</div>
<span
className={cn(
"size-2 shrink-0 rounded-full bg-amber-400",
!modified && "opacity-0"
)}
/>
</div>
<div className="truncate text-xs text-muted-foreground">
{field.key}
</div>
</div>
<div className="flex shrink-0 items-center gap-1">
{isExpectedEditable && modified ? (
<Tooltip>
<TooltipTrigger asChild>
<Button
type="button"
variant="ghost"
size="icon-sm"
className="text-muted-foreground"
onClick={onUndo}
aria-label={`Undo ${field.key}`}
>
<IconPlaceholder
lucide="Undo2"
tabler="IconArrowBackUp"
hugeicons="Undo02Icon"
phosphor="ArrowUUpLeftIcon"
remixicon="RiArrowGoBackLine"
className="size-4"
/>
</Button>
</TooltipTrigger>
<TooltipContent>Revert changes</TooltipContent>
</Tooltip>
) : null}
{isExpectedEditable ? (
<Tooltip>
<TooltipTrigger asChild>
<Button
type="button"
variant="ghost"
size="icon-sm"
className="text-muted-foreground"
onClick={onSetNull}
aria-label={`Set ${field.key} to null`}
>
<IconPlaceholder
lucide="CircleX"
tabler="IconCircleX"
hugeicons="CancelCircleIcon"
phosphor="XCircleIcon"
remixicon="RiCloseCircleLine"
className="size-4"
/>
</Button>
</TooltipTrigger>
<TooltipContent>Set to NULL</TooltipContent>
</Tooltip>
) : null}
<div className="flex h-6 items-center gap-1 rounded-md border bg-muted/50 px-1.5 text-xs text-muted-foreground">
{fieldTypeIcon}
{field.schema.type}
</div>
</div>
</div>
{field.schema.type === "object" ? (
<div className="rounded-md border bg-muted/25 p-2">
<div className="mb-2 flex items-center justify-between gap-3 text-[11px] font-medium text-muted-foreground">
<span>Properties</span>
<span>{propertyEntries.length} fields</span>
</div>
<div className="space-y-2">
{propertyEntries.length ? (
propertyEntries.map(([propertyKey, schema]) => {
const childField: ReviewField = {
key: `${field.key}.${propertyKey}`,
schema,
actual: getObjectValue(field.actual, propertyKey),
expected: getObjectValue(originalValue, propertyKey),
metadataPath: `${field.metadataPath ?? field.key}.${propertyKey}`,
}
return (
<HumanReviewFieldCard
key={childField.key}
field={childField}
value={getObjectValue(value, propertyKey)}
originalValue={childField.expected}
active={childField.key === activeFieldKey}
activeFieldKey={activeFieldKey}
readOnly={readOnly}
showExpected={showExpected}
onChange={(childValue) =>
onChange(setObjectValue(value, propertyKey, childValue))
}
onFieldFocus={onFieldFocus}
onLocationHover={onLocationHover}
onUndo={() =>
onChange(
setObjectValue(value, propertyKey, childField.expected)
)
}
onSetNull={() =>
onChange(setObjectValue(value, propertyKey, null))
}
resolveArrayItemMetadataPath={resolveArrayItemMetadataPath}
resolveLocation={resolveLocation}
/>
)
})
) : (
<div className="rounded-md bg-background px-2 py-1.5 text-sm text-muted-foreground">
No properties
</div>
)}
</div>
</div>
) : field.schema.type === "array" ? (
<div className="grid gap-2">
<HumanReviewArrayValueGrid
activeNestedSide={syncedArrayNestedView.activeSide}
activeSelectionSide={syncedArraySelection.activeSide}
label="Actual"
metadataPath={field.metadataPath ?? field.key}
readOnly
resolveArrayItemMetadataPath={resolveArrayItemMetadataPath}
resolveLocation={resolveLocation}
schema={field.schema}
selectionDepth={syncedArraySelection.depth}
sharedGridSelection={syncedArraySelection.gridSelection}
sharedNestedStack={syncedArrayNestedView.stack}
value={field.actual}
viewSide="actual"
onGridSelectionChange={updateSyncedArraySelection}
onLocationHover={onLocationHover}
onNestedStackChange={updateSyncedArrayNestedView}
/>
{showExpected ? (
<HumanReviewArrayValueGrid
activeNestedSide={syncedArrayNestedView.activeSide}
activeSelectionSide={syncedArraySelection.activeSide}
label="Expected"
metadataPath={field.metadataPath ?? field.key}
readOnly={readOnly}
resolveArrayItemMetadataPath={resolveArrayItemMetadataPath}
resolveLocation={resolveLocation}
schema={field.schema}
selectionDepth={syncedArraySelection.depth}
sharedGridSelection={syncedArraySelection.gridSelection}
sharedNestedStack={syncedArrayNestedView.stack}
value={value}
viewSide="expected"
onChange={onChange}
onGridSelectionChange={updateSyncedArraySelection}
onLocationHover={onLocationHover}
onNestedStackChange={updateSyncedArrayNestedView}
/>
) : null}
</div>
) : (
<div className={cn("grid gap-2", showExpected && "sm:grid-cols-2")}>
<div className="rounded-md border bg-muted/30 p-2">
{field.schema.description ? (
<p className="mb-2 text-xs text-muted-foreground">
{field.schema.description}
</p>
) : null}
<div className="mb-1 text-[11px] font-medium text-muted-foreground">
Actual
</div>
<div className="min-h-7 rounded-md bg-background px-2 py-1.5 text-sm">
{formatValue(field.actual)}
</div>
</div>
{showExpected ? (
<div className="rounded-md border bg-muted/30 p-2">
<div className="mb-1 text-[11px] font-medium text-muted-foreground">
Expected
</div>
<HumanReviewValueInput
readOnly={readOnly}
schema={field.schema}
value={getPrimitiveValue(value)}
onChange={onChange}
/>
</div>
) : null}
</div>
)}
</div>
)
}
export function HumanReviewHighlight({
location,
}: {
location: ReviewLocation
}) {
const area = location.area
return (
<div
className={cn(
"pointer-events-none absolute z-10 border",
REVIEW_HIGHLIGHT_STYLE
)}
style={{
left: `${area.left}%`,
top: `${area.top}%`,
width: `${area.width}%`,
height: `${area.height}%`,
}}
/>
)
}
export function JsonDiffView({
actual,
expected,
theme = "light",
}: {
actual: JsonObject
expected: JsonObject
theme?: HumanReviewTheme
}) {
const oldFile = React.useMemo(
() => ({
name: "actual.json",
contents: formatJson(actual),
lang: "json",
}),
[actual]
)
const newFile = React.useMemo(
() => ({
name: "expected.json",
contents: formatJson(expected),
lang: "json",
}),
[expected]
)
return (
<ScrollAreaVirtualizer
className="h-full bg-surface/60"
contentClassName="min-w-full"
>
<div className="bounding-box-citations-diff h-full text-xs">
<MultiFileDiff
className="block min-w-full"
oldFile={oldFile}
newFile={newFile}
options={{
diffStyle: "split",
disableFileHeader: true,
diffIndicators: "bars",
hunkSeparators: "line-info-basic",
overflow: "wrap",
themeType: theme,
theme: {
light: "pierre-light-soft",
dark: "pierre-dark-soft",
},
}}
/>
</div>
</ScrollAreaVirtualizer>
)
}
export function JsonCodeView({
name = "actual.json",
theme = "light",
value,
}: {
name?: string
theme?: HumanReviewTheme
value: JsonValue
}) {
const file = React.useMemo(() => {
const contents = formatJson(value)
return {
name,
contents,
lang: "json" as const,
cacheKey: contents,
}
}, [name, value])
return (
<div
data-rehype-pretty-code-figure
className="relative m-0! h-full overflow-hidden rounded-none! bg-code text-code-foreground"
>
<WorkerPoolContextProvider
poolOptions={CODE_WORKER_POOL_OPTIONS}
highlighterOptions={CODE_HIGHLIGHTER_OPTIONS}
>
<ScrollAreaVirtualizer
key={`${file.cacheKey}:${theme}`}
className="h-full min-w-0"
contentClassName="min-w-full"
>
<File
key={`${file.cacheKey}:${theme}`}
className="block min-w-full"
file={file}
metrics={CODE_VIRTUAL_FILE_METRICS}
style={CODE_FILE_THEME}
options={{
disableFileHeader: true,
overflow: "scroll",
themeType: theme,
theme: {
light: "pierre-light-soft",
dark: "pierre-dark-soft",
},
}}
/>
</ScrollAreaVirtualizer>
</WorkerPoolContextProvider>
</div>
)
}
export function HumanReviewPanel({
fields = REVIEW_FIELDS,
activeFieldKey,
className,
onFieldFocus,
onLocationHover,
resolveArrayItemMetadataPath,
resolveLocation,
showExpected = true,
theme = "light",
}: {
fields?: ReviewField[]
activeFieldKey?: string
className?: string
onFieldFocus?: (field: ReviewField) => void
onLocationHover?: (location?: ReviewLocation) => void
resolveArrayItemMetadataPath?: (
metadataPath: string,
rowIndex: number,
rowValue: JsonValue
) => string | undefined
resolveLocation?: (metadataPath: string) => ReviewLocation | undefined
showExpected?: boolean
theme?: HumanReviewTheme
} = {}) {
const [activeTab, setActiveTab] = React.useState("form")
const actualValues = React.useMemo(
() => valuesFromFields(fields, "actual"),
[fields]
)
const initialExpectedValues = React.useMemo(
() => valuesFromFields(fields, "expected"),
[fields]
)
const [expected, setExpected] = React.useState<JsonObject>(
initialExpectedValues
)
const [previousExpectedValues, setPreviousExpectedValues] = React.useState(
initialExpectedValues
)
if (!Object.is(previousExpectedValues, initialExpectedValues)) {
setPreviousExpectedValues(initialExpectedValues)
setExpected(initialExpectedValues)
}
const updateValue = React.useCallback((key: string, value: JsonValue) => {
setExpected((current) =>
Object.is(current[key], value) ? current : { ...current, [key]: value }
)
}, [])
return (
<TooltipProvider delay={200}>
<Tabs
value={activeTab}
onValueChange={setActiveTab}
className={cn("flex h-[560px] flex-col gap-0 bg-background", className)}
>
<div className="flex min-h-12 items-center justify-between gap-3 border-b px-3">
<TabsList className="h-8 sm:h-7">
<TabsTrigger value="form" className="h-7 sm:h-6">
<IconPlaceholder
lucide="CaptionsIcon"
tabler="IconTextCaption"
hugeicons="TextCheckIcon"
phosphor="TextTIcon"
remixicon="RiTextWrap"
className="size-4"
/>
Form
</TabsTrigger>
<TabsTrigger value="json" className="h-7 sm:h-6">
<IconPlaceholder
lucide="SquareCode"
tabler="IconSourceCode"
hugeicons="SourceCodeSquareIcon"
phosphor="CodeIcon"
remixicon="RiCodeBlock"
className="size-4"
/>
JSON
</TabsTrigger>
</TabsList>
</div>
<TabsContent value="form" keepMounted className="min-h-0 flex-1">
<ScrollArea className="h-full" scrollFade>
<div className="space-y-3 p-3">
{fields.map((field) => (
<HumanReviewFieldCard
key={field.key}
field={field}
value={
showExpected ? (expected[field.key] ?? null) : field.actual
}
originalValue={showExpected ? field.expected : field.actual}
active={
field.key === activeFieldKey ||
activeFieldKey?.startsWith(`${field.key}.`)
}
activeFieldKey={activeFieldKey}
showExpected={showExpected}
onChange={(value) => updateValue(field.key, value)}
onFieldFocus={onFieldFocus}
onLocationHover={onLocationHover}
onUndo={() => updateValue(field.key, field.expected)}
onSetNull={() => updateValue(field.key, null)}
resolveArrayItemMetadataPath={resolveArrayItemMetadataPath}
resolveLocation={resolveLocation}
/>
))}
</div>
</ScrollArea>
</TabsContent>
<TabsContent value="json" keepMounted className="min-h-0 flex-1">
{showExpected ? (
<JsonDiffView
actual={actualValues}
expected={expected}
theme={theme}
/>
) : (
<JsonCodeView value={actualValues} theme={theme} />
)}
</TabsContent>
</Tabs>
</TooltipProvider>
)
}
author/apps/v4/lib/registry-icon-props.ts
import type * as React from "react"
export type RegistryIconProps = Omit<
React.ComponentProps<"svg">,
"children" | "strokeWidth"
> & { strokeWidth?: number }
author/apps/v4/lib/utils.ts
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"
import { siteConfig } from "@/lib/config"
export function cn(...inputs: ClassValue[]): string {
return twMerge(clsx(inputs))
}
export function absoluteUrl(path: string): string {
const normalizedPath = path.startsWith("/") ? path : `/${path}`
return `${siteConfig.url}${normalizedPath === "/" ? "" : normalizedPath}`
}
author/apps/v4/components/ui/button.tsx
"use client"
import * as React from "react"
import { mergeProps } from "@base-ui/react/merge-props"
import { useRender } from "@base-ui/react/use-render"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
import { Spinner } from "@/components/ui/spinner"
export const buttonVariants = cva(
"relative inline-flex shrink-0 cursor-pointer items-center justify-center gap-2 rounded-lg border text-base font-medium whitespace-nowrap transition-shadow outline-none before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-lg)-1px)] focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-64 data-loading:text-transparent data-loading:select-none sm:text-sm pointer-coarse:after:absolute pointer-coarse:after:size-full pointer-coarse:after:min-h-11 pointer-coarse:after:min-w-11 [&_svg]:pointer-events-none [&_svg]:-mx-0.5 [&_svg]:shrink-0 [&_svg:not([class*='opacity-'])]:opacity-80 [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4",
{
defaultVariants: {
size: "default",
variant: "default",
},
variants: {
size: {
default: "h-9 px-[calc(--spacing(3)-1px)] sm:h-8",
icon: "size-9 sm:size-8",
"icon-lg": "size-10 sm:size-9",
"icon-sm": "size-8 sm:size-7",
"icon-xl":
"size-11 sm:size-10 [&_svg:not([class*='size-'])]:size-5 sm:[&_svg:not([class*='size-'])]:size-4.5",
"icon-xs":
"size-7 rounded-md before:rounded-[calc(var(--radius-md)-1px)] sm:size-6 not-in-data-[slot=input-group]:[&_svg:not([class*='size-'])]:size-4 sm:not-in-data-[slot=input-group]:[&_svg:not([class*='size-'])]:size-3.5",
lg: "h-10 px-[calc(--spacing(3.5)-1px)] sm:h-9",
sm: "h-8 gap-1.5 px-[calc(--spacing(2.5)-1px)] sm:h-7",
xl: "h-11 px-[calc(--spacing(4)-1px)] text-lg sm:h-10 sm:text-base [&_svg:not([class*='size-'])]:size-5 sm:[&_svg:not([class*='size-'])]:size-4.5",
xs: "h-7 gap-1 rounded-md px-[calc(--spacing(2)-1px)] text-sm before:rounded-[calc(var(--radius-md)-1px)] sm:h-6 sm:text-xs [&_svg:not([class*='size-'])]:size-4 sm:[&_svg:not([class*='size-'])]:size-3.5",
},
variant: {
default:
"border-primary bg-primary text-primary-foreground shadow-xs shadow-primary/24 not-disabled:inset-shadow-[0_1px_--theme(--color-white/16%)] hover:bg-primary/90 data-pressed:bg-primary/90 *:data-[slot=button-loading-indicator]:text-primary-foreground [:active,[data-pressed]]:inset-shadow-[0_1px_--theme(--color-black/8%)] [:disabled,:active,[data-pressed]]:shadow-none",
destructive:
"border-destructive bg-destructive text-white shadow-xs shadow-destructive/24 not-disabled:inset-shadow-[0_1px_--theme(--color-white/16%)] hover:bg-destructive/90 data-pressed:bg-destructive/90 *:data-[slot=button-loading-indicator]:text-white [:active,[data-pressed]]:inset-shadow-[0_1px_--theme(--color-black/8%)] [:disabled,:active,[data-pressed]]:shadow-none",
"destructive-outline":
"border-input bg-popover text-destructive-foreground shadow-xs/5 not-dark:bg-clip-padding not-disabled:not-active:not-data-pressed:before:shadow-[0_1px_--theme(--color-black/4%)] hover:border-destructive/32 hover:bg-destructive/4 data-pressed:border-destructive/32 data-pressed:bg-destructive/4 *:data-[slot=button-loading-indicator]:text-foreground dark:bg-input/32 dark:not-disabled:before:shadow-[0_-1px_--theme(--color-white/2%)] dark:not-disabled:not-active:not-data-pressed:before:shadow-[0_-1px_--theme(--color-white/6%)] [:disabled,:active,[data-pressed]]:shadow-none",
ghost:
"border-transparent text-foreground hover:bg-accent data-pressed:bg-accent *:data-[slot=button-loading-indicator]:text-foreground",
link: "border-transparent text-foreground underline-offset-4 hover:underline data-pressed:underline *:data-[slot=button-loading-indicator]:text-foreground",
outline:
"border-input bg-popover text-foreground shadow-xs/5 not-dark:bg-clip-padding not-disabled:not-active:not-data-pressed:before:shadow-[0_1px_--theme(--color-black/4%)] hover:bg-accent/50 data-pressed:bg-accent/50 *:data-[slot=button-loading-indicator]:text-foreground dark:bg-input/32 dark:not-disabled:before:shadow-[0_-1px_--theme(--color-white/2%)] dark:not-disabled:not-active:not-data-pressed:before:shadow-[0_-1px_--theme(--color-white/6%)] dark:hover:bg-input/64 dark:data-pressed:bg-input/64 [:disabled,:active,[data-pressed]]:shadow-none",
secondary:
"border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/90 data-pressed:bg-secondary/90 *:data-[slot=button-loading-indicator]:text-secondary-foreground [:active,[data-pressed]]:bg-secondary/80",
},
},
}
)
export interface ButtonProps extends useRender.ComponentProps<"button"> {
variant?: VariantProps<typeof buttonVariants>["variant"]
size?: VariantProps<typeof buttonVariants>["size"]
loading?: boolean
}
export function Button({
className,
variant,
size,
render,
children,
loading = false,
disabled: disabledProp,
...props
}: ButtonProps): React.ReactElement {
const isDisabled: boolean = Boolean(loading || disabledProp)
const typeValue: React.ButtonHTMLAttributes<HTMLButtonElement>["type"] =
render ? undefined : "button"
const defaultProps = {
children: (
<>
{children}
{loading && (
<Spinner
className="pointer-events-none absolute"
data-slot="button-loading-indicator"
/>
)}
</>
),
className: cn(buttonVariants({ className, size, variant })),
"aria-disabled": loading || undefined,
"data-loading": loading ? "" : undefined,
"data-slot": "button",
disabled: isDisabled,
type: typeValue,
}
return useRender({
defaultTagName: "button",
props: mergeProps<"button">(defaultProps, props),
render,
})
}
author/apps/v4/components/ui/input.tsx
"use client"
export {
Input,
InputPrimitive,
type InputProps,
} from "@/registry/new-york-v4/ui/input"
author/apps/v4/components/ui/scroll-area.tsx
"use client"
import * as React from "react"
import { ScrollArea as ScrollAreaPrimitive } from "@base-ui/react/scroll-area"
import { cn } from "@/lib/utils"
export function ScrollArea({
className,
children,
orientation = "both",
scrollFade = false,
scrollbarGutter = false,
scrollbarOverflowOnly = false,
viewportClassName,
viewportProps,
viewportRef,
...props
}: ScrollAreaPrimitive.Root.Props & {
orientation?: "vertical" | "horizontal" | "both"
scrollFade?: boolean
scrollbarGutter?: boolean
scrollbarOverflowOnly?: boolean
viewportClassName?: string
viewportProps?: ScrollAreaPrimitive.Viewport.Props
viewportRef?: React.Ref<HTMLDivElement>
}): React.ReactElement {
const {
className: viewportPropsClassName,
key: viewportKey,
ref: viewportPropsRef,
...resolvedViewportProps
} = viewportProps ?? {}
const composedViewportRef = React.useMemo(
() => composeRefs(viewportPropsRef, viewportRef),
[viewportPropsRef, viewportRef]
)
return (
<ScrollAreaPrimitive.Root
className={cn(
"size-full min-h-0",
scrollbarOverflowOnly && "scrollbar-overflow-only",
className
)}
{...props}
>
<ScrollAreaPrimitive.Viewport
key={viewportKey}
{...resolvedViewportProps}
ref={composedViewportRef}
className={cn(
"transition-shadows h-full rounded-[inherit] outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background data-has-overflow-x:overscroll-x-contain data-has-overflow-y:overscroll-y-contain",
scrollFade &&
"mask-t-from-[calc(100%-min(var(--fade-size),var(--scroll-area-overflow-y-start)))] mask-r-from-[calc(100%-min(var(--fade-size),var(--scroll-area-overflow-x-end)))] mask-b-from-[calc(100%-min(var(--fade-size),var(--scroll-area-overflow-y-end)))] mask-l-from-[calc(100%-min(var(--fade-size),var(--scroll-area-overflow-x-start)))] [--fade-size:1.5rem]",
scrollbarGutter && orientation !== "vertical" && "pb-3.5",
scrollbarGutter && orientation !== "horizontal" && "pe-3.5",
viewportPropsClassName,
viewportClassName
)}
data-slot="scroll-area-viewport"
>
{children}
</ScrollAreaPrimitive.Viewport>
{orientation !== "horizontal" ? (
<ScrollBar orientation="vertical" />
) : null}
{orientation !== "vertical" ? (
<ScrollBar orientation="horizontal" />
) : null}
{orientation === "both" ? (
<ScrollAreaPrimitive.Corner data-slot="scroll-area-corner" />
) : null}
</ScrollAreaPrimitive.Root>
)
}
function composeRefs<T>(
...refs: Array<React.Ref<T> | undefined>
): React.RefCallback<T> {
return (node) => {
refs.forEach((ref) => {
if (!ref) return
if (typeof ref === "function") {
ref(node)
return
}
ref.current = node
})
}
}
export function ScrollBar({
className,
orientation = "vertical",
...props
}: ScrollAreaPrimitive.Scrollbar.Props): React.ReactElement {
return (
<ScrollAreaPrimitive.Scrollbar
className={cn(
"m-1 flex opacity-0 transition-opacity delay-300 data-hovering:opacity-100 data-hovering:delay-0 data-hovering:duration-100 data-scrolling:opacity-100 data-scrolling:delay-0 data-scrolling:duration-100 data-[orientation=horizontal]:h-1.5 data-[orientation=horizontal]:flex-col data-[orientation=vertical]:w-1.5",
className
)}
data-slot="scroll-area-scrollbar"
orientation={orientation}
{...props}
>
<ScrollAreaPrimitive.Thumb
className="relative flex-1 rounded-full bg-foreground/20"
data-slot="scroll-area-thumb"
/>
</ScrollAreaPrimitive.Scrollbar>
)
}
export { ScrollAreaPrimitive }
author/apps/v4/components/ui/tabs.tsx
"use client"
export {
Tabs,
TabsContent,
TabsList,
TabsPrimitive,
TabsTrigger,
type TabsVariant,
} from "@/registry/new-york-v4/ui/tabs"
author/apps/v4/components/ui/tooltip.tsx
"use client"
export {
Tooltip,
TooltipContent,
TooltipCreateHandle,
TooltipPopup,
TooltipPrimitive,
TooltipProvider,
TooltipTrigger,
} from "@/registry/new-york-v4/ui/tooltip"
author/apps/v4/components/icon-placeholder.tsx
import type React from "react"
import {
Add01Icon,
Alert01Icon,
AlignBoxBottomCenterIcon,
AlignBoxMiddleCenterIcon,
AlignBoxTopCenterIcon,
ArrowDown01Icon,
ArrowDown02Icon,
ArrowExpandDiagonal01Icon,
ArrowExpandDiagonal02Icon,
ArrowLeft01Icon,
ArrowLeftRightIcon,
ArrowRight01Icon,
ArrowUp01Icon,
ArrowUp02Icon,
ArrowUpDownIcon,
ArrowUpRight01Icon,
Attachment01Icon,
Book01Icon,
BorderAll01Icon,
BorderBottom01Icon,
BorderHorizontalIcon,
BorderInnerIcon,
BorderLeft01Icon,
BorderNone01Icon,
BorderRight01Icon,
BorderTop01Icon,
BorderVerticalIcon,
BrushIcon,
BubbleChatIcon,
Calendar03Icon,
Camera01Icon,
Cancel01Icon,
CancelCircleIcon,
CellsIcon,
CheckIcon,
CheckmarkCircle01Icon,
CheckmarkSquare01Icon,
ChevronDown,
ChevronDownIcon,
ChevronUp,
CircleIcon,
ClipboardIcon,
ColorPickerIcon,
ColumnsThreeCogIcon,
Comment01Icon,
CommentAdd01Icon,
Copy01Icon,
Cursor01Icon,
CursorRectangleSelection01Icon,
CursorTextIcon,
Delete02Icon,
DollarCircleIcon,
Download01Icon,
DragDropVerticalIcon,
DropletOffIcon,
EditOffIcon,
ExternalLinkIcon,
File01Icon,
FileAddIcon,
FileDiffIcon,
FileImageIcon,
FilePenIcon,
FileSpreadsheetIcon,
FileUploadIcon,
FilterIcon,
Flag01Icon,
FloppyDiskIcon,
FullScreenIcon,
GalleryThumbnailsIcon,
GitMergeIcon,
Grid2X2XIcon,
GridViewIcon,
GroupItemsIcon,
HandIcon,
Heading01Icon,
HighlighterIcon,
Image01Icon,
ImageAdd01Icon,
ImageCompositionIcon,
InformationCircleIcon,
InputNumericIcon,
InputTextIcon,
Key01Icon,
KeyboardIcon,
Layers01Icon,
LayoutThreeColumnIcon,
LeftToRightListBulletIcon,
LeftToRightListNumberIcon,
LineIcon,
Link02Icon,
ListTreeIcon,
Loading03Icon,
MinimizeScreenIcon,
MinusSignCircleIcon,
MinusSignIcon,
Moon02Icon,
MoreHorizontalIcon,
MoveIcon,
PaintBoardIcon,
PaintBucketIcon,
ParagraphIcon,
PathfinderMergeIcon,
Pen01Icon,
PencilEdit01Icon,
PercentIcon,
PlusSignCircleIcon,
PolygonIcon,
PrinterIcon,
RadioButtonIcon,
Redo02Icon,
RefreshIcon,
ReplaceIcon,
RotateClockwiseIcon,
Scissor01Icon,
Search01Icon,
SecondBracketIcon,
Shield01Icon,
SidebarLeftIcon,
SidebarRightIcon,
SignatureIcon,
SlidersHorizontalIcon,
SourceCodeSquareIcon,
SparklesIcon,
SplinePointerIcon,
SquareIcon,
SquareLock02Icon,
SquareUnlock02Icon,
StampIcon,
StickyNote01Icon,
Sun03Icon,
Table01Icon,
TableIcon,
TableRowsSplitIcon,
TextAlignCenterIcon,
TextAlignJustifyLeftIcon,
TextAlignLeft01Icon,
TextAlignRight01Icon,
TextBoldIcon,
TextCenterlineCenterTopIcon,
TextCheckIcon,
TextColorIcon,
TextFontIcon,
TextIcon,
TextItalicIcon,
TextNumberSignIcon,
TextStrikethroughIcon,
TextSubscriptIcon,
TextSuperscriptIcon,
TextUnderlineIcon,
TextWrapIcon,
Tick02Icon,
Undo02Icon,
UnfoldMoreIcon,
UngroupItemsIcon,
Upload01Icon,
ViewIcon,
ViewOffIcon,
WaveIcon,
ZoomInAreaIcon,
} from "@hugeicons/core-free-icons"
import { HugeiconsIcon } from "@hugeicons/react"
// The registry source files reference icons through <IconPlaceholder /> with a
// per-library icon name for every icon library the shadcn CLI supports. At
// install time the CLI replaces the placeholder with the consumer's configured
// `iconLibrary` from components.json and strips this import. This component is
// only ever rendered on this docs site, which uses hugeicons.
//
// When a registry component starts using a new icon, add its hugeicons name
// here — the `hugeicons` prop is typed against this registry, so a missing
// entry is a type error.
const hugeicons = {
Add01Icon,
Alert01Icon,
AlignBoxBottomCenterIcon,
AlignBoxMiddleCenterIcon,
AlignBoxTopCenterIcon,
ArrowDown01Icon,
ArrowDown02Icon,
ArrowExpandDiagonal01Icon,
ArrowExpandDiagonal02Icon,
ArrowLeft01Icon,
ArrowLeftRightIcon,
ArrowRight01Icon,
ArrowUp01Icon,
ArrowUp02Icon,
ArrowUpDownIcon,
ArrowUpRight01Icon,
Attachment01Icon,
Book01Icon,
BorderAll01Icon,
BorderBottom01Icon,
BorderHorizontalIcon,
BorderInnerIcon,
BorderLeft01Icon,
BorderNone01Icon,
BorderRight01Icon,
BorderTop01Icon,
BorderVerticalIcon,
BrushIcon,
BubbleChatIcon,
Calendar03Icon,
Camera01Icon,
Cancel01Icon,
CancelCircleIcon,
CellsIcon,
CheckIcon,
CheckmarkCircle01Icon,
CheckmarkSquare01Icon,
ChevronDown,
ChevronDownIcon,
ChevronUp,
CircleIcon,
ClipboardIcon,
ColumnsThreeCogIcon,
Comment01Icon,
CommentAdd01Icon,
ColorPickerIcon,
Copy01Icon,
Cursor01Icon,
CursorRectangleSelection01Icon,
CursorTextIcon,
Delete02Icon,
DollarCircleIcon,
Download01Icon,
DragDropVerticalIcon,
DropletOffIcon,
EditOffIcon,
ExternalLinkIcon,
File01Icon,
FileAddIcon,
FileDiffIcon,
FileImageIcon,
FilePenIcon,
FileSpreadsheetIcon,
FileUploadIcon,
FilterIcon,
Flag01Icon,
FloppyDiskIcon,
FullScreenIcon,
GalleryThumbnailsIcon,
GitMergeIcon,
Grid2X2XIcon,
GridViewIcon,
GroupItemsIcon,
HandIcon,
Heading01Icon,
HighlighterIcon,
Image01Icon,
ImageAdd01Icon,
ImageCompositionIcon,
InformationCircleIcon,
InputNumericIcon,
InputTextIcon,
Key01Icon,
KeyboardIcon,
Layers01Icon,
LayoutThreeColumnIcon,
LeftToRightListBulletIcon,
LeftToRightListNumberIcon,
LineIcon,
Link02Icon,
ListTreeIcon,
Loading03Icon,
MinimizeScreenIcon,
MinusSignCircleIcon,
MinusSignIcon,
Moon02Icon,
MoreHorizontalIcon,
MoveIcon,
PaintBoardIcon,
PaintBucketIcon,
ParagraphIcon,
PathfinderMergeIcon,
Pen01Icon,
PencilEdit01Icon,
PercentIcon,
PlusSignCircleIcon,
PolygonIcon,
PrinterIcon,
RadioButtonIcon,
Redo02Icon,
RefreshIcon,
ReplaceIcon,
RotateClockwiseIcon,
Scissor01Icon,
Search01Icon,
SecondBracketIcon,
Shield01Icon,
SidebarLeftIcon,
SidebarRightIcon,
SignatureIcon,
SlidersHorizontalIcon,
SourceCodeSquareIcon,
SparklesIcon,
SplinePointerIcon,
SquareIcon,
SquareLock02Icon,
SquareUnlock02Icon,
StampIcon,
StickyNote01Icon,
Sun03Icon,
Table01Icon,
TableIcon,
TableRowsSplitIcon,
TextAlignCenterIcon,
TextAlignJustifyLeftIcon,
TextAlignLeft01Icon,
TextAlignRight01Icon,
TextBoldIcon,
TextCenterlineCenterTopIcon,
TextCheckIcon,
TextColorIcon,
TextFontIcon,
TextIcon,
TextItalicIcon,
TextNumberSignIcon,
TextStrikethroughIcon,
TextSubscriptIcon,
TextSuperscriptIcon,
TextUnderlineIcon,
TextWrapIcon,
Tick02Icon,
Undo02Icon,
UnfoldMoreIcon,
UngroupItemsIcon,
Upload01Icon,
ViewIcon,
ViewOffIcon,
WaveIcon,
ZoomInAreaIcon,
}
export type IconPlaceholderProps = {
lucide: string
tabler: string
hugeicons: keyof typeof hugeicons
phosphor: string
remixicon: string
} & React.ComponentProps<"svg">
export function IconPlaceholder(props: IconPlaceholderProps) {
const {
lucide: _lucide,
tabler: _tabler,
hugeicons: hugeiconsName,
phosphor: _phosphor,
remixicon: _remixicon,
...rest
} = props
return (
<HugeiconsIcon
icon={hugeicons[hugeiconsName]}
{...(rest as Omit<React.ComponentProps<typeof HugeiconsIcon>, "icon">)}
/>
)
}
author/apps/v4/lib/config.ts
const canonicalUrl = "https://www.extend.ai/ui"
export const siteConfig = {
name: "Extend UI",
url: canonicalUrl,
ogImage: `${canonicalUrl}/opengraph-image.png`,
description:
"Open source UI primitives for building document processing products with viewers, review surfaces, and validation workflows.",
links: {
github: "https://github.com/extend-hq/ui",
},
navItems: [
{
href: "/docs",
label: "Docs",
},
{
href: "/docs/components",
label: "Components",
},
{
href: "/blocks",
label: "Blocks",
},
],
}
export const META_THEME_COLORS = {
light: "#ffffff",
dark: "#09090b",
}
author/apps/v4/components/ui/spinner.tsx
"use client"
export { Spinner } from "@/registry/new-york-v4/ui/spinner"
author/apps/v4/registry/new-york-v4/ui/input.tsx
"use client"
import type * as React from "react"
import { Input as InputPrimitive } from "@base-ui/react/input"
import { cn } from "@/lib/utils"
export type InputProps = Omit<
InputPrimitive.Props & React.RefAttributes<HTMLInputElement>,
"size"
> & {
size?: "sm" | "default" | "lg" | number
unstyled?: boolean
nativeInput?: boolean
}
export function Input({
className,
size = "default",
unstyled = false,
nativeInput = false,
style,
...props
}: InputProps): React.ReactElement {
const inputClassName = cn(
"h-8.5 w-full min-w-0 rounded-[inherit] px-[calc(--spacing(3)-1px)] leading-8.5 outline-none [transition:background-color_5000000s_ease-in-out_0s] placeholder:text-muted-foreground/72 sm:h-7.5 sm:leading-7.5",
size === "sm" &&
"h-7.5 px-[calc(--spacing(2.5)-1px)] leading-7.5 sm:h-6.5 sm:leading-6.5",
size === "lg" && "h-9.5 leading-9.5 sm:h-8.5 sm:leading-8.5",
props.type === "search" &&
"[&::-webkit-search-cancel-button]:appearance-none [&::-webkit-search-decoration]:appearance-none [&::-webkit-search-results-button]:appearance-none [&::-webkit-search-results-decoration]:appearance-none",
props.type === "file" &&
"text-muted-foreground file:me-3 file:bg-transparent file:text-sm file:font-medium file:text-foreground"
)
return (
<span
className={
cn(
!unstyled &&
"relative inline-flex w-full rounded-lg border border-input bg-popover text-base text-foreground shadow-xs/5 ring-ring/24 transition-shadow not-dark:bg-clip-padding before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-lg)-1px)] not-has-disabled:not-has-focus-visible:not-has-aria-invalid:before:shadow-[0_1px_--theme(--color-black/4%)] has-autofill:bg-foreground/4 has-focus-visible:border-ring has-focus-visible:ring-[3px] has-disabled:opacity-64 has-aria-invalid:border-destructive/36 has-focus-visible:has-aria-invalid:border-destructive/64 has-focus-visible:has-aria-invalid:ring-destructive/16 has-[:disabled,:focus-visible,[aria-invalid]]:shadow-none sm:text-sm dark:bg-input/32 dark:not-has-disabled:not-has-focus-visible:not-has-aria-invalid:before:shadow-[0_-1px_--theme(--color-white/6%)] dark:has-autofill:bg-foreground/8 dark:has-aria-invalid:ring-destructive/24",
className
) || undefined
}
data-size={size}
data-slot="input-control"
>
{nativeInput ? (
<input
className={inputClassName}
data-slot="input"
size={typeof size === "number" ? size : undefined}
style={typeof style === "function" ? undefined : style}
{...props}
/>
) : (
<InputPrimitive
className={inputClassName}
data-slot="input"
size={typeof size === "number" ? size : undefined}
style={style}
{...props}
/>
)}
</span>
)
}
export { InputPrimitive }
author/apps/v4/registry/new-york-v4/ui/tabs.tsx
"use client"
import type React from "react"
import { Tabs as TabsPrimitive } from "@base-ui/react/tabs"
import { cn } from "@/lib/utils"
export type TabsVariant = "default" | "underline"
export function Tabs({
className,
...props
}: TabsPrimitive.Root.Props): React.ReactElement {
return (
<TabsPrimitive.Root
className={cn(
"flex flex-col gap-2 data-[orientation=vertical]:flex-row",
className
)}
data-slot="tabs"
{...props}
/>
)
}
export function TabsList({
variant = "default",
className,
children,
...props
}: TabsPrimitive.List.Props & {
variant?: TabsVariant
}): React.ReactElement {
return (
<TabsPrimitive.List
className={cn(
"relative z-0 flex w-fit items-center justify-center gap-x-0.5 text-muted-foreground",
"data-[orientation=vertical]:flex-col",
variant === "default"
? "rounded-lg bg-muted p-0.5 text-muted-foreground/72"
: "data-[orientation=horizontal]:py-1 data-[orientation=vertical]:px-1 *:data-[slot=tabs-tab]:hover:bg-accent",
className
)}
data-slot="tabs-list"
{...props}
>
{children}
<TabsPrimitive.Indicator
className={cn(
"absolute bottom-0 left-0 h-(--active-tab-height) w-(--active-tab-width) translate-x-(--active-tab-left) -translate-y-(--active-tab-bottom) transition-[width,translate] duration-200 ease-in-out",
variant === "underline"
? "z-10 bg-primary data-[orientation=horizontal]:h-0.5 data-[orientation=horizontal]:translate-y-px data-[orientation=vertical]:w-0.5 data-[orientation=vertical]:-translate-x-px"
: "-z-1 rounded-md bg-background shadow-sm/5 dark:bg-input"
)}
data-slot="tab-indicator"
/>
</TabsPrimitive.List>
)
}
export function TabsTab({
className,
...props
}: TabsPrimitive.Tab.Props): React.ReactElement {
return (
<TabsPrimitive.Tab
className={cn(
"relative flex h-9 shrink-0 grow cursor-pointer items-center justify-center gap-1.5 rounded-md border border-transparent px-[calc(--spacing(2.5)-1px)] text-base font-medium whitespace-nowrap transition-[color,background-color,box-shadow] outline-none hover:text-muted-foreground focus-visible:ring-2 focus-visible:ring-ring data-[orientation=vertical]:w-full data-[orientation=vertical]:justify-start sm:h-8 sm:text-sm data-disabled:pointer-events-none data-disabled:opacity-64 data-active:text-foreground data-active:hover:text-foreground [&_svg]:pointer-events-none [&_svg]:-mx-0.5 [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4",
className
)}
data-slot="tabs-tab"
{...props}
/>
)
}
export function TabsPanel({
className,
...props
}: TabsPrimitive.Panel.Props): React.ReactElement {
return (
<TabsPrimitive.Panel
className={cn("flex-1 outline-none", className)}
data-slot="tabs-content"
{...props}
/>
)
}
export { TabsPrimitive, TabsTab as TabsTrigger, TabsPanel as TabsContent }
author/apps/v4/registry/new-york-v4/ui/tooltip.tsx
"use client"
import type React from "react"
import { isValidElement } from "react"
import { Tooltip as TooltipPrimitive } from "@base-ui/react/tooltip"
import { cn } from "@/lib/utils"
export const TooltipCreateHandle: typeof TooltipPrimitive.createHandle =
TooltipPrimitive.createHandle
export function TooltipProvider({
delayDuration,
delay = delayDuration,
...props
}: TooltipPrimitive.Provider.Props & {
delayDuration?: TooltipPrimitive.Provider.Props["delay"]
}): React.ReactElement {
return <TooltipPrimitive.Provider delay={delay} {...props} />
}
export const Tooltip: typeof TooltipPrimitive.Root = TooltipPrimitive.Root
export function TooltipTrigger({
asChild,
children,
render,
...props
}: TooltipPrimitive.Trigger.Props & {
asChild?: boolean
}): React.ReactElement {
return (
<TooltipPrimitive.Trigger
data-slot="tooltip-trigger"
render={
render ??
(asChild && isValidElement(children)
? (children as React.ReactElement<Record<string, unknown>>)
: undefined)
}
{...props}
>
{asChild && isValidElement(children) ? undefined : children}
</TooltipPrimitive.Trigger>
)
}
export function TooltipPopup({
className,
align = "center",
sideOffset = 4,
side = "top",
anchor,
children,
portalProps,
...props
}: TooltipPrimitive.Popup.Props & {
align?: TooltipPrimitive.Positioner.Props["align"]
side?: TooltipPrimitive.Positioner.Props["side"]
sideOffset?: TooltipPrimitive.Positioner.Props["sideOffset"]
anchor?: TooltipPrimitive.Positioner.Props["anchor"]
portalProps?: TooltipPrimitive.Portal.Props
}): React.ReactElement {
return (
<TooltipPrimitive.Portal {...portalProps}>
<TooltipPrimitive.Positioner
align={align}
anchor={anchor}
className="z-50 h-(--positioner-height) w-(--positioner-width) max-w-(--available-width) transition-[top,left,right,bottom,transform] data-instant:transition-none"
data-slot="tooltip-positioner"
side={side}
sideOffset={sideOffset}
>
<TooltipPrimitive.Popup
className={cn(
"relative flex h-(--popup-height,auto) w-(--popup-width,auto) origin-(--transform-origin) rounded-md border bg-popover text-xs text-balance text-popover-foreground shadow-md/5 transition-[width,height,scale,opacity] not-dark:bg-clip-padding before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-md)-1px)] before:shadow-[0_1px_--theme(--color-black/4%)] data-ending-style:scale-98 data-ending-style:opacity-0 data-instant:duration-0 data-starting-style:scale-98 data-starting-style:opacity-0 dark:before:shadow-[0_-1px_--theme(--color-white/6%)]",
className
)}
data-slot="tooltip-popup"
{...props}
>
<TooltipPrimitive.Viewport
className="relative size-full overflow-clip px-(--viewport-inline-padding) py-1 [--viewport-inline-padding:--spacing(2)] **:data-current:w-[calc(var(--popup-width)-2*var(--viewport-inline-padding)-2px)] **:data-current:opacity-100 **:data-current:transition-opacity **:data-current:data-ending-style:opacity-0 data-instant:transition-none **:data-previous:w-[calc(var(--popup-width)-2*var(--viewport-inline-padding)-2px)] **:data-previous:truncate **:data-previous:opacity-100 **:data-previous:transition-opacity **:data-previous:data-ending-style:opacity-0 **:data-current:data-starting-style:opacity-0 **:data-previous:data-starting-style:opacity-0"
data-slot="tooltip-viewport"
>
{children}
</TooltipPrimitive.Viewport>
</TooltipPrimitive.Popup>
</TooltipPrimitive.Positioner>
</TooltipPrimitive.Portal>
)
}
export { TooltipPrimitive, TooltipPopup as TooltipContent }
author/apps/v4/registry/new-york-v4/ui/spinner.tsx
import type React from "react"
import { cn } from "@/lib/utils"
import { IconPlaceholder } from "@/components/icon-placeholder"
export function Spinner({
className,
...props
}: React.ComponentProps<"svg">): React.ReactElement {
return (
<IconPlaceholder
lucide="Loader2"
tabler="IconLoader"
hugeicons="Loading03Icon"
phosphor="SpinnerIcon"
remixicon="RiLoaderLine"
aria-label="Loading"
className={cn("animate-spin", className)}
role="status"
{...props}
/>
)
}
author/apps/v4/app/globals.css
@import "tailwindcss";
@import "tw-animate-css";
@import "./shadcn-tailwind.css";
@import "./legacy-themes.css";
@import "../registry/styles/style-vega.css" layer(base);
@import "../registry/styles/style-nova.css" layer(base);
@import "../registry/styles/style-lyra.css" layer(base);
@import "../registry/styles/style-maia.css" layer(base);
@import "../registry/styles/style-mira.css" layer(base);
@import "../registry/styles/style-luma.css" layer(base);
@import "../registry/styles/style-sera.css" layer(base);
@custom-variant style-vega (&:where(.style-vega *));
@custom-variant style-nova (&:where(.style-nova *));
@custom-variant style-lyra (&:where(.style-lyra *));
@custom-variant style-maia (&:where(.style-maia *));
@custom-variant style-mira (&:where(.style-mira *));
@custom-variant style-luma (&:where(.style-luma *));
@custom-variant style-sera (&:where(.style-sera *));
@custom-variant dark (&:is(.dark *));
@custom-variant fixed (&:is(.layout-fixed *));
@theme inline {
--breakpoint-3xl: 1600px;
--breakpoint-4xl: 2000px;
--font-sans: var(--font-sans);
--font-heading: var(--font-heading);
--font-mono: var(--font-mono);
--radius-sm: calc(var(--radius) * 0.6);
--radius-md: calc(var(--radius) * 0.8);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) * 1.4);
--radius-2xl: calc(var(--radius) * 1.8);
--radius-3xl: calc(var(--radius) * 2.2);
--radius-4xl: calc(var(--radius) * 2.6);
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-card: var(--card);
--color-card-foreground: var(--card-foreground);
--color-popover: var(--popover);
--color-popover-foreground: var(--popover-foreground);
--color-primary: var(--primary);
--color-primary-foreground: var(--primary-foreground);
--color-secondary: var(--secondary);
--color-secondary-foreground: var(--secondary-foreground);
--color-muted: var(--muted);
--color-muted-foreground: var(--muted-foreground);
--color-accent: var(--accent);
--color-accent-foreground: var(--accent-foreground);
--color-destructive: var(--destructive);
--color-destructive-foreground: var(--destructive-foreground);
--color-border: var(--border);
--color-input: var(--input);
--color-ring: var(--ring);
--color-chart-1: var(--chart-1);
--color-chart-2: var(--chart-2);
--color-chart-3: var(--chart-3);
--color-chart-4: var(--chart-4);
--color-chart-5: var(--chart-5);
--color-sidebar: var(--sidebar);
--color-sidebar-foreground: var(--sidebar-foreground);
--color-sidebar-primary: var(--sidebar-primary);
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
--color-sidebar-accent: var(--sidebar-accent);
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
--color-sidebar-border: var(--sidebar-border);
--color-sidebar-ring: var(--sidebar-ring);
--color-surface: var(--surface);
--color-surface-foreground: var(--surface-foreground);
--color-code: var(--code);
--color-code-foreground: var(--code-foreground);
--color-code-highlight: var(--code-highlight);
--color-code-number: var(--code-number);
--color-selection: var(--selection);
--color-selection-foreground: var(--selection-foreground);
--color-warning-foreground: var(--warning-foreground);
--color-warning: var(--warning);
--color-success-foreground: var(--success-foreground);
--color-success: var(--success);
--color-info-foreground: var(--info-foreground);
--color-info: var(--info);
--animate-skeleton: skeleton 2s -1s infinite linear;
@keyframes skeleton {
to {
background-position: -200% 0;
}
}
}
:root {
--radius: 0.625rem;
--background: oklch(1 0 0);
--foreground: var(--color-neutral-800);
--card: var(--color-white);
--card-foreground: var(--color-neutral-800);
--popover: var(--color-white);
--popover-foreground: var(--color-neutral-800);
--primary: var(--color-neutral-800);
--primary-foreground: var(--color-neutral-50);
--secondary: --alpha(var(--color-black) / 4%);
--secondary-foreground: var(--color-neutral-800);
--muted: --alpha(var(--color-black) / 4%);
--muted-foreground: color-mix(
in srgb,
var(--color-neutral-500) 90%,
var(--color-black)
);
--accent: --alpha(var(--color-black) / 4%);
--accent-foreground: var(--color-neutral-800);
--destructive: var(--color-red-500);
--destructive-foreground: var(--color-red-700);
--border: --alpha(var(--color-black) / 8%);
--input: --alpha(var(--color-black) / 10%);
--ring: var(--color-neutral-400);
--chart-1: var(--color-blue-300);
--chart-2: var(--color-blue-500);
--chart-3: var(--color-blue-600);
--chart-4: var(--color-blue-700);
--chart-5: var(--color-blue-800);
--sidebar: var(--color-neutral-50);
--sidebar-foreground: color-mix(
in srgb,
var(--color-neutral-800) 64%,
var(--sidebar)
);
--sidebar-primary: var(--color-neutral-800);
--sidebar-primary-foreground: var(--color-neutral-50);
--sidebar-accent: --alpha(var(--color-black) / 4%);
--sidebar-accent-foreground: var(--color-neutral-800);
--sidebar-border: --alpha(var(--color-black) / 6%);
--sidebar-ring: var(--color-neutral-400);
--surface: oklch(0.98 0 0);
--surface-foreground: var(--foreground);
--code: var(--surface);
--code-foreground: var(--surface-foreground);
--code-highlight: oklch(0.96 0 0);
--code-number: oklch(0.56 0 0);
--selection: oklch(0.145 0 0);
--selection-foreground: oklch(1 0 0);
--info: var(--color-blue-500);
--info-foreground: var(--color-blue-700);
--success: var(--color-emerald-500);
--success-foreground: var(--color-emerald-700);
--warning: var(--color-amber-500);
--warning-foreground: var(--color-amber-700);
}
.dark {
--background: oklch(0.145 0 0);
--foreground: var(--color-neutral-100);
--card: color-mix(in srgb, var(--background) 98%, var(--color-white));
--card-foreground: var(--color-neutral-100);
--popover: color-mix(in srgb, var(--background) 98%, var(--color-white));
--popover-foreground: var(--color-neutral-100);
--primary: var(--color-neutral-100);
--primary-foreground: var(--color-neutral-800);
--secondary: --alpha(var(--color-white) / 4%);
--secondary-foreground: var(--color-neutral-100);
--muted: --alpha(var(--color-white) / 4%);
--muted-foreground: color-mix(
in srgb,
var(--color-neutral-500) 90%,
var(--color-white)
);
--accent: --alpha(var(--color-white) / 4%);
--accent-foreground: var(--color-neutral-100);
--destructive: color-mix(
in srgb,
var(--color-red-500) 90%,
var(--color-white)
);
--destructive-foreground: var(--color-red-400);
--border: --alpha(var(--color-white) / 6%);
--input: --alpha(var(--color-white) / 8%);
--ring: var(--color-neutral-500);
--chart-1: var(--color-blue-300);
--chart-2: var(--color-blue-500);
--chart-3: var(--color-blue-600);
--chart-4: var(--color-blue-700);
--chart-5: var(--color-blue-800);
--sidebar: color-mix(
in srgb,
var(--color-neutral-950) 97%,
var(--color-white)
);
--sidebar-foreground: color-mix(
in srgb,
var(--color-neutral-100) 64%,
var(--sidebar)
);
--sidebar-primary: var(--color-neutral-100);
--sidebar-primary-foreground: var(--color-neutral-800);
--sidebar-accent: --alpha(var(--color-white) / 4%);
--sidebar-accent-foreground: var(--color-neutral-100);
--sidebar-border: --alpha(var(--color-white) / 5%);
--sidebar-ring: var(--color-neutral-400);
--surface: oklch(0.2 0 0);
--surface-foreground: oklch(0.708 0 0);
--code: var(--surface);
--code-foreground: var(--surface-foreground);
--code-highlight: oklch(0.27 0 0);
--code-number: oklch(0.72 0 0);
--selection: oklch(0.922 0 0);
--selection-foreground: oklch(0.205 0 0);
--info: var(--color-blue-500);
--info-foreground: var(--color-blue-400);
--success: var(--color-emerald-500);
--success-foreground: var(--color-emerald-400);
--warning: var(--color-amber-500);
--warning-foreground: var(--color-amber-400);
}
@layer base {
* {
@apply border-border outline-ring/50;
}
::selection {
@apply bg-selection text-selection-foreground;
}
html {
@apply overscroll-y-none;
}
body {
font-synthesis-weight: none;
text-rendering: optimizeLegibility;
}
.cn-font-heading {
@apply font-heading;
}
[data-slot="layout"] {
@apply overscroll-none;
}
@supports (font: -apple-system-body) and (-webkit-appearance: none) {
[data-wrapper] {
@apply min-[1800px]:border-t;
}
}
a:active,
button:active {
@apply opacity-60 md:opacity-100;
}
[data-lang="ar"] {
font-family: var(--font-ar);
}
[data-lang="he"] {
font-family: var(--font-he);
}
}
@utility border-grid {
@apply border-border/50 dark:border-border;
}
@utility section-soft {
@apply bg-linear-to-b from-background to-surface/40 dark:bg-background 3xl:fixed:bg-none;
}
@utility theme-container {
@apply font-sans;
}
@utility container-wrapper {
@apply mx-auto w-full px-2 3xl:fixed:max-w-[calc(var(--breakpoint-2xl)+2rem)];
}
@utility container {
@apply mx-auto max-w-[1400px] px-4 3xl:max-w-screen-2xl lg:px-8;
}
@utility no-scrollbar {
-ms-overflow-style: none;
scrollbar-width: none;
&::-webkit-scrollbar {
display: none;
}
}
@utility scrollbar-overflow-only {
&:not(:has([data-slot="scroll-area-viewport"][data-has-overflow-x]))
[data-slot="scroll-area-scrollbar"][data-orientation="horizontal"] {
display: none;
}
&:not(:has([data-slot="scroll-area-viewport"][data-has-overflow-y]))
[data-slot="scroll-area-scrollbar"][data-orientation="vertical"] {
display: none;
}
}
@utility border-ghost {
@apply relative after:absolute after:inset-0 after:border after:border-border after:mix-blend-darken dark:after:mix-blend-lighten;
}
@utility step {
counter-increment: step;
&:before {
@apply mr-2 inline-flex size-6 items-center justify-center rounded-full border-background bg-background text-center -indent-px font-mono text-sm font-medium md:absolute md:mt-[-4px] md:ml-[-50px] md:size-9 md:border-4;
box-shadow: inset 0 0 0 9999px var(--muted);
content: counter(step);
}
}
@utility extend-touch-target {
@media (pointer: coarse) {
@apply relative touch-manipulation after:absolute after:-inset-2;
}
}
@layer components {
.steps {
&:first-child {
@apply !mt-0;
}
&:first-child > h3:first-child {
@apply !mt-0;
}
> h3 {
@apply !mt-8;
}
> h3 + p {
@apply !mt-2;
}
}
.docs-view-code-button {
background-color: var(--background) !important;
color: var(--foreground);
box-shadow: 0 1px 2px rgb(0 0 0 / 6%) !important;
transition-property: border-color, box-shadow, color;
}
.docs-view-code-button:hover,
.docs-view-code-button[data-pressed] {
background-color: var(--background) !important;
border-color: color-mix(in oklab, var(--ring) 55%, transparent) !important;
box-shadow:
0 4px 14px rgb(0 0 0 / 10%),
0 0 0 3px color-mix(in oklab, var(--ring) 16%, transparent) !important;
}
.dark .docs-view-code-button {
background-color: var(--background) !important;
box-shadow: 0 1px 2px rgb(0 0 0 / 30%) !important;
}
.dark .docs-view-code-button:hover,
.dark .docs-view-code-button[data-pressed] {
background-color: var(--background) !important;
box-shadow:
0 4px 14px rgb(0 0 0 / 36%),
0 0 0 3px color-mix(in oklab, var(--ring) 20%, transparent) !important;
}
[data-rehype-pretty-code-figure] {
background-color: var(--color-code);
color: var(--color-code-foreground);
border-radius: var(--radius-xl);
border-width: 0px;
border-color: var(--border);
margin-top: calc(var(--spacing) * 6);
overflow: hidden;
font-size: var(--text-sm);
outline: none;
position: relative;
@apply -mx-1 md:-mx-1;
&:has([data-rehype-pretty-code-title]) [data-slot="copy-button"] {
top: calc(var(--spacing) * 1.5) !important;
}
}
[data-rehype-pretty-code-title] {
border-bottom: color-mix(in oklab, var(--border) 30%, transparent);
border-bottom-width: 1px;
border-bottom-style: solid;
padding-block: calc(var(--spacing) * 2.5);
padding-inline: calc(var(--spacing) * 4);
font-size: var(--text-sm);
font-family: var(--font-mono);
color: var(--color-code-foreground);
}
[data-line-numbers] {
display: grid;
min-width: 100%;
white-space: pre;
border: 0;
background: transparent;
padding: 0;
counter-reset: line;
box-decoration-break: clone;
}
[data-line-numbers] [data-line]::before {
font-size: var(--text-sm);
counter-increment: line;
content: counter(line);
display: inline-block;
width: calc(var(--spacing) * 16);
padding-right: calc(var(--spacing) * 6);
text-align: right;
color: var(--color-code-number);
background-color: var(--color-code);
position: sticky;
left: 0;
}
[data-line-numbers] [data-highlighted-line][data-line]::before {
background-color: var(--color-code-highlight);
}
[data-line] {
padding-top: calc(var(--spacing) * 0.5);
padding-bottom: calc(var(--spacing) * 0.5);
min-height: calc(var(--spacing) * 1);
width: 100%;
display: inline-block;
}
/*
* ```text composition trees use box-drawing characters; per-line padding makes
* vertical connectors look broken. rehype-pretty-code sets `data-language` on
* `pre`/`code` (not `language-*` classes). It also sets `code { display: grid }`,
* which can add visible row separation — reset to a normal pre stack for text.
*/
[data-rehype-pretty-code-figure] pre[data-language="text"] code,
[data-rehype-pretty-code-figure] pre[data-language="plaintext"] code,
[data-slot="docs"] pre[data-language="text"] code,
[data-slot="docs"] pre[data-language="plaintext"] code {
display: block !important;
white-space: pre;
line-height: 0.95;
font-family:
ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono",
"Courier New", monospace;
font-variant-ligatures: none;
}
[data-rehype-pretty-code-figure] pre[data-language="text"] [data-line],
[data-rehype-pretty-code-figure] pre[data-language="plaintext"] [data-line],
[data-rehype-pretty-code-figure] code[data-language="text"] [data-line],
[data-rehype-pretty-code-figure] code[data-language="plaintext"] [data-line],
[data-slot="docs"] pre[data-language="text"] [data-line],
[data-slot="docs"] pre[data-language="plaintext"] [data-line] {
padding-top: 0;
padding-bottom: 0;
min-height: unset;
line-height: 0.95;
display: block;
}
[data-line] span {
color: var(--shiki-light);
@variant dark {
color: var(--shiki-dark) !important;
}
}
[data-highlighted-line],
[data-highlighted-chars] {
position: relative;
background-color: var(--color-code-highlight);
}
[data-highlighted-line] {
&:after {
position: absolute;
top: 0;
left: 0;
width: 2px;
height: 100%;
content: "";
background-color: color-mix(
in oklab,
var(--muted-foreground) 50%,
transparent
);
}
}
[data-highlighted-chars] {
border-radius: var(--radius-sm);
padding-inline: 0.3rem;
padding-block: 0.1rem;
font-family: var(--font-mono);
font-size: 0.8rem;
}
}
@layer components {
.dialog-ring {
@apply rounded-xl border-none bg-clip-padding shadow-2xl ring-4 ring-neutral-200/80 dark:bg-neutral-900 dark:ring-neutral-800;
}
}
author/apps/v4/app/shadcn-tailwind.css
@theme inline {
@keyframes accordion-down {
from {
height: 0;
}
to {
height: var(
--radix-accordion-content-height,
var(--accordion-panel-height, auto)
);
}
}
@keyframes accordion-up {
from {
height: var(
--radix-accordion-content-height,
var(--accordion-panel-height, auto)
);
}
to {
height: 0;
}
}
}
/* Custom variants */
@custom-variant data-open {
&:where([data-state="open"]),
&:where([data-open]:not([data-open="false"])) {
@slot;
}
}
@custom-variant data-closed {
&:where([data-state="closed"]),
&:where([data-closed]:not([data-closed="false"])) {
@slot;
}
}
@custom-variant data-checked {
&:where([data-state="checked"]),
&:where([data-checked]:not([data-checked="false"])) {
@slot;
}
}
@custom-variant data-unchecked {
&:where([data-state="unchecked"]),
&:where([data-unchecked]:not([data-unchecked="false"])) {
@slot;
}
}
@custom-variant data-selected {
&:where([data-selected="true"]) {
@slot;
}
}
@custom-variant data-disabled {
&:where([data-disabled="true"]),
&:where([data-disabled]:not([data-disabled="false"])) {
@slot;
}
}
@custom-variant data-active {
&:where([data-state="active"]),
&:where([data-active]:not([data-active="false"])) {
@slot;
}
}
@custom-variant data-horizontal {
&:where([data-orientation="horizontal"]) {
@slot;
}
}
@custom-variant data-vertical {
&:where([data-orientation="vertical"]) {
@slot;
}
}
@utility no-scrollbar {
-ms-overflow-style: none;
scrollbar-width: none;
&::-webkit-scrollbar {
display: none;
}
}
author/apps/v4/package.json
{
"name": "v4",
"version": "0.1.0",
"private": true,
"type": "module",
"scripts": {
"dev": "next dev --turbopack --port 4000",
"build": "pnpm registry:build && next build",
"start": "next start --port 4000",
"preview": "next build && next start --port 4000",
"lint": "eslint . --cache --cache-location .eslintcache",
"lint:fix": "eslint --fix . --cache --cache-location .eslintcache",
"typecheck": "tsc --noEmit",
"format:write": "prettier --write \"**/*.{ts,tsx,mdx}\" --cache",
"format:check": "prettier --check \"**/*.{ts,tsx,mdx}\" --cache",
"registry:build": "node scripts/build-registry.mts",
"registry:check-extend-ui": "node scripts/check-extend-ui-registry.mts",
"registry:test": "node --test scripts/registry-source.test.mts",
"registry:validate": "shadcn registry validate",
"postinstall": "fumadocs-mdx"
},
"dependencies": {
"@base-ui/react": "^1.4.1",
"@dnd-kit/core": "^6.3.1",
"@dnd-kit/sortable": "^10.0.0",
"@dnd-kit/utilities": "^3.2.2",
"@embedpdf/core": "^2.15.0",
"@embedpdf/default-stamps": "0.0.1",
"@embedpdf/engines": "^2.15.0",
"@embedpdf/models": "^2.15.0",
"@embedpdf/plugin-annotation": "2.15.0",
"@embedpdf/plugin-attachment": "2.15.0",
"@embedpdf/plugin-bookmark": "2.15.0",
"@embedpdf/plugin-capture": "2.15.0",
"@embedpdf/plugin-document-manager": "^2.15.0",
"@embedpdf/plugin-export": "2.15.0",
"@embedpdf/plugin-form": "2.15.0",
"@embedpdf/plugin-fullscreen": "2.15.0",
"@embedpdf/plugin-history": "2.15.0",
"@embedpdf/plugin-interaction-manager": "^2.15.0",
"@embedpdf/plugin-pan": "2.15.0",
"@embedpdf/plugin-print": "2.15.0",
"@embedpdf/plugin-redaction": "2.15.0",
"@embedpdf/plugin-render": "^2.15.0",
"@embedpdf/plugin-rotate": "^2.15.0",
"@embedpdf/plugin-scroll": "^2.15.0",
"@embedpdf/plugin-search": "^2.15.0",
"@embedpdf/plugin-selection": "^2.15.0",
"@embedpdf/plugin-signature": "2.15.0",
"@embedpdf/plugin-spread": "2.15.0",
"@embedpdf/plugin-stamp": "2.15.0",
"@embedpdf/plugin-thumbnail": "^2.15.0",
"@embedpdf/plugin-tiling": "^2.15.0",
"@embedpdf/plugin-view-manager": "2.15.0",
"@embedpdf/plugin-viewport": "^2.15.0",
"@embedpdf/plugin-zoom": "^2.15.0",
"@embedpdf/utils": "2.15.0",
"@extend-ai/react-docx": "0.9.2",
"@extend-ai/react-pptx": "0.2.1",
"@extend-ai/react-xlsx": "0.16.4",
"@glideapps/glide-data-grid": "6.0.4-alpha24",
"@hugeicons/core-free-icons": "^4.2.0",
"@hugeicons/react": "^1.1.6",
"@paper-design/shaders-react": "^0.0.76",
"@pierre/diffs": "^1.2.7",
"@pierre/trees": "1.0.0-beta.4",
"@radix-ui/react-hover-card": "^1.1.23",
"@radix-ui/react-popover": "^1.1.23",
"@radix-ui/react-scroll-area": "^1.2.18",
"@radix-ui/react-select": "^2.3.7",
"@tabler/icons-react": "^3.31.0",
"@tanstack/react-virtual": "3.13.26",
"@vercel/analytics": "^2.0.1",
"border-beam": "^1.0.1",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"cmdk": "^1.1.1",
"fumadocs-core": "16.0.5",
"fumadocs-mdx": "13.0.2",
"jotai": "^2.15.0",
"lodash": "^4.18.1",
"lru-cache": "^11.2.4",
"marked": "^16.0.10",
"next": "16.3.3",
"next-themes": "0.4.6",
"nuqs": "^2.8.9",
"papaparse": "^5.5.3",
"pdf-lib": "^1.17.1",
"radix-ui": "^1.4.3",
"react": "19.2.3",
"react-dom": "19.2.3",
"react-markdown": "^10.1.0",
"react-resizable-panels": "^4.11.2",
"react-responsive-carousel": "^3.2.23",
"rehype-pretty-code": "^0.14.1",
"rehype-raw": "^7.0.0",
"rehype-sanitize": "^6.0.0",
"remark-gfm": "4.0.1",
"shadcn": "^4.8.2",
"shiki": "^1.10.1",
"signature_pad": "^5.1.3",
"sonner": "^2.0.0",
"tailwind-merge": "^3.3.1",
"zod": "^3.25.76"
},
"devDependencies": {
"@ianvs/prettier-plugin-sort-imports": "^4.4.1",
"@tailwindcss/postcss": "^4.2.4",
"@types/node": "^20",
"@types/papaparse": "^5.5.2",
"@types/react": "19.2.2",
"@types/react-dom": "19.2.2",
"babel-plugin-react-compiler": "^1.0.0",
"eslint": "^9",
"eslint-config-next": "16.2.10",
"eslint-plugin-react-hooks": "7.1.1",
"prettier": "^3.4.2",
"prettier-plugin-tailwindcss": "^0.7.2",
"tailwindcss": "^4",
"ts-morph": "27.0.2",
"tw-animate-css": "^1.4.0",
"typescript": "^5",
"typescript-eslint": "^8.59.1"
},
"prettier": {
"endOfLine": "lf",
"semi": false,
"singleQuote": false,
"tabWidth": 2,
"trailingComma": "es5",
"importOrder": [
"^(react/(.*)$)|^(react$)",
"^(next/(.*)$)|^(next$)",
"<THIRD_PARTY_MODULES>",
"",
"^types$",
"^@/types/(.*)$",
"^@/config/(.*)$",
"^@/lib/(.*)$",
"^@/hooks/(.*)$",
"^@/components/ui/(.*)$",
"^@/components/(.*)$",
"^@/registry/(.*)$",
"^@/styles/(.*)$",
"^@/app/(.*)$",
"^@/www/(.*)$",
"",
"^[./]"
],
"importOrderParserPlugins": [
"typescript",
"jsx",
"decorators-legacy"
],
"tailwindStylesheet": "./app/globals.css",
"tailwindFunctions": [
"cn",
"cva"
],
"plugins": [
"@ianvs/prettier-plugin-sort-imports",
"prettier-plugin-tailwindcss"
]
}
}
Usage.tsx실행 안내·자료
// Local host for the unchanged exact baseline demonstration.
import OriginalDemo from "./baseline-example.tsx";
export default function Demo() { return <><OriginalDemo /></>; }
runtime/author-mount.tsx실행 안내·자료
/** Local sandbox host. Ready is a mount observation, never a verification result. */
import React, { Component, useEffect } from 'react';
import { createRoot } from 'react-dom/client';
declare global {
interface Window {
__STYLEGALLERY_PREVIEW__: { id: string; status: string; errors: string[] };
}
}
export function mount(Demo: React.ComponentType, id: string) {
const state = window.__STYLEGALLERY_PREVIEW__ = { id, status: 'loading', errors: [] as string[] };
const send = (message: object) => parent.postMessage({ ...message, id }, '*');
const report = (error: unknown) => {
const message = error instanceof Error ? error.message : String(error);
if (!state.errors.includes(message)) state.errors.push(message);
state.status = 'error';
document.body.dataset.previewStatus = 'error';
send({ type: 'sg-preview-error', message });
};
window.addEventListener('error', (event) => report(event.error ?? event.message));
window.addEventListener('unhandledrejection', (event) => report(event.reason));
window.addEventListener('securitypolicyviolation', (event) => report(`Blocked by preview policy: ${event.violatedDirective}`));
const theme = new URLSearchParams(location.search).get('theme') === 'dark' ? 'dark' : 'light';
document.documentElement.classList.toggle('dark', theme === 'dark');
document.documentElement.dataset.theme = theme;
// Keep original source bytes while ensuring imported attribution/navigation remains local text.
const removeDestinations = () => document.querySelectorAll('a[href]').forEach((link) => {
link.removeAttribute('href');
link.removeAttribute('target');
});
document.addEventListener('click', (event) => {
if ((event.target as Element)?.closest?.('a')) event.preventDefault();
}, true);
document.addEventListener('submit', (event) => event.preventDefault());
new MutationObserver(removeDestinations).observe(document.getElementById('root')!, { childList: true, subtree: true });
const observe = () => {
const root = document.getElementById('root')!;
const elements = Array.from(root.querySelectorAll('*'));
const rect = root.getBoundingClientRect();
const diagnostics = {
textLength: (root.textContent ?? '').trim().length,
elementCount: elements.length,
visibleElementCount: elements.filter((element) => {
const bounds = element.getBoundingClientRect();
const style = getComputedStyle(element);
return bounds.width > 0 && bounds.height > 0 && style.display !== 'none' && style.visibility !== 'hidden' && Number(style.opacity) !== 0;
}).length,
images: [],
canvases: [],
rootRect: { width: rect.width, height: rect.height },
documentWidth: document.documentElement.scrollWidth,
viewportWidth: innerWidth,
};
send({ type: 'sg-preview-observation', diagnostics });
};
class Boundary extends Component<{ children: React.ReactNode }, { error: string | null }> {
state = { error: null as string | null };
static getDerivedStateFromError(error: Error) { return { error: error.message }; }
componentDidCatch(error: Error) { report(error); }
render() { return this.state.error ? <p role="alert">This preview could not render: {this.state.error}</p> : this.props.children; }
}
function Ready() {
useEffect(() => {
const preference = matchMedia('(prefers-reduced-motion: reduce)');
const syncSVG = () => document.querySelectorAll('svg').forEach((svg) => {
if (preference.matches) svg.pauseAnimations?.();
else svg.unpauseAnimations?.();
});
syncSVG();
preference.addEventListener('change', syncSVG);
let second = 0;
let delayed = 0;
const first = requestAnimationFrame(() => { second = requestAnimationFrame(() => {
if (state.status !== 'error') {
state.status = 'mounted';
document.body.dataset.previewStatus = 'mounted';
send({ type: 'sg-preview-ready' });
observe();
delayed = window.setTimeout(observe, 700);
}
}); });
return () => {
cancelAnimationFrame(first);
cancelAnimationFrame(second);
clearTimeout(delayed);
preference.removeEventListener('change', syncSVG);
};
}, []);
return <Demo />;
}
createRoot(document.getElementById('root')!).render(<Boundary><Ready /></Boundary>);
}
runtime/author-styles.css실행 안내·자료
@import "tailwindcss";
@custom-variant dark (&:where(.dark, .dark *));
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-card: var(--background);
--color-card-foreground: var(--foreground);
--color-primary: var(--primary);
--color-primary-foreground: var(--primary-foreground);
--color-secondary: var(--muted);
--color-secondary-foreground: var(--foreground);
--color-muted: var(--muted);
--color-muted-foreground: var(--muted-foreground);
--color-accent: var(--muted);
--color-accent-foreground: var(--foreground);
--color-popover: var(--background);
--color-popover-foreground: var(--foreground);
--color-destructive: #dc2626;
--color-destructive-foreground: #ffffff;
--color-input: var(--border);
--color-border: var(--border);
--color-ring: var(--foreground);
--radius-lg: 0.5rem;
--radius-md: 0.375rem;
--radius-sm: 0.25rem;
}
:root {
--background: #ffffff;
--border: #d4d4d8;
--foreground: #18181b;
--muted: #f4f4f5;
--muted-foreground: #71717a;
--primary: #18181b;
--primary-foreground: #fafafa;
background: var(--background);
color: var(--foreground);
color-scheme: light;
font-family: Arial, sans-serif;
}
.dark {
--background: #09090b;
--border: #3f3f46;
--foreground: #fafafa;
--muted: #27272a;
--muted-foreground: #a1a1aa;
--primary: #fafafa;
--primary-foreground: #18181b;
color-scheme: dark;
}
body {
margin: 0;
min-height: 100vh;
}
#root {
align-items: center;
box-sizing: border-box;
display: flex;
justify-content: center;
min-height: 100vh;
padding: 24px;
width: 100%;
}
#root > * {
max-width: 100%;
}
noscript {
display: block;
padding: 24px;
}
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-play-state: paused !important;
}
}
runtime/applied-theme.css실행 안내·자료
@custom-variant style-vega (&:where(.style-vega *));
@custom-variant style-nova (&:where(.style-nova *));
@custom-variant style-lyra (&:where(.style-lyra *));
@custom-variant style-maia (&:where(.style-maia *));
@custom-variant style-mira (&:where(.style-mira *));
@custom-variant style-luma (&:where(.style-luma *));
@custom-variant style-sera (&:where(.style-sera *));
@custom-variant dark (&:is(.dark *));
@custom-variant fixed (&:is(.layout-fixed *));
@theme inline {
--breakpoint-3xl: 1600px;
--breakpoint-4xl: 2000px;
--font-sans: var(--font-sans);
--font-heading: var(--font-heading);
--font-mono: var(--font-mono);
--radius-sm: calc(var(--radius) * 0.6);
--radius-md: calc(var(--radius) * 0.8);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) * 1.4);
--radius-2xl: calc(var(--radius) * 1.8);
--radius-3xl: calc(var(--radius) * 2.2);
--radius-4xl: calc(var(--radius) * 2.6);
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-card: var(--card);
--color-card-foreground: var(--card-foreground);
--color-popover: var(--popover);
--color-popover-foreground: var(--popover-foreground);
--color-primary: var(--primary);
--color-primary-foreground: var(--primary-foreground);
--color-secondary: var(--secondary);
--color-secondary-foreground: var(--secondary-foreground);
--color-muted: var(--muted);
--color-muted-foreground: var(--muted-foreground);
--color-accent: var(--accent);
--color-accent-foreground: var(--accent-foreground);
--color-destructive: var(--destructive);
--color-destructive-foreground: var(--destructive-foreground);
--color-border: var(--border);
--color-input: var(--input);
--color-ring: var(--ring);
--color-chart-1: var(--chart-1);
--color-chart-2: var(--chart-2);
--color-chart-3: var(--chart-3);
--color-chart-4: var(--chart-4);
--color-chart-5: var(--chart-5);
--color-sidebar: var(--sidebar);
--color-sidebar-foreground: var(--sidebar-foreground);
--color-sidebar-primary: var(--sidebar-primary);
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
--color-sidebar-accent: var(--sidebar-accent);
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
--color-sidebar-border: var(--sidebar-border);
--color-sidebar-ring: var(--sidebar-ring);
--color-surface: var(--surface);
--color-surface-foreground: var(--surface-foreground);
--color-code: var(--code);
--color-code-foreground: var(--code-foreground);
--color-code-highlight: var(--code-highlight);
--color-code-number: var(--code-number);
--color-selection: var(--selection);
--color-selection-foreground: var(--selection-foreground);
--color-warning-foreground: var(--warning-foreground);
--color-warning: var(--warning);
--color-success-foreground: var(--success-foreground);
--color-success: var(--success);
--color-info-foreground: var(--info-foreground);
--color-info: var(--info);
--animate-skeleton: skeleton 2s -1s infinite linear;
@keyframes skeleton {
to {
background-position: -200% 0;
}
}
}
:root {
--radius: 0.625rem;
--background: oklch(1 0 0);
--foreground: var(--color-neutral-800);
--card: var(--color-white);
--card-foreground: var(--color-neutral-800);
--popover: var(--color-white);
--popover-foreground: var(--color-neutral-800);
--primary: var(--color-neutral-800);
--primary-foreground: var(--color-neutral-50);
--secondary: --alpha(var(--color-black) / 4%);
--secondary-foreground: var(--color-neutral-800);
--muted: --alpha(var(--color-black) / 4%);
--muted-foreground: color-mix(
in srgb,
var(--color-neutral-500) 90%,
var(--color-black)
);
--accent: --alpha(var(--color-black) / 4%);
--accent-foreground: var(--color-neutral-800);
--destructive: var(--color-red-500);
--destructive-foreground: var(--color-red-700);
--border: --alpha(var(--color-black) / 8%);
--input: --alpha(var(--color-black) / 10%);
--ring: var(--color-neutral-400);
--chart-1: var(--color-blue-300);
--chart-2: var(--color-blue-500);
--chart-3: var(--color-blue-600);
--chart-4: var(--color-blue-700);
--chart-5: var(--color-blue-800);
--sidebar: var(--color-neutral-50);
--sidebar-foreground: color-mix(
in srgb,
var(--color-neutral-800) 64%,
var(--sidebar)
);
--sidebar-primary: var(--color-neutral-800);
--sidebar-primary-foreground: var(--color-neutral-50);
--sidebar-accent: --alpha(var(--color-black) / 4%);
--sidebar-accent-foreground: var(--color-neutral-800);
--sidebar-border: --alpha(var(--color-black) / 6%);
--sidebar-ring: var(--color-neutral-400);
--surface: oklch(0.98 0 0);
--surface-foreground: var(--foreground);
--code: var(--surface);
--code-foreground: var(--surface-foreground);
--code-highlight: oklch(0.96 0 0);
--code-number: oklch(0.56 0 0);
--selection: oklch(0.145 0 0);
--selection-foreground: oklch(1 0 0);
--info: var(--color-blue-500);
--info-foreground: var(--color-blue-700);
--success: var(--color-emerald-500);
--success-foreground: var(--color-emerald-700);
--warning: var(--color-amber-500);
--warning-foreground: var(--color-amber-700);
}
.dark {
--background: oklch(0.145 0 0);
--foreground: var(--color-neutral-100);
--card: color-mix(in srgb, var(--background) 98%, var(--color-white));
--card-foreground: var(--color-neutral-100);
--popover: color-mix(in srgb, var(--background) 98%, var(--color-white));
--popover-foreground: var(--color-neutral-100);
--primary: var(--color-neutral-100);
--primary-foreground: var(--color-neutral-800);
--secondary: --alpha(var(--color-white) / 4%);
--secondary-foreground: var(--color-neutral-100);
--muted: --alpha(var(--color-white) / 4%);
--muted-foreground: color-mix(
in srgb,
var(--color-neutral-500) 90%,
var(--color-white)
);
--accent: --alpha(var(--color-white) / 4%);
--accent-foreground: var(--color-neutral-100);
--destructive: color-mix(
in srgb,
var(--color-red-500) 90%,
var(--color-white)
);
--destructive-foreground: var(--color-red-400);
--border: --alpha(var(--color-white) / 6%);
--input: --alpha(var(--color-white) / 8%);
--ring: var(--color-neutral-500);
--chart-1: var(--color-blue-300);
--chart-2: var(--color-blue-500);
--chart-3: var(--color-blue-600);
--chart-4: var(--color-blue-700);
--chart-5: var(--color-blue-800);
--sidebar: color-mix(
in srgb,
var(--color-neutral-950) 97%,
var(--color-white)
);
--sidebar-foreground: color-mix(
in srgb,
var(--color-neutral-100) 64%,
var(--sidebar)
);
--sidebar-primary: var(--color-neutral-100);
--sidebar-primary-foreground: var(--color-neutral-800);
--sidebar-accent: --alpha(var(--color-white) / 4%);
--sidebar-accent-foreground: var(--color-neutral-100);
--sidebar-border: --alpha(var(--color-white) / 5%);
--sidebar-ring: var(--color-neutral-400);
--surface: oklch(0.2 0 0);
--surface-foreground: oklch(0.708 0 0);
--code: var(--surface);
--code-foreground: var(--surface-foreground);
--code-highlight: oklch(0.27 0 0);
--code-number: oklch(0.72 0 0);
--selection: oklch(0.922 0 0);
--selection-foreground: oklch(0.205 0 0);
--info: var(--color-blue-500);
--info-foreground: var(--color-blue-400);
--success: var(--color-emerald-500);
--success-foreground: var(--color-emerald-400);
--warning: var(--color-amber-500);
--warning-foreground: var(--color-amber-400);
}
@layer base {
* {
@apply border-border outline-ring/50;
}
::selection {
@apply bg-selection text-selection-foreground;
}
html {
@apply overscroll-y-none;
}
body {
font-synthesis-weight: none;
text-rendering: optimizeLegibility;
}
.cn-font-heading {
@apply font-heading;
}
[data-slot="layout"] {
@apply overscroll-none;
}
@supports (font: -apple-system-body) and (-webkit-appearance: none) {
[data-wrapper] {
@apply min-[1800px]:border-t;
}
}
a:active,
button:active {
@apply opacity-60 md:opacity-100;
}
[data-lang="ar"] {
font-family: var(--font-ar);
}
[data-lang="he"] {
font-family: var(--font-he);
}
}
@utility border-grid {
@apply border-border/50 dark:border-border;
}
@utility section-soft {
@apply bg-linear-to-b from-background to-surface/40 dark:bg-background 3xl:fixed:bg-none;
}
@utility theme-container {
@apply font-sans;
}
@utility container-wrapper {
@apply mx-auto w-full px-2 3xl:fixed:max-w-[calc(var(--breakpoint-2xl)+2rem)];
}
@utility container {
@apply mx-auto max-w-[1400px] px-4 3xl:max-w-screen-2xl lg:px-8;
}
@utility no-scrollbar {
-ms-overflow-style: none;
scrollbar-width: none;
&::-webkit-scrollbar {
display: none;
}
}
@utility scrollbar-overflow-only {
&:not(:has([data-slot="scroll-area-viewport"][data-has-overflow-x]))
[data-slot="scroll-area-scrollbar"][data-orientation="horizontal"] {
display: none;
}
&:not(:has([data-slot="scroll-area-viewport"][data-has-overflow-y]))
[data-slot="scroll-area-scrollbar"][data-orientation="vertical"] {
display: none;
}
}
@utility border-ghost {
@apply relative after:absolute after:inset-0 after:border after:border-border after:mix-blend-darken dark:after:mix-blend-lighten;
}
@utility step {
counter-increment: step;
&:before {
@apply mr-2 inline-flex size-6 items-center justify-center rounded-full border-background bg-background text-center -indent-px font-mono text-sm font-medium md:absolute md:mt-[-4px] md:ml-[-50px] md:size-9 md:border-4;
box-shadow: inset 0 0 0 9999px var(--muted);
content: counter(step);
}
}
@utility extend-touch-target {
@media (pointer: coarse) {
@apply relative touch-manipulation after:absolute after:-inset-2;
}
}
@layer components {
.steps {
&:first-child {
@apply !mt-0;
}
&:first-child > h3:first-child {
@apply !mt-0;
}
> h3 {
@apply !mt-8;
}
> h3 + p {
@apply !mt-2;
}
}
.docs-view-code-button {
background-color: var(--background) !important;
color: var(--foreground);
box-shadow: 0 1px 2px rgb(0 0 0 / 6%) !important;
transition-property: border-color, box-shadow, color;
}
.docs-view-code-button:hover,
.docs-view-code-button[data-pressed] {
background-color: var(--background) !important;
border-color: color-mix(in oklab, var(--ring) 55%, transparent) !important;
box-shadow:
0 4px 14px rgb(0 0 0 / 10%),
0 0 0 3px color-mix(in oklab, var(--ring) 16%, transparent) !important;
}
.dark .docs-view-code-button {
background-color: var(--background) !important;
box-shadow: 0 1px 2px rgb(0 0 0 / 30%) !important;
}
.dark .docs-view-code-button:hover,
.dark .docs-view-code-button[data-pressed] {
background-color: var(--background) !important;
box-shadow:
0 4px 14px rgb(0 0 0 / 36%),
0 0 0 3px color-mix(in oklab, var(--ring) 20%, transparent) !important;
}
[data-rehype-pretty-code-figure] {
background-color: var(--color-code);
color: var(--color-code-foreground);
border-radius: var(--radius-xl);
border-width: 0px;
border-color: var(--border);
margin-top: calc(var(--spacing) * 6);
overflow: hidden;
font-size: var(--text-sm);
outline: none;
position: relative;
@apply -mx-1 md:-mx-1;
&:has([data-rehype-pretty-code-title]) [data-slot="copy-button"] {
top: calc(var(--spacing) * 1.5) !important;
}
}
[data-rehype-pretty-code-title] {
border-bottom: color-mix(in oklab, var(--border) 30%, transparent);
border-bottom-width: 1px;
border-bottom-style: solid;
padding-block: calc(var(--spacing) * 2.5);
padding-inline: calc(var(--spacing) * 4);
font-size: var(--text-sm);
font-family: var(--font-mono);
color: var(--color-code-foreground);
}
[data-line-numbers] {
display: grid;
min-width: 100%;
white-space: pre;
border: 0;
background: transparent;
padding: 0;
counter-reset: line;
box-decoration-break: clone;
}
[data-line-numbers] [data-line]::before {
font-size: var(--text-sm);
counter-increment: line;
content: counter(line);
display: inline-block;
width: calc(var(--spacing) * 16);
padding-right: calc(var(--spacing) * 6);
text-align: right;
color: var(--color-code-number);
background-color: var(--color-code);
position: sticky;
left: 0;
}
[data-line-numbers] [data-highlighted-line][data-line]::before {
background-color: var(--color-code-highlight);
}
[data-line] {
padding-top: calc(var(--spacing) * 0.5);
padding-bottom: calc(var(--spacing) * 0.5);
min-height: calc(var(--spacing) * 1);
width: 100%;
display: inline-block;
}
/*
* ```text composition trees use box-drawing characters; per-line padding makes
* vertical connectors look broken. rehype-pretty-code sets `data-language` on
* `pre`/`code` (not `language-*` classes). It also sets `code { display: grid }`,
* which can add visible row separation — reset to a normal pre stack for text.
*/
[data-rehype-pretty-code-figure] pre[data-language="text"] code,
[data-rehype-pretty-code-figure] pre[data-language="plaintext"] code,
[data-slot="docs"] pre[data-language="text"] code,
[data-slot="docs"] pre[data-language="plaintext"] code {
display: block !important;
white-space: pre;
line-height: 0.95;
font-family:
ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono",
"Courier New", monospace;
font-variant-ligatures: none;
}
[data-rehype-pretty-code-figure] pre[data-language="text"] [data-line],
[data-rehype-pretty-code-figure] pre[data-language="plaintext"] [data-line],
[data-rehype-pretty-code-figure] code[data-language="text"] [data-line],
[data-rehype-pretty-code-figure] code[data-language="plaintext"] [data-line],
[data-slot="docs"] pre[data-language="text"] [data-line],
[data-slot="docs"] pre[data-language="plaintext"] [data-line] {
padding-top: 0;
padding-bottom: 0;
min-height: unset;
line-height: 0.95;
display: block;
}
[data-line] span {
color: var(--shiki-light);
@variant dark {
color: var(--shiki-dark) !important;
}
}
[data-highlighted-line],
[data-highlighted-chars] {
position: relative;
background-color: var(--color-code-highlight);
}
[data-highlighted-line] {
&:after {
position: absolute;
top: 0;
left: 0;
width: 2px;
height: 100%;
content: "";
background-color: color-mix(
in oklab,
var(--muted-foreground) 50%,
transparent
);
}
}
[data-highlighted-chars] {
border-radius: var(--radius-sm);
padding-inline: 0.3rem;
padding-block: 0.1rem;
font-family: var(--font-mono);
font-size: 0.8rem;
}
}
@layer components {
.dialog-ring {
@apply rounded-xl border-none bg-clip-padding shadow-2xl ring-4 ring-neutral-200/80 dark:bg-neutral-900 dark:ring-neutral-800;
}
}
@theme inline {
@keyframes accordion-down {
from {
height: 0;
}
to {
height: var(
--radix-accordion-content-height,
var(--accordion-panel-height, auto)
);
}
}
@keyframes accordion-up {
from {
height: var(
--radix-accordion-content-height,
var(--accordion-panel-height, auto)
);
}
to {
height: 0;
}
}
}
/* Custom variants */
@custom-variant data-open {
&:where([data-state="open"]),
&:where([data-open]:not([data-open="false"])) {
@slot;
}
}
@custom-variant data-closed {
&:where([data-state="closed"]),
&:where([data-closed]:not([data-closed="false"])) {
@slot;
}
}
@custom-variant data-checked {
&:where([data-state="checked"]),
&:where([data-checked]:not([data-checked="false"])) {
@slot;
}
}
@custom-variant data-unchecked {
&:where([data-state="unchecked"]),
&:where([data-unchecked]:not([data-unchecked="false"])) {
@slot;
}
}
@custom-variant data-selected {
&:where([data-selected="true"]) {
@slot;
}
}
@custom-variant data-disabled {
&:where([data-disabled="true"]),
&:where([data-disabled]:not([data-disabled="false"])) {
@slot;
}
}
@custom-variant data-active {
&:where([data-state="active"]),
&:where([data-active]:not([data-active="false"])) {
@slot;
}
}
@custom-variant data-horizontal {
&:where([data-orientation="horizontal"]) {
@slot;
}
}
@custom-variant data-vertical {
&:where([data-orientation="vertical"]) {
@slot;
}
}
@utility no-scrollbar {
-ms-overflow-style: none;
scrollbar-width: none;
&::-webkit-scrollbar {
display: none;
}
}
THIRD-PARTY-LICENSES.txt실행 안내·자료
react 19.2.3 — LICENSE
MIT License
Copyright (c) Meta Platforms, Inc. and affiliates.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
========================================================================
lodash 4.17.21 — LICENSE
Copyright OpenJS Foundation and other contributors <https://openjsf.org/>
Based on Underscore.js, copyright Jeremy Ashkenas,
DocumentCloud and Investigative Reporters & Editors <http://underscorejs.org/>
This software consists of voluntary contributions made by many
individuals. For exact contribution history, see the revision history
available at https://github.com/lodash/lodash
The following license applies to all parts of this software except as
documented below:
====
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
====
Copyright and related rights for sample code are waived via CC0. Sample
code is defined as all source code displayed within the prose of the
documentation.
CC0: http://creativecommons.org/publicdomain/zero/1.0/
====
Files located in the node_modules and vendor directories are externally
maintained libraries used by this software which have their own
licenses; we recommend you read them, as their terms may differ from the
terms above.
========================================================================
@glideapps/glide-data-grid 6.0.4-alpha24 — LICENSE
MIT License
Copyright (c) 2021 typeguard, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
========================================================================
@emotion/memoize 0.9.0 — LICENSE
MIT License
Copyright (c) Emotion team and other contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
========================================================================
@emotion/is-prop-valid 1.4.0 — LICENSE
MIT License
Copyright (c) Emotion team and other contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
========================================================================
@linaria/core 6.3.0 — LICENSE
MIT License
Copyright (c) 2017 Callstack
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
========================================================================
@linaria/react 6.3.0 — LICENSE
MIT License
Copyright (c) 2017 Callstack
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
========================================================================
prop-types 15.8.1 — LICENSE
MIT License
Copyright (c) 2013-present, Facebook, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
========================================================================
react-easy-swipe 0.0.21 — MIT notice from the official repository at a620dfd858b725190dd631199c7536e519a5140f; repository package version 0.0.23
MIT License
Copyright (c) 2016 Leandro Lemos
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
========================================================================
classnames 2.5.1 — LICENSE
The MIT License (MIT)
Copyright (c) 2018 Jed Watson
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
========================================================================
react-responsive-carousel 3.2.23 — LICENSE.md
MIT License
Copyright (c) [year] [fullname]
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
========================================================================
marked 16.4.2 — LICENSE.md
# License information
## Contribution License Agreement
If you contribute code to this project, you are implicitly allowing your code
to be distributed under the MIT license. You are also implicitly verifying that
all code is your original work. `</legalese>`
## Marked
Copyright (c) 2018+, MarkedJS (https://github.com/markedjs/)
Copyright (c) 2011-2018, Christopher Jeffrey (https://github.com/chjj/)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
## Markdown
Copyright © 2004, John Gruber
http://daringfireball.net/
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the name “Markdown” nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
This software is provided by the copyright holders and contributors “as is” and any express or implied warranties, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose are disclaimed. In no event shall the copyright owner or contributors be liable for any direct, indirect, incidental, special, exemplary, or consequential damages (including, but not limited to, procurement of substitute goods or services; loss of use, data, or profits; or business interruption) however caused and on any theory of liability, whether in contract, strict liability, or tort (including negligence or otherwise) arising in any way out of the use of this software, even if advised of the possibility of such damage.
========================================================================
canvas-hypertxt 1.0.3 — LICENSE
MIT License
Copyright (c) 2022 Glide
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
========================================================================
react-dom 19.2.3 — LICENSE
MIT License
Copyright (c) Meta Platforms, Inc. and affiliates.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
========================================================================
react-number-format 5.4.5 — LICENSE
MIT License
Copyright (c) 2020-present Sudhanshu Yadav
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
========================================================================
@pierre/diffs 1.2.7 — LICENSE.md
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction, and
distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by the
copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all other
entities that control, are controlled by, or are under common control with
that entity. For the purposes of this definition, "control" means (i) the
power, direct or indirect, to cause the direction or management of such
entity, whether by contract or otherwise, or (ii) ownership of fifty percent
(50%) or more of the outstanding shares, or (iii) beneficial ownership of
such entity.
"You" (or "Your") shall mean an individual or Legal Entity exercising
permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation source, and
configuration files.
"Object" form shall mean any form resulting from mechanical transformation
or translation of a Source form, including but not limited to compiled
object code, generated documentation, and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or Object form,
made available under the License, as indicated by a copyright notice that is
included in or attached to the work (an example is provided in the Appendix
below).
"Derivative Works" shall mean any work, whether in Source or Object form,
that is based on (or derived from) the Work and for which the editorial
revisions, annotations, elaborations, or other modifications represent, as a
whole, an original work of authorship. For the purposes of this License,
Derivative Works shall not include works that remain separable from, or
merely link (or bind by name) to the interfaces of, the Work and Derivative
Works thereof.
"Contribution" shall mean any work of authorship, including the original
version of the Work and any modifications or additions to that Work or
Derivative Works thereof, that is intentionally submitted to Licensor for
inclusion in the Work by the copyright owner or by an individual or Legal
Entity authorized to submit on behalf of the copyright owner. For the
purposes of this definition, "submitted" means any form of electronic,
verbal, or written communication sent to the Licensor or its
representatives, including but not limited to communication on electronic
mailing lists, source code control systems, and issue tracking systems that
are managed by, or on behalf of, the Licensor for the purpose of discussing
and improving the Work, but excluding communication that is conspicuously
marked or otherwise designated in writing by the copyright owner as "Not a
Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity on
behalf of whom a Contribution has been received by Licensor and subsequently
incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of this
License, each Contributor hereby grants to You a perpetual, worldwide,
non-exclusive, no-charge, royalty-free, irrevocable copyright license to
reproduce, prepare Derivative Works of, publicly display, publicly perform,
sublicense, and distribute the Work and such Derivative Works in Source or
Object form.
3. Grant of Patent License. Subject to the terms and conditions of this
License, each Contributor hereby grants to You a perpetual, worldwide,
non-exclusive, no-charge, royalty-free, irrevocable (except as stated in
this section) patent license to make, have made, use, offer to sell, sell,
import, and otherwise transfer the Work, where such license applies only to
those patent claims licensable by such Contributor that are necessarily
infringed by their Contribution(s) alone or by combination of their
Contribution(s) with the Work to which such Contribution(s) was submitted.
If You institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work or a
Contribution incorporated within the Work constitutes direct or contributory
patent infringement, then any patent licenses granted to You under this
License for that Work shall terminate as of the date such litigation is
filed.
4. Redistribution. You may reproduce and distribute copies of the Work or
Derivative Works thereof in any medium, with or without modifications, and
in Source or Object form, provided that You meet the following conditions:
(a) You must give any other recipients of the Work or Derivative Works a
copy of this License; and
(b) You must cause any modified files to carry prominent notices stating
that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works that You
distribute, all copyright, patent, trademark, and attribution notices from
the Source form of the Work, excluding those notices that do not pertain to
any part of the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its distribution,
then any Derivative Works that You distribute must include a readable copy
of the attribution notices contained within such NOTICE file, excluding
those notices that do not pertain to any part of the Derivative Works, in at
least one of the following places: within a NOTICE text file distributed as
part of the Derivative Works; within the Source form or documentation, if
provided along with the Derivative Works; or, within a display generated by
the Derivative Works, if and wherever such third-party notices normally
appear. The contents of the NOTICE file are for informational purposes only
and do not modify the License. You may add Your own attribution notices
within Derivative Works that You distribute, alongside or as an addendum to
the NOTICE text from the Work, provided that such additional attribution
notices cannot be construed as modifying the License.
You may add Your own copyright statement to Your modifications and may
provide additional or different license terms and conditions for use,
reproduction, or distribution of Your modifications, or for any such
Derivative Works as a whole, provided Your use, reproduction, and
distribution of the Work otherwise complies with the conditions stated in
this License.
5. Submission of Contributions. Unless You explicitly state otherwise, any
Contribution intentionally submitted for inclusion in the Work by You to the
Licensor shall be under the terms and conditions of this License, without
any additional terms or conditions. Notwithstanding the above, nothing
herein shall supersede or modify the terms of any separate license agreement
you may have executed with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade names,
trademarks, service marks, or product names of the Licensor, except as
required for reasonable and customary use in describing the origin of the
Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or agreed to in
writing, Licensor provides the Work (and each Contributor provides its
Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied, including, without limitation, any
warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or
FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining
the appropriateness of using or redistributing the Work and assume any risks
associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory, whether in
tort (including negligence), contract, or otherwise, unless required by
applicable law (such as deliberate and grossly negligent acts) or agreed to
in writing, shall any Contributor be liable to You for damages, including
any direct, indirect, special, incidental, or consequential damages of any
character arising as a result of this License or out of the use or inability
to use the Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all other
commercial damages or losses), even if such Contributor has been advised of
the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing the Work or
Derivative Works thereof, You may choose to offer, and charge a fee for,
acceptance of support, warranty, indemnity, or other liability obligations
and/or rights consistent with this License. However, in accepting such
obligations, You may act only on Your own behalf and on Your sole
responsibility, not on behalf of any other Contributor, and only if You
agree to indemnify, defend, and hold each Contributor harmless for any
liability incurred by, or claims asserted against, such Contributor by
reason of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2025 Pierre Computer Company
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License.
========================================================================
html-void-elements 3.0.0 — license
(The MIT License)
Copyright (c) 2016 Titus Wormer <tituswormer@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
========================================================================
property-information 7.2.0 — license
(The MIT License)
Copyright (c) Titus Wormer <mailto:tituswormer@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
========================================================================
zwitch 2.0.4 — license
(The MIT License)
Copyright (c) 2016 Titus Wormer <tituswormer@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
========================================================================
stringify-entities 4.0.4 — license
(The MIT License)
Copyright (c) 2015 Titus Wormer <mailto:tituswormer@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
========================================================================
character-entities-legacy 3.0.0 — license
(The MIT License)
Copyright (c) 2015 Titus Wormer <tituswormer@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
========================================================================
character-entities-html4 2.1.0 — license
(The MIT License)
Copyright (c) 2015 Titus Wormer <tituswormer@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
========================================================================
hast-util-to-html 9.0.5 — license
(The MIT License)
Copyright (c) Titus Wormer <tituswormer@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
========================================================================
ccount 2.0.1 — license
(The MIT License)
Copyright (c) 2015 Titus Wormer <tituswormer@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
========================================================================
comma-separated-tokens 2.0.3 — license
(The MIT License)
Copyright (c) 2016 Titus Wormer <tituswormer@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
========================================================================
space-separated-tokens 2.0.2 — license
(The MIT License)
Copyright (c) 2016 Titus Wormer <tituswormer@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
========================================================================
hast-util-whitespace 3.0.0 — license
(The MIT License)
Copyright (c) 2016 Titus Wormer <tituswormer@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
========================================================================
@shikijs/types 3.23.0 — LICENSE
MIT License
Copyright (c) 2021 Pine Wu
Copyright (c) 2023 Anthony Fu <https://github.com/antfu>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
========================================================================
@shikijs/vscode-textmate 10.0.2 — LICENSE.md
The MIT License (MIT)
Copyright (c) Microsoft Corporation
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
========================================================================
@shikijs/core 3.23.0 — LICENSE
MIT License
Copyright (c) 2021 Pine Wu
Copyright (c) 2023 Anthony Fu <https://github.com/antfu>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
========================================================================
@shikijs/langs 3.23.0 — LICENSE
MIT License
Copyright (c) 2021 Pine Wu
Copyright (c) 2023 Anthony Fu <https://github.com/antfu>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
========================================================================
shiki 3.23.0 — LICENSE
MIT License
Copyright (c) 2021 Pine Wu
Copyright (c) 2023 Anthony Fu <https://github.com/antfu>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
========================================================================
@shikijs/themes 3.23.0 — LICENSE
MIT License
Copyright (c) 2021 Pine Wu
Copyright (c) 2023 Anthony Fu <https://github.com/antfu>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
========================================================================
@shikijs/engine-oniguruma 3.23.0 — LICENSE
MIT License
Copyright (c) 2021 Pine Wu
Copyright (c) 2023 Anthony Fu <https://github.com/antfu>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
========================================================================
oniguruma-parser 0.12.2 — LICENSE
MIT License
Copyright (c) 2025-2026 Steven Levithan
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
========================================================================
regex 6.1.0 — LICENSE
MIT License
Copyright (c) 2025 Steven Levithan
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
========================================================================
regex-utilities 2.3.0 — LICENSE
MIT License
Copyright (c) 2024 Steven Levithan
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
========================================================================
regex-recursion 6.0.2 — LICENSE
MIT License
Copyright (c) 2025 Steven Levithan
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
========================================================================
oniguruma-to-es 4.3.6 — LICENSE
MIT License
Copyright (c) 2024-2026 Steven Levithan
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
========================================================================
@shikijs/engine-javascript 3.23.0 — LICENSE
MIT License
Copyright (c) 2021 Pine Wu
Copyright (c) 2023 Anthony Fu <https://github.com/antfu>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
========================================================================
@pierre/theme 1.0.3 — LICENSE
MIT License
Copyright (c) 2026 The Pierre Computer Company
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
========================================================================
@shikijs/transformers 3.23.0 — LICENSE
MIT License
Copyright (c) 2021 Pine Wu
Copyright (c) 2023 Anthony Fu <https://github.com/antfu>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
========================================================================
diff 8.0.3 — LICENSE
BSD 3-Clause License
Copyright (c) 2009-2015, Kevin Decker <kpdecker@gmail.com>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
========================================================================
lru_map 0.4.1 — exact license section in README.md
# MIT license
Copyright (c) 2010-2016 Rasmus Andersson <https://rsms.me/>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
========================================================================
clsx 2.1.1 — license
MIT License
Copyright (c) Luke Edwards <luke.edwards05@gmail.com> (lukeed.com)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
========================================================================
tailwind-merge 3.3.1 — LICENSE.md
MIT License
Copyright (c) 2021 Dany Castillo
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
========================================================================
@base-ui/utils 0.2.8 — LICENSE
The MIT License (MIT)
Copyright (c) 2019 Material-UI SAS
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
========================================================================
@base-ui/react 1.4.1 — LICENSE
The MIT License (MIT)
Copyright (c) 2019 Material-UI SAS
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
========================================================================
class-variance-authority 0.7.1 — LICENSE
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
Copyright 2022 Joe Bell
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
========================================================================
@hugeicons/core-free-icons 4.2.0 — MIT notice from the official repository at ed8816e7f9f918df3c31c6abe2d5143d758851bd; repository package version publisher-wide free-icons notice
MIT License
Copyright (c) 2025 Hugeicons
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
========================================================================
@hugeicons/react 1.1.6 — LICENSE.md
MIT License
Copyright (c) 2025 Hugeicons
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
========================================================================
@floating-ui/utils 0.2.12 — LICENSE
MIT License
Copyright (c) 2021-present Floating UI contributors
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
========================================================================
use-sync-external-store 1.7.0 — LICENSE
MIT License
Copyright (c) Meta Platforms, Inc. and affiliates.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
========================================================================
@floating-ui/core 1.8.0 — LICENSE
MIT License
Copyright (c) 2021-present Floating UI contributors
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
========================================================================
@floating-ui/dom 1.8.0 — LICENSE
MIT License
Copyright (c) 2021-present Floating UI contributors
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
========================================================================
@floating-ui/react-dom 2.1.9 — LICENSE
MIT License
Copyright (c) 2021-present Floating UI contributors
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
========================================================================
reselect 5.3.0 — LICENSE
The MIT License (MIT)
Copyright (c) 2015-2018 Reselect Contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
========================================================================
scheduler 0.27.0 — LICENSE
MIT License
Copyright (c) Meta Platforms, Inc. and affiliates.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
========================================================================
tailwindcss 4.1.13 — LICENSE
MIT License
Copyright (c) Tailwind Labs, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
========================================================================
tw-animate-css 1.3.8 — LICENSE
MIT License
Copyright (c) 2025 Wombosvideo
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
provenance.json실행 안내·자료
{
"id": "21st-8be0498b064f",
"sourceRevision": "a0ddcecd7bb3c92458b148cb0d34cbd5263191d4",
"demoIdentity": {
"file": "baseline-example.tsx",
"export": "default",
"source": "exact-public-demo"
},
"fidelity": {
"preserved": "21st 공개 discovery의 고유 데모 ID와 미리보기 이미지가 가리키는 원래 데모 바이트 전체",
"dependency_revision": "a0ddcecd7bb3c92458b148cb0d34cbd5263191d4",
"observed_differences": [
"현재 작성자 커밋의 실제 HumanReviewPanel을 원래 680px 예제 wrapper에 연결한다. PDF viewer나 다른 block을 임의 추가하지 않는다."
],
"mapping": []
},
"acquisitionLimitations": [
"CDN 데모는 SHA256으로, 작성자 원본과 의존 파일은 Git 커밋 및 blob SHA로 고정했다.",
"현재 작성자 revision의 의존 파일이 과거 21st 내부 구현과 바이트 단위로 같다는 주장은 하지 않는다.",
"다운로드한 코드를 실행하지 않았으며 브라우저·상호작용 검증은 별도 단계다."
],
"files": [
{
"file": "baseline-example.tsx",
"kind": "implementation",
"sha256": "e1676438fcd022195ad2acfbfbb3fa591055ebe1ab6ac9be4825ee17e7836bfc",
"sourceRevision": "sha256:e1676438fcd022195ad2acfbfbb3fa591055ebe1ab6ac9be4825ee17e7836bfc",
"license": "MIT"
},
{
"file": "LICENSE",
"kind": "license",
"sha256": "9f05510549c7c2ad283764dfe0e527278e5fb075fb750a9f1dd148f3763269e5",
"sourceRevision": "a0ddcecd7bb3c92458b148cb0d34cbd5263191d4",
"license": "MIT"
},
{
"file": "author/apps/v4/components/extend/bounding-box-citations.tsx",
"kind": "implementation-dependency",
"sha256": "ad7ded7f8d5e149efc687c6839fc0262261acd0932d5cb21210811adaf82a057",
"sourceRevision": "a0ddcecd7bb3c92458b148cb0d34cbd5263191d4",
"license": "MIT"
},
{
"file": "author/apps/v4/lib/registry-icon-props.ts",
"kind": "implementation-dependency",
"sha256": "a331e72bbe23dbd4bac6827dff2a2867d31638fa74daf69ea4c1f41873a686d6",
"sourceRevision": "a0ddcecd7bb3c92458b148cb0d34cbd5263191d4",
"license": "MIT"
},
{
"file": "author/apps/v4/lib/utils.ts",
"kind": "implementation-dependency",
"sha256": "b80bd7ef9714af80100bd9a5c56f6d415cdc06af9083cd5b218bc6f92d622596",
"sourceRevision": "a0ddcecd7bb3c92458b148cb0d34cbd5263191d4",
"license": "MIT"
},
{
"file": "author/apps/v4/components/ui/button.tsx",
"kind": "implementation-dependency",
"sha256": "cfa1ad200f7f0e7757c34b8df42726298330a206ed7a2f3a7c16162ec96227a8",
"sourceRevision": "a0ddcecd7bb3c92458b148cb0d34cbd5263191d4",
"license": "MIT"
},
{
"file": "author/apps/v4/components/ui/input.tsx",
"kind": "implementation-dependency",
"sha256": "1b1d59d2781557568968573d758c226bb385e36d262394a138315e909113ab6f",
"sourceRevision": "a0ddcecd7bb3c92458b148cb0d34cbd5263191d4",
"license": "MIT"
},
{
"file": "author/apps/v4/components/ui/scroll-area.tsx",
"kind": "implementation-dependency",
"sha256": "4113dd6f63b70ea3681aa5e6837773b7ab1a74bb73fea2a7ace380b503e48187",
"sourceRevision": "a0ddcecd7bb3c92458b148cb0d34cbd5263191d4",
"license": "MIT"
},
{
"file": "author/apps/v4/components/ui/tabs.tsx",
"kind": "implementation-dependency",
"sha256": "fe4002f88952cd2b87e77b452ffa0d801d1418acdfb871268c2fcacf8b6be23f",
"sourceRevision": "a0ddcecd7bb3c92458b148cb0d34cbd5263191d4",
"license": "MIT"
},
{
"file": "author/apps/v4/components/ui/tooltip.tsx",
"kind": "implementation-dependency",
"sha256": "45f74cdca29c400714c6e03e99ca5754fa2fc56557153404d1fbba77c6def0d7",
"sourceRevision": "a0ddcecd7bb3c92458b148cb0d34cbd5263191d4",
"license": "MIT"
},
{
"file": "author/apps/v4/components/icon-placeholder.tsx",
"kind": "implementation-dependency",
"sha256": "4592673a15f005b5682264a2232b06009220f159edbae7e047fc10900d5ec070",
"sourceRevision": "a0ddcecd7bb3c92458b148cb0d34cbd5263191d4",
"license": "MIT"
},
{
"file": "author/apps/v4/lib/config.ts",
"kind": "implementation-dependency",
"sha256": "8ae8af0993afa15d423e7032dc4c6e4f492549dc1caf9590e40e8521edf80dab",
"sourceRevision": "a0ddcecd7bb3c92458b148cb0d34cbd5263191d4",
"license": "MIT"
},
{
"file": "author/apps/v4/components/ui/spinner.tsx",
"kind": "implementation-dependency",
"sha256": "eef80891884720b054d549515ff541f3e36884a6da430c5df7aa4397391f4790",
"sourceRevision": "a0ddcecd7bb3c92458b148cb0d34cbd5263191d4",
"license": "MIT"
},
{
"file": "author/apps/v4/registry/new-york-v4/ui/input.tsx",
"kind": "implementation-dependency",
"sha256": "bc51fa452f920b7e19c396fe4d8ea5bb53670d93d9026a206542027e3e4f9f9d",
"sourceRevision": "a0ddcecd7bb3c92458b148cb0d34cbd5263191d4",
"license": "MIT"
},
{
"file": "author/apps/v4/registry/new-york-v4/ui/tabs.tsx",
"kind": "implementation-dependency",
"sha256": "a32b095106026b3e105fb5276cf73872900cc1933e61fa5852d722b509083352",
"sourceRevision": "a0ddcecd7bb3c92458b148cb0d34cbd5263191d4",
"license": "MIT"
},
{
"file": "author/apps/v4/registry/new-york-v4/ui/tooltip.tsx",
"kind": "implementation-dependency",
"sha256": "b7c98aab7b3162e551b29d0f82c329e311ae4a8d9590956e4ba0d15d95d429f8",
"sourceRevision": "a0ddcecd7bb3c92458b148cb0d34cbd5263191d4",
"license": "MIT"
},
{
"file": "author/apps/v4/registry/new-york-v4/ui/spinner.tsx",
"kind": "implementation-dependency",
"sha256": "e4794a099d77b3f220e0998b6777036175e186e238fd95600697d0fd89052795",
"sourceRevision": "a0ddcecd7bb3c92458b148cb0d34cbd5263191d4",
"license": "MIT"
},
{
"file": "author/apps/v4/app/globals.css",
"kind": "theme-source",
"sha256": "f929d2c1de31312f6fc53bd9a7fff9f6a3feff46d4d5fd65805fea2deddbc759",
"sourceRevision": "a0ddcecd7bb3c92458b148cb0d34cbd5263191d4",
"license": "MIT"
},
{
"file": "author/apps/v4/app/shadcn-tailwind.css",
"kind": "theme-source",
"sha256": "146941ac3ff65496fdf1cb306e697255328e4dadc7c105d66e36bb031b00f6d6",
"sourceRevision": "a0ddcecd7bb3c92458b148cb0d34cbd5263191d4",
"license": "MIT"
},
{
"file": "author/apps/v4/package.json",
"kind": "dependency-manifest",
"sha256": "867c41d3eaf150a51c8294e75f7be09561f54723c6584665641203fdc21f3b1c",
"sourceRevision": "a0ddcecd7bb3c92458b148cb0d34cbd5263191d4",
"license": "MIT"
}
],
"importMap": {
"@/components/ui/bounding-box-citations": "author/apps/v4/components/extend/bounding-box-citations.tsx",
"@/lib/registry-icon-props": "author/apps/v4/lib/registry-icon-props.ts",
"@/lib/utils": "author/apps/v4/lib/utils.ts",
"@/components/ui/button": "author/apps/v4/components/ui/button.tsx",
"@/components/ui/input": "author/apps/v4/components/ui/input.tsx",
"@/components/ui/scroll-area": "author/apps/v4/components/ui/scroll-area.tsx",
"@/components/ui/tabs": "author/apps/v4/components/ui/tabs.tsx",
"@/components/ui/tooltip": "author/apps/v4/components/ui/tooltip.tsx",
"@/components/icon-placeholder": "author/apps/v4/components/icon-placeholder.tsx",
"@/lib/config": "author/apps/v4/lib/config.ts",
"@/components/ui/spinner": "author/apps/v4/components/ui/spinner.tsx",
"@/registry/new-york-v4/ui/input": "author/apps/v4/registry/new-york-v4/ui/input.tsx",
"@/registry/new-york-v4/ui/tabs": "author/apps/v4/registry/new-york-v4/ui/tabs.tsx",
"@/registry/new-york-v4/ui/tooltip": "author/apps/v4/registry/new-york-v4/ui/tooltip.tsx",
"@/registry/new-york-v4/ui/spinner": "author/apps/v4/registry/new-york-v4/ui/spinner.tsx"
},
"declaredDependencies": {
"@base-ui/react": "^1.4.1",
"@glideapps/glide-data-grid": "6.0.4-alpha24",
"@hugeicons/core-free-icons": "^4.2.0",
"@hugeicons/react": "^1.1.6",
"@pierre/diffs": "^1.2.7",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"react": "19.2.3",
"react-dom": "19.2.3",
"tailwind-merge": "^3.3.1"
},
"runtimeDependencies": {
"react": "19.2.3",
"lodash": "4.17.21",
"@glideapps/glide-data-grid": "6.0.4-alpha24",
"@emotion/memoize": "0.9.0",
"@emotion/is-prop-valid": "1.4.0",
"@linaria/core": "6.3.0",
"@linaria/react": "6.3.0",
"prop-types": "15.8.1",
"react-easy-swipe": "0.0.21",
"classnames": "2.5.1",
"react-responsive-carousel": "3.2.23",
"marked": "16.4.2",
"canvas-hypertxt": "1.0.3",
"react-dom": "19.2.3",
"react-number-format": "5.4.5",
"@pierre/diffs": "1.2.7",
"html-void-elements": "3.0.0",
"property-information": "7.2.0",
"zwitch": "2.0.4",
"stringify-entities": "4.0.4",
"character-entities-legacy": "3.0.0",
"character-entities-html4": "2.1.0",
"hast-util-to-html": "9.0.5",
"ccount": "2.0.1",
"comma-separated-tokens": "2.0.3",
"space-separated-tokens": "2.0.2",
"hast-util-whitespace": "3.0.0",
"@shikijs/types": "3.23.0",
"@shikijs/vscode-textmate": "10.0.2",
"@shikijs/core": "3.23.0",
"@shikijs/langs": "3.23.0",
"shiki": "3.23.0",
"@shikijs/themes": "3.23.0",
"@shikijs/engine-oniguruma": "3.23.0",
"oniguruma-parser": "0.12.2",
"regex": "6.1.0",
"regex-utilities": "2.3.0",
"regex-recursion": "6.0.2",
"oniguruma-to-es": "4.3.6",
"@shikijs/engine-javascript": "3.23.0",
"@pierre/theme": "1.0.3",
"@shikijs/transformers": "3.23.0",
"diff": "8.0.3",
"lru_map": "0.4.1",
"clsx": "2.1.1",
"tailwind-merge": "3.3.1",
"@base-ui/utils": "0.2.8",
"@base-ui/react": "1.4.1",
"class-variance-authority": "0.7.1",
"@hugeicons/core-free-icons": "4.2.0",
"@hugeicons/react": "1.1.6",
"@floating-ui/utils": "0.2.12",
"use-sync-external-store": "1.7.0",
"@floating-ui/core": "1.8.0",
"@floating-ui/dom": "1.8.0",
"@floating-ui/react-dom": "2.1.9",
"reselect": "5.3.0",
"scheduler": "0.27.0",
"tailwindcss": "4.1.13",
"tw-animate-css": "1.3.8"
},
"assets": [],
"assetAdaptations": [],
"adaptations": [
"The exact public baseline renders HumanReviewPanel with !h-[680px]. It does not instantiate a PDF viewer or load a document.",
"The pinned author review fields, original editors, default Form tab, and JSON comparison are retained. The host supplies an isolated local runtime and selected original theme CSS.",
"Documentation-only theme imports are omitted; the original component implementation and local import closure are unchanged."
],
"runtime_verified": false
}
README.md실행 안내·자료
# Human Review Panel · Extend UI
The acquired original files are unchanged. Source revision: a0ddcecd7bb3c92458b148cb0d34cbd5263191d4. Each exact file hash and any demo content revision is recorded in provenance.json. Keep all included license notices.
- The exact public baseline renders HumanReviewPanel with !h-[680px]. It does not instantiate a PDF viewer or load a document.
- The pinned author review fields, original editors, default Form tab, and JSON comparison are retained. The host supplies an isolated local runtime and selected original theme CSS.
- Documentation-only theme imports are omitted; the original component implementation and local import closure are unchanged.
- Host spacing and fallback tokens come from runtime/author-styles.css. runtime/applied-theme.css contains the selected original author theme and overrides fallback tokens.
Open the standalone HTML to run with all JavaScript and CSS bundled locally. ?theme=light and ?theme=dark select the host theme. The parent gallery must use an opaque allow-scripts sandbox. No external requests are required.
For source reuse, start with Usage.tsx and the unchanged baseline demonstration. Resolve @/ imports to their included matching author files and install the exact package versions in provenance.json. The host mount and styles are supplied for reference.
Build success and the ready message do not prove runtime or accessibility behavior. Browser validation is recorded separately.
BEHIND THE EXAMPLE
이 장면을 만드는 원리
원래 추출 값과 수정할 검토 값
편집 값과 되돌릴 기준을 구분하고 저장 사실과 동일시하지 않습니다.
- 이 예제에서는
- HumanReviewPanel은 fields.actual로 원래 값을 만들고 fields.expected로 편집 상태를 초기화합니다. 변경은 expected 객체만 갱신하고 Undo는 해당 field.expected로 되돌립니다. 첫 필드의 Jan 1-31, 2026과 January 1-31, 2026 차이도 원래 예시 데이터입니다. 이 패널은 서버 저장을 호출하지 않으며 props의 기준이 바뀌면 현재 편집 상태를 다시 초기화합니다.
코드와 함께 확인하기
코드에서 찾기
valuesFromFields(fields, "actual")bounding-box-citations.tsx비교할 추출 값을 따로 만듭니다.
onUndo={() => updateValue(field.key, field.expected)}bounding-box-citations.tsx되돌리기의 기준은 전달된 expected 원본입니다.
setExpected(initialExpectedValues)bounding-box-citations.tsx새 기준이 들어오면 로컬 편집 값을 교체합니다.
직접 해보기
Statement period의 검토 값을 바꾼 뒤 Undo를 누릅니다.
살펴볼 변화원래 추출 값은 그대로 있고 검토 값만 초기 기준으로 돌아가야 합니다. Undo를 서버의 저장 취소로 설명하지 않습니다.
소비 코드에서 편집 중 fields의 expected를 새 객체로 바꿉니다.
살펴볼 변화입력한 값이 새 기준으로 바뀌는 원본 정책을 확인하고, 실제 협업 편집에 필요한 충돌·보존 정책을 별도로 정해야 합니다.
Form과 JSON이 같은 검토 값을 읽기
같은 데이터의 서로 다른 표현은 하나의 상태를 공유해야 합니다.
- 이 예제에서는
- 초기 activeTab은 form이며 두 탭 내용에는 keepMounted를 전달합니다. Form에서 바뀐 expected를 JSON의 split diff가 actualValues와 비교합니다. 숫자 입력을 비우면 null, 다른 숫자는 Number로 변환하고 boolean은 True·False 버튼으로 바꿉니다. 타입 변환은 완전한 스키마 검증이나 저장 승인이 아닙니다.
코드와 함께 확인하기
코드에서 찾기
React.useState("form")bounding-box-citations.tsx처음 보이는 표현은 Form입니다.
expected={expected}bounding-box-citations.tsxJSON 비교도 같은 편집 상태를 받습니다.
onChange(nextValue === "" ? null : Number(nextValue))bounding-box-citations.tsx숫자 입력의 빈 값은 0과 구분합니다.
직접 해보기
검토 값을 수정하고 JSON 탭으로 갔다가 Form으로 돌아옵니다.
살펴볼 변화두 표현의 값과 변경 표시가 일치하고 탭 전환으로 편집 값이 초기화되지 않아야 합니다.
숫자 필드를 비우고 0을 입력한 경우를 비교합니다.
살펴볼 변화JSON에서 null과 0을 구분해야 하며 문자열 enum·정수 범위 등 추가 검증은 실제 요구에 따라 별도 확인해야 합니다.
문서 위치 자료와 실제 문서 뷰어의 경계
제공된 시각적 예제가 실제로 수행하는 사용자 작업을 명확하게 설명합니다.
- 이 예제에서는
- 21st의 원래 데모는 HumanReviewPanel 하나를 !h-[680px]로 렌더링합니다. 소스에는 인용 좌표와 HumanReviewHighlight도 있지만 이 구성은 PDF나 문서 이미지를 렌더링하지 않고 onLocationHover도 연결하지 않습니다. 따라서 필드를 살피는 검토 패널의 예제이며 문서의 실제 위치를 추적하는 완성 화면은 아닙니다.
코드와 함께 확인하기
코드에서 찾기
<HumanReviewPanel className="!h-[680px]" />baseline-example.tsx원래 실행 구성은 검토 패널 하나입니다.
onLocationHover?: (location?: ReviewLocation) => voidbounding-box-citations.tsx문서 위치와의 연결은 선택적 소비자 콜백입니다.
직접 해보기
640×420 프레임에서 패널을 스크롤하고 Form·JSON을 전환합니다.
살펴볼 변화상단 탭과 내용의 스크롤 책임을 확인합니다. PDF가 표시되지 않는 것을 숨기지 말고 연결해야 할 별도 소비자 작업으로 설명합니다.
