import { ClassValue } from 'clsx'; export { ClassValue } from 'clsx'; import { K as KunMessages, a as KunMessagePath, b as KunMessageCatalog } from './types-BpQaxyFb.cjs'; export { c as KunKbdKeyName, d as KunShortcutKey, e as KunShortcutPlatform, f as KunTranslate, g as formatKunShortcut, p as parseKunShortcut, r as resolveKunShortcut, s as speakKunShortcut } from './types-BpQaxyFb.cjs'; type KunUIVariant = 'solid' | 'bordered' | 'light' | 'flat' | 'shadow'; type KunUIColor = 'default' | 'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'info'; type KunUISize = 'xs' | 'sm' | 'md' | 'lg' | 'xl'; type KunUIRounded = 'none' | 'sm' | 'md' | 'lg' | 'full'; declare const cn: (...inputs: ClassValue[]) => string; declare const kunVariantClasses: (variant: KunUIVariant, color: KunUIColor) => string; declare const kunBgClasses: Record; /** Just the fill. Pair with {@link kunSolidFgClasses} when the fill and its text * live on different elements. */ declare const kunSolidBgClasses: Record; /** The generated on-color that stays legible on the matching solid fill in both * modes (white on medium hues, a dark hue-tint on the bright ones). */ declare const kunSolidFgClasses: Record; /** Fill + matching foreground — the common case (text sits on the fill). */ declare const kunSolidClasses: Record; declare const kunTextClasses: Record; declare const kunBorderClasses: Record; /** * @deprecated Use {@link kunFocusRingClasses} (or {@link kunFocusRingWithinClasses} * for composite widgets). This mixed `:focus` + `:focus-within` so a mouse click * lit the ring, and used a different opacity from the rest of the library. */ declare const kunRingClasses: Record; /** * Unified focus ring — ONE recipe for every directly-focusable control (inputs, * textarea, select trigger, buttons, checkbox/radio, links, tabs, menu items…). * Keyboard focus only (`:focus-visible`, so a mouse click on a button shows no * ring; text fields still show it on click via the browser's focus-visible * heuristic). A flush 2px ring in the control's semantic color at 50% — replaces * the old scatter of `:focus`/`:focus-within` × `ring-1|2|4` × `/25|/40|/50|full`. * Pair with `border-kun` at rest; on an invalid control use the `danger` entry. */ declare const kunFocusRingClasses: Record; /** * Composite-widget variant of {@link kunFocusRingClasses}: the focusable element * is an inner `` but the ring should sit on the WRAPPER. Put this on the * wrapper and give the inner input `outline-none` (and no ring of its own) so * there's exactly one indicator. Used by KunNumberInput / KunTagInput. * * Uses `focus-within` (not `has-[:focus-visible]`): for a text-field wrapper the * two are equivalent — a text input matches `:focus-visible` on click too — and * `focus-within` is a native variant that reliably compiles, whereas the * `has-[:focus-visible]` arbitrary variant isn't extracted from this TS source. */ declare const kunFocusRingWithinClasses: Record; declare const kunSoftBgClasses: Record; declare const kunRoundedClasses: Record; declare const KUN_DEFAULT_ROUNDED: KunUIRounded; declare const resolveRounded: (prop: KunUIRounded | undefined, fallback: KunUIRounded | undefined, configDefault?: KunUIRounded) => KunUIRounded; declare const kunPanelRoundedClass: (rounded: KunUIRounded) => string; declare const kunControlSizeClasses: Record; declare const kunControlSquareClasses: Record; interface KunSelectionSize { box: string; dot: string; check: string; text: string; gap: string; } declare const kunSelectionSizeClasses: Record; declare const kunChipSizeClasses: Record; /** * The ballistic model behind `KunShatter`, as data — outward impulse, air * drag (`1 − (1−t)^dragExponent`), gravity as a t² acceleration — sampled * into `keyframeSteps` linear keyframes per shard. The same manifest * generates `KunShatterPhysics` in the kun_ui_tokens pub package, so the * web and Flutter renditions of the motion cannot drift. Tune the feel in * the manifest, never inline in a component. */ declare const KUN_SHATTER_PHYSICS: { readonly keyframeSteps: 16; readonly reachFactor: 0.5; readonly launchMin: 0.32; readonly launchSpan: 0.55; readonly verticalFactor: 0.8; readonly gravityMin: 0.85; readonly gravitySpan: 0.5; readonly dragExponent: 1.7; readonly scaleEndMin: 0.8; readonly scaleEndSpan: 0.16; readonly fadeOutStartMin: 0.5; readonly fadeOutStartSpan: 0.18; readonly settleExponent: 2; readonly fadeInWindow: 0.4; readonly staggerFraction: 0.22; readonly staggerCapMs: 120; readonly defaultDurationMs: 1100; readonly defaultRotationDeg: 140; }; /** * Drag-to-dismiss on a bottom sheet, as data: dismiss on release when the * sheet moved down and either the release velocity exceeds `closeVelocity` * (px/ms) or the offset reaches `closeDistanceRatio` of the panel height * (capped at the viewport); an upward drag of d px moves the panel * `rubberBandLimit × (1 − e^(−d / rubberBandLimit))`. The same manifest * generates `KunSwipeDismissPhysics` in kun_ui_tokens. */ declare const KUN_SWIPE_DISMISS_PHYSICS: { readonly closeDistanceRatio: 0.25; readonly closeVelocity: 0.4; readonly rubberBandLimit: 32; }; declare const translateKunMessage: (messages: KunMessages, path: KunMessagePath, params?: Record) => string; declare const KUN_CATALOG_ZH_CN: KunMessageCatalog; /** Drop separators that would render as stray lines: leading, trailing, and * all but the first of a run. An app filters one command table per context * (no "Delete" for a post you do not own) and its separators have to follow; * React Spectrum S2 hides the same three cases. */ declare const normalizeKunMenuSeparators: (entries: readonly T[]) => T[]; interface KunSplitSizeOptions { min: number; max: number; /** Widths the divider is pulled to when it comes within `snapThreshold`. */ snapPoints?: readonly number[]; snapThreshold?: number; /** The width a keyboard step starts from. A snap may not cancel or reverse * that step: with a 5 px step inside an 8 px threshold, the divider would * otherwise never leave a snap point. A drag leaves it unset, so the * divider sticks to a snap point until the pointer pulls clear. */ from?: number; } /** Clamp a requested width into [min, max], pull it to the nearest snap point * within the threshold, and round it to a whole pixel. */ declare const resolveKunSplitSize: (raw: number, { min, max, snapPoints, snapThreshold, from }: KunSplitSizeOptions) => number; declare const randomNum: (lowerValue: number, upperValue: number) => number; declare const decodeIfEncoded: (text: string) => string; /** * Deterministically pick one image from an avatar fallback pool. * * The same `seed` always yields the same entry on server and client, so there * is no hydration mismatch and no framework state is needed. * * `pool` is treated as a FIXED-LENGTH array: the index is * `hash(seed) % pool.length`, so adding or removing an entry re-assigns every * seed, while replacing one entry in place moves only the seeds that land on * it. Pool providers should edit slots rather than resize. * * An empty pool returns the bundled data URI, which cannot fail to load. */ declare const pickAvatarFallback: (seed?: string, pool?: readonly string[]) => string; /** * @deprecated Renamed to `pickAvatarFallback`, which takes the pool the host * configured. This alias no longer returns a sticker URL — it returns the * bundled fallback for any input, because the host it used to point at stopped * serving those files. Pass a pool, or read the resolved image off KunAvatar. */ declare const getRandomSticker: (id?: string) => string; declare const KUN_AVATAR_FALLBACK = "data:image/webp;base64,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"; interface KunUser { /** * `0` means there is no profile to link to — an unknown or deleted author, * a signed-out viewer. KunAvatar and KunUserChip render it without a link. */ id: number; name: string; avatar: string; /** * An avatar frame drawn around the avatar (NextMoe `cosmetics.avatar_frame`). * Absent or null → no frame. */ avatarDecoration?: KunAvatarDecoration | null; } /** * A frame asset on a square canvas 1.2× the avatar, the avatar circle centred * in it. `src` is the still image; `animatedSrc`, when present, is played on * hover or always, depending on KunAvatar's `decoration`. */ interface KunAvatarDecoration { src: string; animatedSrc?: string; } type KunChatEntityType = 'bold' | 'italic' | 'underline' | 'strikethrough' | 'spoiler' | 'code' | 'pre' | 'blockquote' | 'text_link' | 'mention' | 'url'; /** * A formatting range over a message's text. `offset` and `length` count * UTF-16 code units, as JavaScript string indices and Dart strings do, so a * range is `text.slice(offset, offset + length)` on both platforms. */ interface KunChatEntity { type: KunChatEntityType; offset: number; length: number; /** `mention` only: the mentioned user. */ user_id?: string; /** `text_link` only: the link target. */ url?: string; /** `pre` only: the code block's language, e.g. `go`. */ language?: string; } interface KunChatPhoto { type: 'photo'; /** Image-service hash. KunUI never builds a URL from it. */ image_hash: string; width: number; height: number; thumbhash?: string | null; /** Where the image is served, as the server sends it (chat spec 1.1.0). * Shown when no `resolveMediaUrl` is passed. Optional so payloads from * before 1.1.0 still type-check. */ url?: string | null; } /** Media attached to a message. Only `photo` exists today; sticker and file * arrive later, and every consumer switches on `type`. */ type KunChatMedia = KunChatPhoto; /** The message a reply points at, embedded by the server so a reply renders * without fetching its target. */ interface KunChatReplyTo { seq: number; sender_id: string; text: string; entities: KunChatEntity[]; /** The replied-to message's `media.type`, or null. */ media_type: string | null; /** The target was deleted for everyone; render "message deleted". */ deleted: boolean; } /** A reply that quotes only part of its target. `offset` is where the quote * starts in the target's text, in UTF-16 code units. */ interface KunChatReplyQuote { text: string; entities: KunChatEntity[]; offset: number; } type KunChatServiceAction = { type: 'group_created'; title?: string; } | { type: 'members_added'; user_ids: string[]; } | { type: 'member_left'; } | { type: 'member_removed'; user_id: string; } | { type: 'title_changed'; title: string; } | { type: 'photo_changed'; } | { type: 'message_pinned'; seq: number; } | { type: 'joined_by_link'; }; type KunChatServiceActionType = KunChatServiceAction['type']; /** A cross-site context card, e.g. "sent from the moyu page of patch X". */ interface KunChatContext { site: string; kind: string; id: string; title: string; url: string; } interface KunChatReaction { /** A key from the reaction vocabulary, never an emoji character. */ reaction: string; count: number; /** Whether the viewer is one of `count`. */ reacted: boolean; } /** One entry of the reaction vocabulary served by `GET /v2/chat/reactions`. */ interface KunChatReactionOption { key: string; /** Native emoji, shown when there is no `image_url`. */ emoji: string; /** Accessible name, e.g. "爱心". */ label: string; /** Animated image to draw instead of the emoji. */ image_url?: string | null; } /** Client-side delivery state of a message the viewer sent. */ type KunChatSendStatus = 'sending' | 'sent' | 'read' | 'failed'; interface KunChatMessage { object?: 'message'; id: string; conversation_id: string; /** Position in the conversation, from 1, gap-free. */ seq: number; sender_id: string; kind: 'message' | 'service'; text: string; entities: KunChatEntity[]; media: KunChatMedia | null; /** Photos sent together share one value; each photo is its own message. */ media_group_id: string | null; reply_to: KunChatReplyTo | null; reply_quote: KunChatReplyQuote | null; service_action: KunChatServiceAction | null; context: KunChatContext | null; reactions: KunChatReaction[]; silent: boolean; pinned_at: string | null; edited_at: string | null; created_at: string; /** Local only: the idempotency key a pending message was sent with. Kept on * the confirmed message, it keeps the row's identity across confirmation. */ client_message_id?: string | null; /** Local only: the delivery state of the viewer's own message. When absent * it is derived — `read` at or below the peer's read cursor, else `sent`. */ status?: KunChatSendStatus; } /** The result of parsing composer input, and what a send carries. */ interface KunChatFormattedText { text: string; entities: KunChatEntity[]; } /** A member of the `users` array chat responses carry. A deleted account keeps * its id with an empty name and `deleted: true`; the components render it as * "deleted account" with the fallback avatar. Not a `KunUser`: that one's id * is a number, this one's a string. */ interface KunChatUser { object?: 'user'; id: string; name: string; avatar: string; deleted?: boolean; } /** A typing notification as the site received it. `at` is the receiving * client's own clock (`Date.now()`), never the server's, so the 6-second * expiry is immune to clock skew. */ interface KunChatTypingEvent { user_id: string; at: number; } /** Longest message text, in UTF-16 code units (Telegram's value). */ declare const KUN_CHAT_TEXT_LIMIT = 4096; /** Most photos one album can hold. */ declare const KUN_CHAT_ALBUM_LIMIT = 10; /** A client sends at most one typing notification per this many ms. */ declare const KUN_CHAT_TYPING_INTERVAL = 5000; /** A typing notification not renewed within this many ms has expired. */ declare const KUN_CHAT_TYPING_TIMEOUT = 6000; /** Types that break the line around themselves. */ declare const KUN_CHAT_BLOCK_ENTITY_TYPES: ReadonlySet; /** * Make an entity list safe to render, without ever changing `text`: * * 1. Drop unknown types, non-integer or negative positions, empty ranges, a * `text_link` without a `url` and a `mention` without a `user_id`; clamp * the end to the text. Widen a range that would cut a surrogate pair (an * emoji) in half. Pull a blockquote's end in over trailing line breaks. * 2. Merge touching or overlapping runs of one inline format (bold, italic, * underline, strikethrough, spoiler). * 3. Settle the blockquotes among themselves (steps 5 and 6, quotes only), * then cut every other entity at the boundaries of a quote that survived, * so quotes are always outermost. * 4. Pull the ends of an inline format in over line breaks, which draw * nothing there (a blockquote's end, before step 3). * 5. Order by start, then longer first, then by nesting order; split an * entity that partially crosses another at the boundary, so every pair * nests or is disjoint. * 6. Drop what cannot nest: anything inside `code`/`pre`, a type inside the * same type, a link inside a link. * * The result is a fixed point: normalizing it again changes nothing. The * server already guarantees most of this; the client does it anyway so a bad * row renders instead of throwing. */ declare const normalizeKunChatEntities: (text: string, entities: readonly KunChatEntity[] | null | undefined) => KunChatEntity[]; interface KunChatTextLeaf { kind: 'text'; text: string; /** Where `text` starts in the message text, in UTF-16 code units. */ offset: number; } interface KunChatEntityNode { kind: 'entity'; entity: KunChatEntity; children: KunChatTextNode[]; } type KunChatTextNode = KunChatTextLeaf | KunChatEntityNode; /** * Turn `text` + `entities` into a tree: entity nodes whose children are text * leaves and nested entity nodes, in document order. The entities are * normalized first (see `normalizeKunChatEntities`), so any input renders. */ declare const buildKunChatEntityTree: (text: string, entities: readonly KunChatEntity[] | null | undefined) => KunChatTextNode[]; /** The plain text of a subtree, as rendered (block-adjacent breaks omitted). */ declare const kunChatTreeText: (nodes: readonly KunChatTextNode[]) => string; /** * The part of a message from `start` to `end` (UTF-16 code units), with the * entities clipped to it and shifted to start at 0 — what a partial quote * carries. The range is widened rather than split through an emoji. */ declare const sliceKunChatEntities: (text: string, entities: readonly KunChatEntity[] | null | undefined, start: number, end: number) => { text: string; entities: KunChatEntity[]; }; /** * Parse composer input into message text and entities. Never throws: input * that is not valid shortcut syntax stays literal text. */ declare const parseKunChatMarkdown: (source: string) => KunChatFormattedText; /** * Turn message text + entities back into composer input — for editing a sent * message, or restoring a draft. Parsing the result gives back the same text * and (normalized) entities, except for what the syntax cannot spell: * * - `url` entities are dropped; the server detects links again on save. * - A blockquote is dropped unless it starts and ends at line breaks that * are text — not inside a code span or pre block. * - Blockquotes on consecutive lines are written, and come back, as one. * - A `pre` language with characters outside `A-Z a-z 0-9 _ + # . -` is * dropped, as is a `text_link` whose URL is itself `mention:`. */ declare const formatKunChatMarkdown: (text: string, entities: readonly KunChatEntity[] | null | undefined) => string; interface KunChatAlbumSize { width: number; height: number; } /** Which outer edges of the mosaic a tile touches — its rounded corners. */ declare const KUN_CHAT_ALBUM_SIDE: { readonly top: 1; readonly right: 2; readonly bottom: 4; readonly left: 8; }; interface KunChatAlbumTile { x: number; y: number; width: number; height: number; /** Bitmask of `KUN_CHAT_ALBUM_SIDE`. */ sides: number; } interface KunChatAlbumLayout { width: number; height: number; tiles: KunChatAlbumTile[]; } interface KunChatAlbumOptions { /** Default 420, tweb's desktop bubble width. */ maxWidth?: number; /** Narrowest tile. Default 100. */ minWidth?: number; /** Gap between tiles. Default 2. */ spacing?: number; } /** * Lay out an album of 1–10 photos as Telegram does. Sizes with a zero or * missing side are treated as square. */ declare const layoutKunChatAlbum: (sizes: readonly KunChatAlbumSize[], options?: KunChatAlbumOptions) => KunChatAlbumLayout; /** The row identity of a message: its `client_message_id` when it has one, so * a pending message keeps its row when the server confirms it. */ declare const kunChatMessageKey: (message: KunChatMessage) => string; /** * `YYYY-MM-DD` of an instant, in `timeZone` (an IANA name) or, when omitted, * the runtime's own zone. Keys sort chronologically as strings. */ declare const kunChatDayKey: (date: Date, timeZone?: string) => string; type KunChatListRow = { type: 'unread'; key: string; } | { type: 'service'; key: string; message: KunChatMessage; } | { type: 'message'; key: string; /** The message the row acts as: an album's first message with a * caption, else its first; the message itself otherwise. */ message: KunChatMessage; /** Every message the row shows — an album's photos, in order. */ messages: KunChatMessage[]; own: boolean; /** First of consecutive messages from one sender: the name goes here. */ groupStart: boolean; /** Last of the run: the avatar and the bubble tail go here. */ groupEnd: boolean; }; interface KunChatDaySection { /** `YYYY-MM-DD` in the list's time zone. */ key: string; /** The first message's instant, for formatting the date pill. */ date: Date; rows: KunChatListRow[]; } interface KunChatGroupOptions { currentUserId: string; /** The viewer's read cursor when the conversation was opened. The unread * divider goes above the first later message someone else sent. */ lastReadSeq?: number | null; /** Longest gap, in seconds, inside one sender group. Default 600 * (telegram-tt's `GROUP_INTERVAL_SECONDS`). */ groupWindow?: number; /** IANA time zone for day boundaries; the runtime's zone when omitted. */ timeZone?: string; } /** * Arrange messages — already in display order — into day sections and rows. * A sender group breaks on another sender, a new day, a gap longer than * `groupWindow`, the unread divider, and any service message. */ declare const groupKunChatMessages: (messages: readonly KunChatMessage[], options: KunChatGroupOptions) => KunChatDaySection[]; interface KunIconData { body: string; width?: number; height?: number; } declare const registerKunIcon: (name: string, data: KunIconData) => void; declare const registerKunIcons: (icons: Record) => void; declare const getKunIcon: (name: string) => KunIconData | undefined; declare const hasKunIcon: (name: string) => boolean; export { KUN_AVATAR_FALLBACK, KUN_CATALOG_ZH_CN, KUN_CHAT_ALBUM_LIMIT, KUN_CHAT_ALBUM_SIDE, KUN_CHAT_BLOCK_ENTITY_TYPES, KUN_CHAT_TEXT_LIMIT, KUN_CHAT_TYPING_INTERVAL, KUN_CHAT_TYPING_TIMEOUT, KUN_DEFAULT_ROUNDED, KUN_SHATTER_PHYSICS, KUN_SWIPE_DISMISS_PHYSICS, type KunAvatarDecoration, type KunChatAlbumLayout, type KunChatAlbumOptions, type KunChatAlbumSize, type KunChatAlbumTile, type KunChatContext, type KunChatDaySection, type KunChatEntity, type KunChatEntityNode, type KunChatEntityType, type KunChatFormattedText, type KunChatGroupOptions, type KunChatListRow, type KunChatMedia, type KunChatMessage, type KunChatPhoto, type KunChatReaction, type KunChatReactionOption, type KunChatReplyQuote, type KunChatReplyTo, type KunChatSendStatus, type KunChatServiceAction, type KunChatServiceActionType, type KunChatTextLeaf, type KunChatTextNode, type KunChatTypingEvent, type KunChatUser, type KunIconData, KunMessageCatalog, KunMessagePath, KunMessages, type KunSelectionSize, type KunSplitSizeOptions, type KunUIColor, type KunUIRounded, type KunUISize, type KunUIVariant, type KunUser, buildKunChatEntityTree, cn, decodeIfEncoded, formatKunChatMarkdown, getKunIcon, getRandomSticker, groupKunChatMessages, hasKunIcon, kunBgClasses, kunBorderClasses, kunChatDayKey, kunChatMessageKey, kunChatTreeText, kunChipSizeClasses, kunControlSizeClasses, kunControlSquareClasses, kunFocusRingClasses, kunFocusRingWithinClasses, kunPanelRoundedClass, kunRingClasses, kunRoundedClasses, kunSelectionSizeClasses, kunSoftBgClasses, kunSolidBgClasses, kunSolidClasses, kunSolidFgClasses, kunTextClasses, kunVariantClasses, layoutKunChatAlbum, normalizeKunChatEntities, normalizeKunMenuSeparators, parseKunChatMarkdown, pickAvatarFallback, randomNum, registerKunIcon, registerKunIcons, resolveKunSplitSize, resolveRounded, sliceKunChatEntities, translateKunMessage };