README.md / README.zh-CN.md / README.ja-JP.md
tinky-link provides a terminal-safe Link component for Tinky applications. It
renders OSC 8 hyperlinks when the terminal supports them, and uses safe fallback
behavior otherwise.
Link is a focused terminal UI component designed to make terminal hyperlinks
usable without scattering protocol-specific formatting logic across your app code.
It also exposes small utility APIs for policy checks and formatting.
- OSC 8 hyperlink rendering for terminals that support it.
- Built-in href validation and normalization.
- Explicit unsafe fallback control: render plain text or throw.
- Accessibility handling for screen readers.
- Theme hooks through
tinky-themewith safety-state-aware style slots. - Small, typed utility exports for integrations.
Install dependencies (use your actual package name if different):
npm install tinky-link tinky tinky-theme reactimport { render } from "tinky";
import { Link } from "tinky-link";
function App() {
return <Link href="https://example.com/docs">Open docs</Link>;
}
render(<App />);| Prop | Type | Default | Description |
|---|---|---|---|
href |
string | URL |
required | Link destination. |
children |
ReactNode |
normalized href | Visible text. Uses normalized href if omitted. |
variant |
"primary" | "muted" | "danger" |
"primary" |
Visual variant passed to theme. |
disabled |
boolean |
false |
Disables hyperlink output and renders plain text. |
showHref |
boolean |
false |
Appends (<href>) in unsafe or non-hyperlink mode. |
unsafeHrefBehavior |
"render-text" | "throw" |
"render-text" |
Fallback policy for unsafe href input. |
wrap |
TextProps["wrap"] |
"wrap" |
Forwarded to Tinky Text wrapping behavior. |
aria-label |
string |
undefined |
Accessibility label. |
aria-hidden |
boolean |
undefined |
Hide from screen readers when true. |
The entrypoint currently exports:
LinkandLinkPropsuseHyperlinkSupportvalidateHref,normalizeHref,HrefValidationReason,HrefValidationResult,LinkHrefOSC8_PREFIX,wrapWithOsc8- Theme exports:
LINK_COMPONENT_NAME,defaultLinkTheme,LinkComponentTheme,LinkTheme,LinkThemeProps,LinkVariant
Link validates href input before wrapping with OSC 8.
Validation is implemented by validateHref(rawHref) with this pipeline:
- Normalize input by trimming (
stringorURLto string) - Reject empty values (
reason: "empty") - Reject OSC terminators (
BEL,ESC\\,\u009C, reason:"contains-osc-terminator") - Reject control characters (
C0andC1, reason:"contains-control-char")
Fallback behavior:
unsafeHrefBehavior="render-text"(default): render plain text, optional suffix.unsafeHrefBehavior="throw": throwUnsafe href rejected (<reason>).- If terminal/feature does not support OSC 8 or
disabledis true: render plain text.
useHyperlinkSupport() determines OSC 8 availability:
- Read
FORCE_HYPERLINKfrom app env first."1"=> force enable"0"=> force disable
- Read terminal name via
useTermcap(). Enable for known names such askitty,wezterm,iterm,xterm,alacritty,ghostty,contour,foot,vscode,tabby,hyper,mintty. - Fallback to environment indicators:
WT_SESSIONITERM_SESSION_IDTERM_PROGRAMvalues:iTerm.app,WezTerm,vscode,Hyper,minttyVTE_VERSION >= 5000
- Default is
false.
The link component uses this flag and screen reader mode to decide whether to emit OSC 8 sequences.
The component reads styles from tinky-theme under the component key
LINK_COMPONENT_NAME ("Link").
Theme slot props include:
variantdisabledshowHrefisUnsafeisHyperlinkEnabled
Available style slots: container, label, href.
import { ThemeProvider } from "tinky-theme";
import { Link, defaultLinkTheme } from "tinky-link";
const appTheme = {
components: {
Link: {
...defaultLinkTheme,
styles: {
...defaultLinkTheme.styles,
label: (props) => ({
...defaultLinkTheme.styles.label(props),
color: props.isUnsafe ? "yellow" : "green",
bold: props.isHyperlinkEnabled,
}),
},
},
},
};
function App() {
return (
<ThemeProvider theme={appTheme}>
<Link href="https://example.com">Custom theme link</Link>
</ThemeProvider>
);
}Scripts defined in this repository:
npm run testnpm run lintnpm run docsnpm run buildnpm run demo(runsdemo/index.mjs)
This package is released under the MIT License. See LICENSE.