import { SafeHtml } from './jsx-runtime.js'; import { M as MountResult } from './mount-Bo2qOx25.js'; import '@preact/signals-core'; import './bindings-CYwoJpQb.js'; /** * Anchored positioning primitives for `kerfjs/overlay` — position an element * relative to an anchor, with viewport flip + clamp. These are standalone: they * take any element and have no overlay lifecycle, so `popover()` / `tooltip()` * build on them but you can also position your own element (an inline hint, a * custom menu). Re-exported from `src/overlay.ts` so the public `kerfjs/overlay` * surface is unchanged (KF-511 split out of overlay.ts's dialog + toast code). */ /** Vertical placement relative to an anchor (used by `popover`, {@link positionAnchored}, `tooltip`). */ type PopoverPlacement = 'bottom' | 'top'; /** Placement options for {@link positionAnchored} / {@link autoReposition}. */ interface AnchorPositionOptions { /** Preferred side of the anchor; flips to the other side if it would overflow the viewport. Default `'bottom'`. */ placement?: PopoverPlacement; /** Horizontal edge to line up with the anchor: `'start'` (left edges) or `'end'` (right edges). Default `'start'`. */ align?: 'start' | 'end'; /** Gap in px between the anchor and the element. Default `4`. */ gap?: number; } /** * One-shot: position `el` relative to `anchor` — below by default, flipping above * if it would overflow the viewport, aligned to a horizontal edge and clamped into * view. Sets `el.style` `position: fixed`, `margin: 0`, `left`, and `top` (fixed so * `left`/`top` are viewport coordinates, matching `getBoundingClientRect`). This is * `popover()`'s placement core, usable on any element (an inline hint, a tooltip) — * no overlay lifecycle. Pair with {@link autoReposition} to keep it glued while open. */ declare function positionAnchored(el: HTMLElement, anchor: Element, options?: AnchorPositionOptions): void; /** * Keep `el` positioned against `anchor` (via {@link positionAnchored}) as the page * scrolls or resizes. Positions once immediately, then re-runs on `scroll` * (capture phase — catches scrolls in any inner container, not just `window`) and * `resize`. Returns a disposer that removes the listeners. */ declare function autoReposition(el: HTMLElement, anchor: Element, options?: AnchorPositionOptions): () => void; /** * Wiring slots passed to a {@link ConfirmOptions.render} — spread `ok` / `cancel` * onto your own clickable elements so `confirm()` still resolves them (they are * `data-confirm` attribute bags). `message` is the raw message (escape it by * interpolating through JSX). */ interface ConfirmRenderSlots { message: string; /** Spread onto the confirm control. */ ok: Record; /** Spread onto the cancel control. */ cancel: Record; } /** Options for {@link confirm}. */ interface ConfirmOptions { /** Where to append the overlay. Default `document.body`. */ container?: Element; /** Wrapper class. Default `'kerf-overlay'`. */ className?: string; /** Optional heading above the message. */ title?: string; /** Confirm button label. Default `'OK'`. */ okText?: string; /** Cancel button label. Default `'Cancel'`. */ cancelText?: string; /** Add a `kerf-confirm--danger` class to the wrapper for destructive actions. */ danger?: boolean; /** Host the dialog in the browser top layer (`.showModal()`) where supported. See {@link OverlayOptions.native}. */ native?: boolean; /** * Bring your own markup (design-system dialogs): return the full dialog body, * spreading the provided `ok`/`cancel` wiring onto your buttons. Overrides the * default two-button markup; `confirm()` keeps owning dismiss / focus-trap / * focus-restore and still resolves `true`/`false` for OK/Cancel/dismissal. */ render?: (slots: ConfirmRenderSlots) => OverlayContent; } /** * A promise-based `window.confirm` replacement (that global is a no-op in Tauri * webviews). Renders a two-button dialog and resolves `true` for OK, `false` * for Cancel or any dismissal (Escape / backdrop). Message + labels are * auto-escaped (rendered through the JSX runtime). Pass `render` for your own markup. */ declare function confirm(message: string, options?: ConfirmOptions): Promise; /** * Validate a single field's value. Return a non-empty error string to BLOCK * submission (shown inline next to the field); return `undefined`/`null`/`''` to * allow it. */ type FieldValidator = (value: string) => string | null | undefined | void; /** Options for {@link prompt}. */ interface PromptOptions { /** Where to append the overlay. Default `document.body`. */ container?: Element; /** Wrapper class. Default `'kerf-overlay'`. */ className?: string; /** Optional heading above the message. */ title?: string; /** Pre-filled input value. Default `''`. */ defaultValue?: string; /** Input placeholder. */ placeholder?: string; /** `type` attribute of the input (`'text'`, `'email'`, `'password'`, …). Default `'text'`. */ inputType?: string; /** Confirm button label. Default `'OK'`. */ okText?: string; /** Cancel button label. Default `'Cancel'`. */ cancelText?: string; /** Block OK while this returns an error string; the message shows inline. */ validate?: FieldValidator; /** Host the dialog in the browser top layer (`.showModal()`) where supported. See {@link OverlayOptions.native}. */ native?: boolean; /** * Bring your own markup: return the full dialog body, spreading the provided * `input` (the text field), `ok`/`cancel` (buttons), and optional `error` (the * inline-error slot) wiring. `prompt()` still reads the input, runs `validate`, * submits on Enter, and owns dismiss / focus. If you omit the `error` slot, * `validate` simply re-focuses the input without an inline message. */ render?: (slots: PromptRenderSlots) => OverlayContent; } /** Wiring slots for a {@link PromptOptions.render} — spread each onto your own markup. */ interface PromptRenderSlots { message: string; /** Spread onto your `` — carries the marker, `type`, `value`, and `placeholder`. */ input: Record; /** Spread onto your inline-error element (optional). */ error: Record; /** Spread onto the confirm control. */ ok: Record; /** Spread onto the cancel control. */ cancel: Record; } /** * A promise-based `window.prompt` replacement (that global is a no-op in Tauri * webviews). Renders a one-field dialog and resolves the entered **string** on OK * (an empty string is a valid result) or `null` on Cancel / dismissal. Enter in * the input submits. `message`, the default value, and labels are auto-escaped * (rendered through the JSX runtime). Optional `validate` blocks OK inline. Pass * `render` for your own markup. */ declare function prompt(message: string, options?: PromptOptions): Promise; /** A single field in a {@link form}. */ interface FormField { /** Field name — the key in the resolved record (and the input's `name`). */ name: string; /** Label shown above the input. Defaults to `name`. */ label?: string; /** Pre-filled value. Default `''`. */ defaultValue?: string; /** Input placeholder. */ placeholder?: string; /** `type` attribute of the input. Default `'text'`. */ type?: string; /** Block OK while this returns an error string; the message shows inline for this field. */ validate?: FieldValidator; } /** One field's wiring in a {@link FormRenderSlots} — spread `input`/`error` onto your markup. */ interface FormRenderField { name: string; label: string; /** Spread onto your `` — carries the marker, `name`, `type`, `value`, `placeholder`. */ input: Record; /** Spread onto your inline-error element (optional). */ error: Record; } /** Wiring slots for a {@link FormOptions.render}. */ interface FormRenderSlots { fields: FormRenderField[]; /** Spread onto the confirm control. */ ok: Record; /** Spread onto the cancel control. */ cancel: Record; } /** Options for {@link form}. */ interface FormOptions { /** Where to append the overlay. Default `document.body`. */ container?: Element; /** Wrapper class. Default `'kerf-overlay'`. */ className?: string; /** Optional heading above the fields. */ title?: string; /** Confirm button label. Default `'OK'`. */ okText?: string; /** Cancel button label. Default `'Cancel'`. */ cancelText?: string; /** Host the dialog in the browser top layer (`.showModal()`) where supported. See {@link OverlayOptions.native}. */ native?: boolean; /** * Bring your own markup: return the full form body, laying out `slots.fields` * (each with `input`/`error` wiring to spread) and the `ok`/`cancel` buttons. * `form()` still reads each input, runs per-field `validate`, focuses the first * invalid field, submits on Enter, and owns dismiss / focus. Omit a field's * `error` slot to skip its inline message. */ render?: (slots: FormRenderSlots) => OverlayContent; } /** * A promise-based multi-field dialog — the two-or-three-input sibling of * {@link prompt}. Renders one labeled input per {@link FormField} and resolves a * `Record` on OK (after every field's `validate` passes) or `null` * on Cancel / dismissal. Enter in any field submits. All labels, defaults, and * the title are auto-escaped through the JSX runtime. */ declare function form(fields: readonly FormField[], options?: FormOptions): Promise | null>; /** One choosable action in a {@link choice} dialog. */ interface ChoiceAction { /** The value this action resolves. */ value: R; /** Button label (auto-escaped). */ label: string; /** Extra class on this action's button. */ className?: string; } /** Wiring slots for a {@link ChoiceOptions.render} — spread `actions[i]` onto your i-th button. */ interface ChoiceRenderSlots { message: string; /** One attribute bag per action (in order) — spread onto that action's control. */ actions: Array>; } /** Options for {@link choice}. */ interface ChoiceOptions { /** Where to append the overlay. Default `document.body`. */ container?: Element; /** Wrapper class. Default `'kerf-overlay'`. */ className?: string; /** Optional heading above the message. */ title?: string; /** The value resolved when **Enter** is pressed anywhere in the dialog (the default action). */ defaultValue?: R; /** Host the dialog in the browser top layer (`.showModal()`) where supported. See {@link OverlayOptions.native}. */ native?: boolean; /** Bring your own markup: return the full body, spreading each `slots.actions[i]` onto your buttons. */ render?: (slots: ChoiceRenderSlots) => OverlayContent; } /** * The **N-way** sibling of {@link confirm}: renders one button per {@link ChoiceAction} * and resolves that action's `value` on click, or `null` on Cancel / dismissal. * Pass `defaultValue` to make **Enter** (anywhere in the dialog) resolve a default * action — the "global Enter-to-confirm" model — without you having to hold the * overlay handle. `message` + labels are auto-escaped; pass `render` for your own * markup. kerf owns dismiss / focus-trap / focus-restore. For fully bespoke * keyboard/close control, drive {@link overlay} directly. */ declare function choice(message: string, actions: ReadonlyArray>, options?: ChoiceOptions): Promise; /** * `toast()` for `kerfjs/overlay` — a non-modal, auto-dismissing notification that * stacks in a shared body-level region. Split out of `overlay.ts` (KF-513) since * it's a distinct transient-UI concern from the modal dialogs; re-exported from * `overlay.ts` so the public `kerfjs/overlay` surface is unchanged. Structural * only — kerf ships no CSS; you style the region / toast / animations. */ /** Content for a {@link toast}: text, `SafeHtml`, or a render function. */ type ToastContent = string | SafeHtml | (() => MountResult); /** Accent variant for a {@link toast} — mapped to a `${className}--${variant}` class. */ type ToastVariant = 'info' | 'success' | 'warning'; /** Options for {@link toast}. */ interface ToastOptions { /** Where toasts stack. Default: a lazily-created `
` on `document.body`. */ container?: Element; /** Class on the toast element. Default `'kerf-toast'`. */ className?: string; /** Auto-dismiss after this many ms. `0` keeps it until dismissed by hand. Default `4000`. */ duration?: number; /** ARIA role. Default `'status'`. */ role?: string; /** * `'stack'` (default) shows toasts stacked in the region; `'replace'` dismisses * the region's current toast(s) first (collapse-to-latest for a rapid sequence). */ mode?: 'stack' | 'replace'; /** * How `mode: 'replace'` drops the prior toast(s): `'fade'` (default) runs their * full exit transition (nice for a STACKING region), or `'instant'` removes them * synchronously with no exit — what a single, exactly-centered toast slot wants, * so the outgoing and incoming messages never cross-fade in the same spot. */ collapse?: 'fade' | 'instant'; /** Accent variant — adds a `${className}--${variant}` class (kerf ships no CSS; you style it). */ variant?: ToastVariant; /** Class added on the next animation frame after mount, so a CSS **entrance** transition can run. */ enterClass?: string; /** * Class added when dismissing, so CSS owns the **exit**. On dismiss the * `enterClass` (if any) is also REMOVED, so `exitClass` doesn't have to * out-specify it — and a symmetric single-class fade (entrance = add * `enterClass`, exit = remove it) works by setting only `enterClass` + * `exitDuration`. The node is removed `exitDuration` ms later. */ exitClass?: string; /** ms to wait before removing the node on dismiss — applies when `exitClass` is set OR when it's > 0 (to let a removed `enterClass` transition out). Default `0`. */ exitDuration?: number; } /** Handle returned by {@link toast}. */ interface ToastHandle { /** The toast element — inspect it, or run your own entrance/exit transitions. */ el: HTMLElement; /** * Dismiss it early. Default runs the `exitClass` transition (removed after * `exitDuration`); pass `{ instant: true }` to remove it **synchronously** with * no exit — for an action button that immediately shows a replacement toast in a * single centered slot (no cross-fade). Idempotent. */ dismiss(options?: { instant?: boolean; }): void; } /** * Show a non-modal, auto-dismissing notification. Stacks in a shared body-level * region (or your `container`). Returns a {@link ToastHandle} (`{ el, dismiss }`) * so you can run entrance/exit transitions, wire an action button, or inspect the * node. `mode: 'replace'` collapses a rapid sequence to the latest; `variant` * adds an accent class; `enterClass`/`exitClass` let CSS own the animation. */ declare function toast(content: ToastContent, options?: ToastOptions): ToastHandle; /** * `kerfjs/overlay` — the modal / overlay + dismiss manager. * * Every real kerf app hand-rolls this: `toElement → body.appendChild → mount → * wire dismissal → remove`, plus the fiddly parts (Escape, backdrop / outside * click, focus trap, restoring focus on close). `window.confirm` is a no-op in * Tauri WKWebViews, so a hand-built overlay is mandatory there. This subpath * blesses the pattern as three functions over `mount()` — `overlay()`, and the * `confirm()` / `toast()` conveniences built on it. No per-instance framework * state: each call owns its DOM + listeners in a closure and returns a handle. * * import { overlay, confirm, toast } from 'kerfjs/overlay'; * * const ok = await confirm('Delete this file?', { danger: true }); * toast('Saved'); * const dialog = overlay(, { dismiss: ['escape', 'backdrop'] }); * // …later: dialog.close(); or await dialog.result; * * Structural only — kerf ships no CSS. The wrapper gets your `className`; style * the backdrop / centering / animation yourself. */ /** A user-initiated dismissal trigger. */ type DismissTrigger = 'escape' | 'backdrop' | 'outside'; /** Content for an overlay: static `SafeHtml`, or a render function `mount()` drives reactively. */ type OverlayContent = SafeHtml | (() => MountResult); /** Options for {@link overlay}. */ interface OverlayOptions { /** Where to append the overlay wrapper. Default `document.body`. */ container?: Element; /** Class on the wrapper element (you style it — kerf ships no CSS). Default `'kerf-overlay'`. */ className?: string; /** * Which user actions dismiss the overlay. Default `['escape', 'backdrop']`. * `'backdrop'` = a click on the wrapper itself (not its content); `'outside'` * = a click anywhere outside the wrapper (for anchored popovers). `false` * disables user dismissal (close it programmatically). */ dismiss?: DismissTrigger | DismissTrigger[] | false; /** * Where focus lands on open: a selector, `true` (first focusable element, or * the wrapper if none), or `false` (leave focus alone). Default `true`. */ initialFocus?: string | boolean; /** * Trap Tab / Shift+Tab within the overlay while open and mark it * `role="dialog"` / `aria-modal="true"`. Default `true`. Set `false` for a * non-modal popover. */ trap?: boolean; /** ARIA role for the wrapper when `trap` is on. Default `'dialog'`. */ role?: string; /** Called on any user-initiated dismissal (before `close()` runs). */ onDismiss?: () => void; /** For `'outside'` dismissal: clicks on these elements do NOT count as outside (e.g. the trigger button). */ outsideIgnore?: Element | readonly Element[]; /** * Opt into the browser **top layer** (`docs/19-native-overlay-backing.md`). * When `true` and the engine supports it, a modal overlay (`trap: true`) is * hosted in a `` opened with `.showModal()` — real inerting of the rest * of the document + guaranteed stacking above any `z-index` — and a non-modal * one (`trap: false`) uses the Popover API (`[popover]` + `showPopover()`). * Feature-detected; falls back to today's plain `
` where unsupported. * * The `render` slot + promise API are unchanged — kerf just hosts your markup * in a `` / `[popover]` instead of a `
`. Two caveats: native * `` / `[popover]` carry **UA default styles** (a `::backdrop`, * centering, border, padding) that kerf does not reset — style the element (and * its `::backdrop`) via `className`; and `container` is effectively a **no-op** * for visual position, since the top layer ignores where the element lives in * the DOM. Default `false`. */ native?: boolean; } /** Handle returned by {@link overlay}. Holds no framework state — it's a closure. */ interface OverlayHandle { /** The wrapper element (mounted into, appended to `container`). */ el: HTMLElement; /** Tear down: dispose the mount, remove listeners + the node, restore focus, resolve `result`. Idempotent. */ close(result?: unknown): void; /** Resolves with the value passed to `close()` (or `undefined` on user dismissal). */ result: Promise; } /** * Open an overlay: append a wrapper to `container`, `mount()` `content` inside * it, wire the requested dismissals + (optionally) a focus trap, and return a * handle. See {@link OverlayOptions}. */ declare function overlay(content: OverlayContent, options?: OverlayOptions): OverlayHandle; /** Options for {@link popover}. */ interface PopoverOptions { /** Where to append the popover wrapper. Default `document.body`. */ container?: Element; /** Class on the wrapper. Default `'kerf-popover'`. */ className?: string; /** Preferred side of the anchor. Flips to the other side if it would overflow the viewport. Default `'bottom'`. */ placement?: PopoverPlacement; /** Horizontal edge to line up with the anchor: `'start'` (left edges) or `'end'` (right edges). Default `'start'`. */ align?: 'start' | 'end'; /** Gap in px between the anchor and the popover. Default `4`. */ gap?: number; /** * Which user actions dismiss the popover. Default `['outside']` (a click * outside the popover, the anchor exempt). Pass `false` to close only via `close()`. */ dismiss?: DismissTrigger | DismissTrigger[] | false; /** Focus behavior on open. Default `false` (non-modal — leave focus alone). */ initialFocus?: string | boolean; /** Extra elements (besides the anchor) whose clicks do NOT count as outside. */ outsideIgnore?: Element | readonly Element[]; /** Called on any user-initiated dismissal. */ onDismiss?: () => void; /** * Host the popover in the browser top layer (the Popover API — `[popover]` + * `showPopover()`) where supported, so it stacks above any `z-index` without a * z-index war. Falls back to today's plain `
` where unsupported. kerf keeps * owning positioning + its own dismiss wiring; the popover is `popover="manual"`. * See {@link OverlayOptions.native}. Default `false`. */ native?: boolean; } /** * Anchored, non-modal overlay: positions `content` relative to `anchor` (below by * default, flipping above if it would overflow, and clamped horizontally to the * viewport) and repositions on scroll / resize while open. A thin wrapper over * {@link overlay} with non-modal defaults — `trap: false`, `dismiss: ['outside']`, * and the anchor added to `outsideIgnore` so the trigger click doesn't self-close. * Returns the same {@link OverlayHandle}; `close()` also drops the reposition * listeners. `position: fixed` is set inline (you style everything else). */ declare function popover(anchor: Element, content: OverlayContent, options?: PopoverOptions): OverlayHandle; /** Content for a {@link tooltip}: text (auto-escaped), `SafeHtml`, or a render function. */ type TooltipContent = string | SafeHtml | (() => MountResult); /** Options for {@link tooltip}. */ interface TooltipOptions extends AnchorPositionOptions { /** Where to append the tooltip wrapper. Default `document.body`. */ container?: Element; /** Class on the wrapper. Default `'kerf-tooltip'`. */ className?: string; /** Delay in ms before showing after hover/focus enters. Default `400`. */ delay?: number; /** Delay in ms before hiding after hover/focus leaves. Default `100`. */ hideDelay?: number; /** ARIA role on the wrapper. Default `'tooltip'`. */ role?: string; /** Host the tooltip in the browser top layer (the Popover API) where supported. See {@link OverlayOptions.native}. Default `false`. */ native?: boolean; } /** * A hover/focus-triggered, non-modal, auto-hiding tooltip anchored to `anchor`. * Shows after `delay` on `pointerenter`/`focus`, hides after `hideDelay` on * `pointerleave`/`blur`, and positions itself with {@link autoReposition} (above * the anchor by default). Unlike {@link popover} there is no click-dismiss model — * it follows the pointer/focus. Returns a disposer that removes the anchor * listeners and hides any shown tooltip. Structural only (kerf ships no CSS). */ declare function tooltip(anchor: Element, content: TooltipContent, options?: TooltipOptions): () => void; export { type AnchorPositionOptions, type ChoiceAction, type ChoiceOptions, type ChoiceRenderSlots, type ConfirmOptions, type ConfirmRenderSlots, type DismissTrigger, type FieldValidator, type FormField, type FormOptions, type FormRenderField, type FormRenderSlots, type OverlayContent, type OverlayHandle, type OverlayOptions, type PopoverOptions, type PopoverPlacement, type PromptOptions, type PromptRenderSlots, type ToastContent, type ToastHandle, type ToastOptions, type ToastVariant, type TooltipContent, type TooltipOptions, autoReposition, choice, confirm, form, overlay, popover, positionAnchored, prompt, toast, tooltip };