import React from 'react'; type HtmlTagName = keyof HTMLElementTagNameMap; /** * tailwind-styled-v4 — Core Types * * CRITIQUE-20 #1 fix: CvFn dan TwComponentFactory sekarang punya proper generic types * bukan `any`. Type safety sudah berlaku di public API. */ type VariantLiterals = string | number | boolean; /** Sizes sugar syntax — shorthand untuk variants.size */ type SizesConfig = Record; /** * Shared literal-narrowing logic dipakai baik oleh `InferVariantProps` (top-level * component variants) maupun `InferSubVariantsFromConfig` (sub-component variants) — * supaya kedua tempat itu tidak duplikasi & selalu konsisten. */ type InferVariantPropsFromVariantsMap = { [K in keyof V as string extends K ? never : number extends K ? never : K]?: V[K] extends Record ? Key extends "true" | "false" ? boolean : Key extends number ? Key : Key extends `${infer N extends number}` ? N : Key : never; }; type InferVariantProps = InferVariantPropsFromVariantsMap; /** * Infer allowed types for defaultVariants based on variant keys. * Supports: * - String keys: { variant: "primary" } * - Number keys: { priority: 0 } * - Boolean keys: { disabled: false } (translates true/false to "true"/"false" keys) * * This helper ensures type safety when defaultVariants contains non-string values. */ type InferDefaultVariantsType = InferVariantPropsFromVariantsMap; type InferSizeProps = T["sizes"] extends Record ? { size?: keyof T["sizes"]; } : Record; /** * Infer boolean props dari states config. * Setiap key di states → optional boolean prop di component. * * @example * // states: { loading: "...", fullWidth: "..." } * // → { loading?: boolean, fullWidth?: boolean } */ type InferStatesProps = { [K in keyof T["states"] as string extends K ? never : number extends K ? never : K]?: boolean; }; /** * Sub-component config bisa berupa: * - string: "font-bold text-lg" → render dengan classes * - Record: nested HTML tag dengan component names dan classes * - Record: nested HTML tag dengan component names dan full variant config * * @example * sub: { * icon: "w-4 h-4", // → dipanggil Card.icon * header: { topBar: "bg-gray-900" }, // →
dipanggil Card.topBar * h2: { title: "text-xl font-bold" }, // →

dipanggil Card.title * section: { content: "px-6 py-4" }, // →
dipanggil Card.content * canvas: { base: "p-6", variants: { layout: { wrap: "gap-4", column: "gap-0" } } } // →
dengan variants * } */ interface SubComponentConfig { base?: string; variants?: Record>; defaultVariants?: Record; compoundVariants?: Array<{ class: string; [key: string]: string; }>; } type SubValue = string | SubComponentConfig | Record; /** * Boolean props yang di-resolve via bitmask lookup table (pre-generated di build time). * Berbeda dari `state` (CSS data-attribute driven) — ini adalah React props boolean. * * @example * states: { * loading: "opacity-60 cursor-wait pointer-events-none", * fullWidth: "w-full", * disabled: "opacity-50 cursor-not-allowed", * } * * // JSX — boolean prop langsung: * */ type StatesConfig = Record; interface ComponentConfig { base?: string; /** Variants — nested: { intent: { primary: "..." }, size: { sm: "..." } } * Supports string and boolean keys: { disabled: { true: "...", false: "..." } } */ variants?: Record>; /** defaultVariants accepts string | number | boolean to match all possible variant key types */ defaultVariants?: Record; compoundVariants?: Array<{ class: string; [key: string]: string; }>; state?: Record>; container?: Record; containerName?: string; /** Sugar syntax untuk variants.size — { sm: "...", md: "...", lg: "..." } */ sizes?: SizesConfig; /** defaultSize — default ke key pertama kalau tidak diset */ defaultSize?: string; /** * Boolean props — di-resolve via Rust bitmask lookup table di build time. * Maksimal 16 states per komponen (2^16 kombinasi). */ states?: StatesConfig; /** Sub-component definitions — string atau nested { tag: { name: classes } } */ sub?: Record; /** * Pre-computed hash — di-inject oleh turbopackLoader via `inject_state_hash()` (Rust). * Kalau ada, `stateEngine.ts` skip runtime `hashState()` computation sepenuhnya. * Format: 6-char FNV-1a hex string, identik dengan output `hashState()`. * @internal — jangan set manual, ini di-inject otomatis saat build/dev. */ __hash?: string; /** * Wave 3: Semantic component type untuk auto-generated ARIA metadata. * Digunakan oleh build-time ARIA injection plugin untuk determine semantic role. * * Common values: 'button', 'link', 'checkbox', 'radio', 'input', 'form', 'dialog', * 'navigation', 'heading', 'alert', 'tab', 'section', 'status', 'aside' * * @example * '@semantic': 'button' // → auto-inject role="button" * '@semantic': 'dialog' // → auto-inject role="dialog", aria-modal="true" */ '@semantic'?: string; /** * Wave 3: Explicit ARIA attributes untuk semantic component. * Merged dengan auto-injected ARIA dari semantic type. * User-provided ARIA memiliki precedence lebih tinggi (tidak di-override). * * @example * '@aria': { role: 'tab', 'aria-selected': 'true' } */ '@aria'?: Record; /** * Wave 3: State → ARIA property mapping untuk semantic metadata. * Determines mana variant/state yang map ke ARIA properties. * * @example * '@state': { expanded: 'aria-expanded', disabled: 'aria-disabled' } * → When expanded prop true → aria-expanded="true" auto-injected */ '@state'?: Record; } /** * Strip tag prefix dari "tag:name" format sub key. * "div:action" → "action" * "header" → "header" (tidak berubah) */ type ExtractSubName = K extends `${string}:${infer Name}` ? Name : K; /** * True kalau S[K] adalah SubComponentConfig LANGSUNG (satu sub-component dengan * `base`/`variants` sendiri, mis. `canvas: { base: "...", variants: {...} }`) — * bukan nested named-group (mis. `header: { topBar: "...", nav: "..." }`). * * HARUS identik dengan discriminant runtime di createComponent.ts: * `"base" in value || "variants" in value`. Kedua bentuk sama-sama tampak sebagai * `Record` secara struktural, jadi tidak bisa dibedakan cuma lewat * `extends Record` — perlu cek keberadaan key `base`/`variants` * secara eksplisit seperti runtime-nya. */ type IsDirectSubConfig = V extends string ? false : "variants" extends keyof V ? true : "base" extends keyof V ? true : false; /** * Infer semua sub-component names dari config.sub: * - string value plain → key langsung: { icon: "..." } → "icon" * - string value tag:name → strip tag: { "div:action": "..." } → "action" * - direct SubComponentConfig → key langsung (support "tag:name" juga): { canvas: {base, variants} } → "canvas" * - nested named-group → nested keys: { h2: { title: "..." } } → "title" */ type InferSubFromConfig = C extends { sub: infer S extends Record; } ? { [K in keyof S]: S[K] extends string ? K extends string ? ExtractSubName : never : IsDirectSubConfig extends true ? K extends string ? ExtractSubName : never : S[K] extends Record ? N : never; }[keyof S] : never; /** * HTML tags yang otomatis dikenali sebagai tag dari bare sub-key (tanpa "tag:name" syntax). * HARUS identik dengan SEMANTIC_HTML_TAGS di createComponent.ts (parseSubKey) — kalau * drift, sub-component bisa di-tipe-kan dengan native attributes yang salah (tag yang * benar2 di-render di runtime beda dari yang dipakai untuk inference type-nya). */ type SemanticSubTag = "article" | "aside" | "details" | "figcaption" | "figure" | "footer" | "header" | "main" | "mark" | "nav" | "section" | "summary" | "time" | "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "p" | "ul" | "ol" | "li" | "dl" | "dt" | "dd" | "table" | "thead" | "tbody" | "tfoot" | "tr" | "th" | "td" | "form" | "fieldset" | "legend" | "label" | "a" | "button" | "img" | "span" | "div" | "blockquote" | "pre" | "code" | "em" | "strong" | "small"; /** * Resolve tag HTML dari satu sub-key string — mirror logic `parseSubKey()` runtime * di createComponent.ts: * - "a:link" → tag eksplisit sebelum colon ("a") * - "header" → bare key, fallback ke key itu sendiri kalau termasuk SemanticSubTag * - "icon" → bare key, bukan semantic tag → fallback "span" (default runtime) */ type ResolveSubTag = K extends `${infer Tag}:${string}` ? Tag extends HtmlTagName ? Tag : "span" : K extends SemanticSubTag ? K : "span"; /** Gabungkan union of object types jadi satu intersection — dipakai untuk merge per-key tag map. */ type UnionToIntersection = (U extends unknown ? (k: U) => void : never) extends (k: infer I) => void ? I : never; /** * Infer mapping { subComponentName: htmlTag } dari config.sub — dipakai supaya setiap * sub-component (Card.icon, Breadcrumb.link, dst) punya native HTML attributes yang * SESUAI tag yang benar2 di-render (href untuk , src untuk , dst), bukan cuma * `{ children?, className? }` generik. * * @example * sub: { "a:link": "...", "span:sep": "..." } → { link: "a"; sep: "span" } * sub: { header: { topBar: "..." } } → { topBar: "header" } */ type InferSubTagsFromConfig = C extends { sub: infer S extends Record; } ? UnionToIntersection<{ [K in keyof S]: K extends string ? S[K] extends string ? { [N in ExtractSubName]: ResolveSubTag; } : IsDirectSubConfig extends true ? { [N in ExtractSubName]: ResolveSubTag; } : S[K] extends Record ? { [N in keyof S[K]]: K extends HtmlTagName ? K : "span"; } : never : never; }[keyof S]> : Record; /** * Infer mapping { subComponentName: variantProps } dari config.sub — dipakai supaya * sub-component yang punya `variants` sendiri (mis. `canvas: { base, variants: { layout: {...} } }`) * ikut ke-expose propnya (`layout`) di accessor-nya (`PlaygroundWrap.canvas`), bukan cuma * native HTML attributes dari tag-nya. * * @example * sub: { canvas: { variants: { layout: { wrap: "...", column: "..." } } } } * → { canvas: { layout?: "wrap" | "column" } } */ type InferSubVariantsFromConfig = C extends { sub: infer S extends Record; } ? UnionToIntersection<{ [K in keyof S]: K extends string ? IsDirectSubConfig extends true ? S[K] extends { variants?: infer V; } ? { [N in ExtractSubName]: InferVariantPropsFromVariantsMap; } : Record : Record : Record; }[keyof S]> : Record; interface ContainerConfig { base?: string; queries?: Record; defaultQuery?: string; } interface StateConfig { base?: string; states?: Record>; defaultStates?: Record; } type CvFn = (props?: { [K in keyof C["variants"] as string extends K ? never : number extends K ? never : K]?: keyof C["variants"][K]; } & { class?: string; className?: string; }) => string; interface StyledComponentProps { className?: string; as?: HtmlTagName; children?: React.ReactNode; } type SubComponentMap = Record; type TwSubComponentAccessor = Record> = React.FC, "ref" | keyof ExtraProps> & ExtraProps & { children?: React.ReactNode; className?: string; }>; type TrimLeft = S extends ` ${infer R}` | ` ${infer R}` | ` ${infer R}` | ` ${infer R}` ? TrimLeft : S; type TrimRight = S extends `${infer L} ` | `${infer L} ` | `${infer L} ` | `${infer L} ` ? TrimRight : S; type Trim = TrimLeft>; type ExtractSubNames = T extends `${string}[${infer Name}]${string}{${string}}${infer Rest}` ? Trim | ExtractSubNames : T extends `${string}\n${infer Name}{${string}}${infer Rest}` ? (Trim extends "" ? never : Trim) | ExtractSubNames : never; type SubComponentKeys = Record, SubVariantsMap extends Record> = Record> = { [K in S]: TwSubComponentAccessor>; }; type TwStyledComponent = Record, Tag extends HtmlTagName = HtmlTagName, SubVariantsMap extends Record> = Record> = { (props: Omit, keyof InferVariantProps | keyof InferSizeProps | keyof InferStatesProps> & StyledComponentProps & InferVariantProps & InferSizeProps & InferStatesProps): React.ReactElement | null; displayName?: string; extend: { (strings: TemplateStringsArray, ...exprs: unknown[]): TwStyledComponent; (config: EC & { defaultVariants?: InferDefaultVariantsType<{ variants: EC["variants"]; }>; }): TwStyledComponent; }; withVariants: (config: Partial> & { defaultVariants?: InferDefaultVariantsType; }) => TwStyledComponent; /** * Declare sub-component names secara eksplisit untuk autocomplete + type safety. * * @example * export const Button = tw.button` * flex h-12 ... * icon { flex h-4 } * `.withSub<"icon" | "footer">() * * Button.icon // ✅ autocomplete * Button.footer // ✅ autocomplete * Button.xyz // ❌ TypeScript error */ withSub(): TwStyledComponent; } & SubComponentKeys; interface TwSubComponent

