/** * Owned DS primitives, wearing the design-system classes from * `components.css`. These cover the shared UI states every feature drives from * TanStack Query: loading skeletons, a shared empty state, and a loud error * surface. Plugins consume these — never raw Tailwind utilities. * * `Button` is ONE implementation for both an action and a link. Given an `href` * it renders an anchor, and that href is checked against an http/https * allow-list: a reference beginning `/`, `?`, `#`, `./` or `../` stays in-app, an * absolute `http://`/`https://` URL opens in a new tab with * `rel="noopener noreferrer external"`, and everything else — another scheme * (`javascript:`, `data:`, …), a protocol-relative `//host`, a bare `page.html` — * is NEUTRALIZED, rendered as plain text with no href, so a hostile URL can never * become a live navigation target. This is an XSS pin. The neutralized text names * BOTH admitted forms, because an in-app reference is not an http(s) URL either * and saying only "not an http(s) URL" would describe `/tools` as blocked. * * The check reads the NORMALIZED reference and the anchor is given that same * normalized string, because the raw input and the URL the browser resolves are * not the same URL. See `normalizeHref`. */ import type { AnchorHTMLAttributes, ButtonHTMLAttributes, CSSProperties, ReactNode, Ref } from 'react'; /** * The absolute http(s) URL `href` denotes, or `undefined` when it denotes none. * * The `//` is required rather than inferred from a successful `new URL()`, * because for a scheme that matches the document's own, the authority-less * spelling is a PATH: an anchor with `href="https:/settings"` on an https page * navigates to `/settings` on the CURRENT origin, while `new URL()` with no base * reads the same string as the host `settings`. Only the `//` form means the same * thing to both. * * An absolute http(s) URL carrying `user:pass@` userinfo is rejected too: `.href` * re-serialization preserves the userinfo (unlike host tricks), so * `https://trusted.com@evil.com` would render as trusted.com yet navigate to * evil.com. * * Callers render the returned string rather than their input, so the URL that was * judged is always the URL that is navigated. */ export declare function safeHttpUrl(href: string): string | undefined; /** True only for an absolute `http://`/`https://` URL. Everything else is unsafe. */ export declare function isSafeHttpUrl(url: string): boolean; /** * A blocked href, rendered as inert text: no anchor, no `href`, no handlers, so * it can never become a live navigation target. Shared by `Button`'s link form * and by `ExternalLinkButton`, which applies the stricter http(s)-only policy. * * Its surface is DELIBERATELY narrow. It takes the paint, the id and the name, * and nothing else the caller wrote on the anchor: the props a live link needs * are the props a dead one must not have, so they are refused here rather than * spread onto the span. * * It stays ROLE-LESS on purpose — it is not a link and must not be announced as * one. That is also why the caller's name arrives as visually-hidden TEXT rather * than `aria-label`: ARIA prohibits both naming attributes on the `generic` role * a bare `` maps to, so a platform that honours the prohibition computes NO * name from them and an icon-only blocked link announces as nothing at all. Text * has no such restriction — a generic element contributes no accessible NAME, but * its content is still read. `aria-disabled` stays because * `.tai-btn[aria-disabled='true']` is what paints the disabled look; it is inert * on a role-less element, and the missing `href` is what actually says this does * not navigate. * * A caller-supplied `label` REPLACES the children for assistive tech, exactly as * the `aria-label` it comes from would have done on the live anchor: the children * go behind an `aria-hidden` wrapper that is `display: contents`, so it generates * no box and the button's own flex layout is unchanged. Without that the children * and the label are both read and a blocked link announces its name twice. */ export declare function NeutralizedLink({ className, style, children, id, label, }: { readonly className?: string; readonly style?: CSSProperties; readonly children?: ReactNode; /** Kept so an external `aria-labelledby`/`aria-describedby` IDREF still lands. */ readonly id?: string; /** * The name the caller gave the link, rendered as hidden text. An icon-only * link carries its whole meaning here, and when it is given the children are * hidden from assistive tech so the name is read once, not twice. */ readonly label?: string; }): import("react").JSX.Element; /** * What every primitive in this file accepts alongside its own props: the * design-system class it wears is its own, and the caller's `className` is * APPENDED to it rather than replacing it, so a surface can never lose its paint * by being positioned. */ interface SurfaceProps { readonly className?: string; readonly style?: CSSProperties; } export type ButtonVariant = 'primary' | 'secondary' | 'ghost' | 'danger'; /** The variant a `Button` wears when the caller names none. */ export declare const DEFAULT_BUTTON_VARIANT: ButtonVariant; /** * The one prop both forms share. Deliberately NOT on the public surface: it is a * shared base, and declaration emit keeps it visible inside `primitives.d.ts` * where `ButtonProps`/`LinkButtonProps` reference it, so consumers still see * `variant` and can still `extends` either interface. */ interface ButtonVariantProps { readonly variant?: ButtonVariant; } /** * The ACTION form of `Button`: button attributes, no anchor ones. * * This stays an INTERFACE extending `ButtonHTMLAttributes` * because it is published plugin API: a plugin may write * `interface MyButton extends ButtonProps {}` (a union cannot be extended — * TS2312) and may hand a `ButtonProps` value to a slot typed as plain * `ButtonHTMLAttributes` (a union including anchor attributes * is not assignable — TS2322). The link form is the ADDITIVE `LinkButtonProps`, * and `Button` accepts either. * * It declares NO `href` member. `href?: undefined` here would make * `interface MyButton extends ButtonProps { href: string }` a TS2430 * ("incorrectly extends"), which is a narrowing of a surface that is additive * only. The discriminant `Button` narrows on lives on `ButtonActionProps`. */ export interface ButtonProps extends ButtonVariantProps, ButtonHTMLAttributes { /** A consumer ref for the `