{ (props: P): React.ReactElement | null; displayName?: string; } interface TwTemplateFactory { (config: C & { defaultVariants?: InferDefaultVariantsType; }): TwStyledComponent, InferSubTagsFromConfig extends Record ? InferSubTagsFromConfig : Record, Tag, InferSubVariantsFromConfig extends Record> ? InferSubVariantsFromConfig : Record>; (strings: readonly [T], ...exprs: []): TwStyledComponent, Record, Tag>; (strings: TemplateStringsArray, ...exprs: unknown[]): TwStyledComponent, Tag>; } type TwTagFactory = { [K in HtmlTagName]: TwTemplateFactory; }; type TwTagFactoryAny = { [key: string]: TwTemplateFactory; }; type TwComponentFactory = (tag: T) => TwTemplateFactory; type TwServerObject = TwTagFactory & { [K in HtmlTagName as `${K}`]: TwTemplateFactory; }; type TwObject = TwComponentFactory & TwTagFactory & { server: TwServerObject; }; /** * tailwind-styled-v4 v2 — tw * * API: * tw.div`p-4 bg-zinc-900` * tw.button`px-4 [icon] { h-4 w-4 }` ← sub-components inline * tw.button({ base: "px-4", variants: { size: { sm: "text-sm" } } }) * tw(Link)`underline text-blue-400` * tw.server.div`p-4` ← server-only, compiler enforced + runtime dev warning */ declare const server: TwServerObject; declare const tw: TwObject; /** * tailwind-styled-v4 — cv() * * Runtime: native-first with fallback to generated variant tables. * * Dua mode: * 1. GENERATED (optimal) — import dari variants.generated.ts hasil `npx tw compile-variants` * → O(1) lookup, static, zero runtime computation * 2. RUNTIME (fallback) — compute on-the-fly via native binding * → Requires native Rust binding for variant resolution */ /** * Register pre-computed variant table dari generated file. * Dipanggil otomatis saat import variants.generated.ts */ declare function registerVariantTable(componentId: string, table: Record): void; declare function cv(config: C, componentId?: string): CvFn; /** * tailwind-styled-v4 v2 — cx / cn * * Native Rust binding dipakai di Node.js/SSR untuk performa maksimal. * * Browser fallback (Bug 2 — "Native binding 'resolveClassNames' is required * but not available." crash di browser): * * getNativeBinding() return `null` di browser (bukan throw). Ketika itu terjadi * kita gunakan pure-TS fallback yang semantiknya identik dengan implementasi Rust: * - cn(): filter falsy + join spasi (identik dengan resolve_class_names di Rust) * - cx(): conflict-aware merge via twMergeRawJs (port dari tw_merge.rs) * - cxn(): flatten nested array + filter falsy + join spasi * * Server path TIDAK DIUBAH: throw Error tetap dijalankan jika native null di * Node.js (menandakan miskonfigurasi). Fallback hanya aktif saat isBrowser=true. */ type ClassValue = string | undefined | null | false | 0; /** * cn — simple class name joiner (no conflict resolution). * Node.js: delegates ke Rust `resolve_class_names`. * Browser: pure-TS fallback — filter falsy + join spasi. * * @example cn("p-4", isActive && "opacity-100") → "p-4 opacity-100" */ declare function cn(...inputs: (ClassValue | ClassValue[])[]): string; /** * cx — conflict-aware class merger. * Node.js: delegates ke Rust `tw_merge_many`. * Browser: pure-TS fallback via twMergeRawJs (port algoritma Rust). * * @example cx("p-4 p-8") → "p-8" * @example cx("bg-red-500", "bg-blue-500") → "bg-blue-500" * @example cx(["flex", "items-center"], "px-4") → "flex items-center px-4" */ declare function cx(...inputs: (ClassValue | ClassValue[])[]): string; /** @deprecated Use cx() instead. */ declare const cxm: typeof cx; declare function createComponent(tag: React.ElementType, config: TConfig | string): TwStyledComponent>; interface ThemeConfig$1 { colors: Record; spacing: Record; fonts: Record; breakpoints: Record; animations: Record; raw: Record; } /** * tailwind-styled-v4 — createTwMerge() * * Native Rust binding (twMergeRaw) is used when available (Node.js, build * time, SSR) for maximum performance/parity with the rest of the pipeline. * * Browser fallback (Bug C — "Uncaught Error: Native binding 'twMergeRaw' is * required but not available." crashing in the browser): * * createComponent.ts calls twMerge() unconditionally inside the React render * function for EVERY tw.* component, on EVERY render — including the very * first client-side render of any component that runs/re-renders in the * browser. A `.node` native addon can never load inside a browser JS engine, * so this isn't a rare edge case, it's unconditional: any tw.* component * rendering client-side needs a working fallback path. * * getNativeBinding() returns `null` (not a throw) when running in the * browser — see native.ts. When that happens we use mergeFallback.ts, a * hand-written pure-TS port of the SAME algorithm as `tw_merge_raw` in * tw_merge.rs (conflict_group / split_variants / merge_class_string), * instead of the third-party `tailwind-merge` package. This keeps merge * behavior defined by one algorithm — ours — rather than risking the * browser and the server silently disagreeing on merged className output * because two different implementations drifted apart. The cost: when * tw_merge.rs's conflict_group() changes, mergeFallback.ts must be updated * by hand to match (see comments there). */ interface MergeOptions { /** * NOTE: not currently applied — matches the native path, which calls * `native.twMergeRaw(inputs)` without ever forwarding a prefix either. * Kept on the type for API stability; wire it through conflictGroup() * in mergeFallback.ts (and tw_merge_raw on the Rust side) together if a * custom Tailwind prefix needs to be supported correctly end-to-end. */ prefix?: string; separator?: string; theme?: ThemeConfig$1; } declare function createTwMerge(options?: MergeOptions): (...classLists: Array) => string; declare const twMerge: (...classLists: Array) => string; declare function mergeWithRules(rules: Record string>, ...classLists: string[]): string; /** * tailwind-styled-v4 — Reactive State Engine * * Zero-JS CSS state management via data attributes. * No React re-render needed for style changes. * * How it works: * 1. tw.button({ state: { active: "bg-blue-500", loading: "opacity-70" } }) * 2. State engine generates a unique class + injects CSS: * .tw-s-abc123[data-active="true"] { @apply bg-blue-500; } * .tw-s-abc123[data-loading="true"] { @apply opacity-70; } * 3. Component renders with the state class * 4. User sets data-active="true" directly — no state needed * * Devtools integration: * All components register to __TW_STATE_REGISTRY__ for devtools inspection. */ interface StateComponentEntry { id: string; tag: string; states: string[]; cssInjected: boolean; } declare const stateRegistry: Map; declare global { interface Window { __TW_STATE_REGISTRY__?: typeof stateRegistry; } } interface StateEngineResult { /** CSS class to add to the component */ stateClass: string; /** Whether this component uses state (for SSR data attributes) */ hasState: true; /** List of state names (for devtools) */ stateNames: string[]; } /** * Process a StateConfig for a component. * Returns the state class and injects CSS (client-side only). * * @param tag HTML tag name * @param state State config object * @param precomputedHash Optional pre-computed hash dari `inject_state_hash()` Rust transform. * Kalau ada, skip runtime `hashState()` sepenuhnya → zero hashing overhead. */ declare function processState(tag: string, state: StateConfig, precomputedHash?: string): StateEngineResult; /** * Generate SSR-safe CSS string for a state config. * Used by SSR to inject styles into . */ declare function generateStateCss(tag: string, state: StateConfig): string; /** * Get the state registry (for devtools). */ declare function getStateRegistry(): Map; /** * tailwind-styled-v4 — Container Query Engine * * Generates @container rules from a simple breakpoint config. * * Usage: * const Card = tw.div({ * base: "p-4", * container: { * sm: "flex-col", // @container (min-width: 320px) * md: "flex-row", // @container (min-width: 640px) * lg: "grid-cols-3", // @container (min-width: 1024px) * }, * containerName: "card", * }) * * // Wrap with container context: * const CardWrapper = tw.div`@container` * * Named containers: * const SidebarCard = tw.div({ * base: "p-2", * container: { lg: "text-sm" }, * containerName: "sidebar", * }) * // Generates: @container sidebar (min-width: 1024px) { ... } */ interface ContainerEntry { id: string; tag: string; containerName?: string; breakpoints: Array<{ minWidth: string; classes: string; }>; cssInjected: boolean; } declare const containerRegistry: Map; declare global { interface Window { __TW_CONTAINER_REGISTRY__?: typeof containerRegistry; } } interface ContainerQueryResult { containerClass: string; hasContainer: true; } declare function processContainer(tag: string, container: ContainerConfig, containerName?: string): ContainerQueryResult; /** * Generate @container CSS rules dari breakpoint config. * * Native-only: Rust string building tanpa intermediate allocations. */ declare function generateContainerCss(tag: string, container: ContainerConfig, containerName?: string): string; declare function getContainerRegistry(): Map; /** * tailwind-styled-v4 — createStyledSystem() * * Design System Factory — Mode 3 API. * * Level 1 — utility: tw.div`p-4` * Level 2 — styled: tw(Button)`px-4` * Level 3 — design system: ui.button({ variant: "primary" }) * * Usage: * const ui = createStyledSystem({ * tokens: { * colors: { primary: "#6366f1", muted: "#71717a" }, * radius: { base: "0.5rem", full: "9999px" }, * }, * components: { * button: { * base: "inline-flex items-center font-medium transition-colors", * variants: { * variant: { * primary: "bg-[var(--sys-color-primary)] text-white", * ghost: "bg-transparent border border-current", * danger: "bg-red-500 text-white", * }, * size: { * sm: "h-8 px-3 text-sm", * md: "h-10 px-4 text-base", * lg: "h-12 px-6 text-lg", * }, * }, * defaultVariants: { variant: "primary", size: "md" }, * }, * }, * }) * * const Button = ui.button() * // →