components

package
v1.0.48254 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 14, 2026 License: MIT Imports: 20 Imported by: 0

Documentation

Index

Constants

View Source
const (
	// LineChartDefaultWidth is the total chart width when Width is unset, y-axis
	// gutter included. It fits an 80-column terminal.
	LineChartDefaultWidth = 72

	// LineChartDefaultHeight is the total chart height when Height is unset,
	// including the two rows the x axis and its labels take. See [LineChart.YStep]
	// for why the remainder wants to be a power of two.
	LineChartDefaultHeight = 10

	// LineChartDefaultYStep labels every other plot row, which over the eight rows
	// LineChartDefaultHeight leaves puts the ticks on quarters of the ceiling.
	LineChartDefaultYStep = 2

	// LineChartDefaultXTicks is the number of x-axis marks when XTicks is unset.
	// Four leaves a "1m30s"-sized label clear space either side on a default-width
	// chart.
	LineChartDefaultXTicks = 4
)

LineChart layout defaults, applied to the zero value of the matching field.

View Source
const (

	// PreviewFrameWidth is how many columns the frame steals from the box's
	// outer width before content. Callers subtract it from the pane width to get
	// the column count content must wrap to (e.g. glamour word-wrap width).
	PreviewFrameWidth = previewBorderSize + previewPaddingX

	// PreviewFrameHeight is the rows the frame consumes, so callers can size the
	// box to leave room for surrounding chrome.
	PreviewFrameHeight = previewBorderSize // top+bottom border
)

previewFramePadding is the columns/rows lipgloss border + horizontal padding add around the content. The border costs 1 cell on each side (2 cols, 2 rows) and the padding adds 1 col on each side (2 cols).

View Source
const PagerFooterHeight = 2

PagerFooterHeight is the number of rows PagerModel reserves below the viewport for its footer (one blank separator row + the help/status line). Callers use it to decide whether content fits on one screen without paging.

Variables

View Source
var (
	KeyEnter    = key.NewBinding(key.WithKeys("enter"))
	KeyEsc      = key.NewBinding(key.WithKeys("esc"))
	KeyCtrlC    = key.NewBinding(key.WithKeys("ctrl+c"))
	KeyYes      = key.NewBinding(key.WithKeys("y", "Y"))
	KeyNo       = key.NewBinding(key.WithKeys("n", "N"))
	KeyTab      = key.NewBinding(key.WithKeys("tab"))
	KeyShiftTab = key.NewBinding(key.WithKeys("shift+tab"))
	KeySpace    = key.NewBinding(key.WithKeys(" ", "space"))

	// List/viewport movement. k/j accompany the arrows for vim-style navigation.
	KeyUp       = key.NewBinding(key.WithKeys("up", "k"))
	KeyDown     = key.NewBinding(key.WithKeys("down", "j"))
	KeyPageUp   = key.NewBinding(key.WithKeys("pgup"))
	KeyPageDown = key.NewBinding(key.WithKeys("pgdown"))
	KeyTop      = key.NewBinding(key.WithKeys("g", "home"))
	KeyBottom   = key.NewBinding(key.WithKeys("G", "end"))

	// Search "/" prompt editing. History recall is arrow-only (not KeyUp/KeyDown)
	// so k/j are typed into the pattern as literal text instead of recalling.
	KeyBackspace = key.NewBinding(key.WithKeys("backspace"))
	KeyHistPrev  = key.NewBinding(key.WithKeys("up"))
	KeyHistNext  = key.NewBinding(key.WithKeys("down"))

	// Pager committed-search navigation.
	KeySearchNext = key.NewBinding(key.WithKeys("n"))
	KeySearchPrev = key.NewBinding(key.WithKeys("N"))
)

Dispatch bindings. These carry no help text — they never appear in a footer, they only classify a key press. Movement is split per-direction because up and down (etc.) trigger different actions; the grouped display bindings below layer the combined "↑/↓" hint label on top of the same keystrokes.

View Source
var (
	BindSelect  = key.NewBinding(key.WithKeys("enter"), key.WithHelp("enter", "select"))
	BindSearch  = key.NewBinding(key.WithKeys("/"), key.WithHelp("/", "search"))
	BindRefresh = key.NewBinding(key.WithKeys("r"), key.WithHelp("r", "refresh"))
	BindHelp    = key.NewBinding(key.WithKeys("?"), key.WithHelp("?", "help"))
	BindQuit    = key.NewBinding(key.WithKeys("q"), key.WithHelp("q", "quit"))
)

Footer bindings that also serve as dispatch bindings: single-key actions whose help label matches what they do, so one binding covers both roles.

View Source
var (
	BindBack      = key.NewBinding(key.WithKeys("esc"), key.WithHelp("esc", "back"))
	BindQuitEsc   = key.NewBinding(key.WithKeys("esc"), key.WithHelp("esc", "quit"))
	BindMove      = grouped("↑/↓", "move", KeyUp, KeyDown)
	BindScroll    = grouped("↑/↓", "scroll", KeyUp, KeyDown)
	BindTopBottom = grouped("g/G", "top/bottom", KeyTop, KeyBottom)
	BindPage      = grouped("f/b", "page", KeyPageUp, KeyPageDown)
)

Footer-only display bindings. The two esc variants exist because esc reads as "back" in pickers/pagers that pop a level and "quit" in the ones that exit; the grouped movement bindings reuse the dispatch keystrokes above so the key strings stay defined in exactly one place.

View Source
var LinkStyle = lipgloss.NewStyle().
	Foreground(lipgloss.Color("#00ADD8")).
	Underline(true)

Functions

func DownsamplePeaks added in v1.0.48122

func DownsamplePeaks(values []float64, n int) []float64

DownsamplePeaks reduces values to at most n buckets, each holding the maximum of the samples that fell into it. The maximum — not the mean — is deliberate: these charts exist to show peaks, and averaging is exactly what hides the spike that sent someone looking. An n of zero or less, or a series already short enough, returns values unchanged.

func FlowTitle

func FlowTitle(action string) string

FlowTitle builds a terminal window/tab title for an interactive flow. Our full-screen flows set this so the hosting terminal's tab reflects what the CLI is doing — the way the `claude` CLI does. Bubble Tea emits it as OSC 2 (via tea.View.WindowTitle) and clears it again when the program exits.

An empty action yields the bare prefix, for generic flows (e.g. the pager) that have no single action to name.

func Hints

func Hints(bindings ...key.Binding) string

Hints renders a one-line, muted footer for the given key bindings using the shared house style. Use it for standalone footers a host draws itself (a theme picker, a secret prompt); components that own their footer store the bindings and render them internally.

func NewSpinner

func NewSpinner(color bool) spinner.Model

func Sparkline added in v1.0.48122

func Sparkline(values []float64, ceiling float64) string

Sparkline renders values as a single row of braille — a chart small enough for a table cell or a status line, with no axes and no labels. A line rather than block columns because a flat series scaled to its own peak is all-maximum, which columns draw as a solid run of full blocks: easily misread as "pinned at the maximum" rather than "never changed".

The row is at most one cell per sample, and often one fewer: a line segment spans the interval between two samples rather than a sample itself, and whether the final cell picks up a dot depends on the values. Callers needing an exact width should pad the result rather than assume one.

ceiling is the value that reaches full height; zero or less scales to the largest sample present, which is usually what a sparkline sitting beside an absolute chart wants. Pass values through DownsamplePeaks first to bound the width: the underlying ring buffer keeps only the *last* n samples, silently dropping the start of a longer series. Empty values yield an empty string.

func TrimTrailingSpace added in v1.0.48122

func TrimTrailingSpace(s string) string

TrimTrailingSpace strips trailing spaces from every line, and blank lines from the end. The chart canvas pads each of its rows out to the full width, which would otherwise bury invisible trailing whitespace in every line of output and in every golden file that captures one.

func WithWindowTitle

func WithWindowTitle(v tea.View, title string) tea.View

WithWindowTitle stamps title onto v and returns it. Callers set it on every frame so the title stays put for the flow's lifetime; Bubble Tea only writes the escape when the value changes, and resets the terminal title on exit.

Types

type ChartSeries added in v1.0.48122

type ChartSeries struct {
	// Name labels the series in the legend. A chart with a single series, or one
	// whose series are all unnamed, renders no legend.
	Name string
	// Values are the samples, oldest first, taken at even intervals.
	Values []float64
}

ChartSeries is one line on a LineChart.

type HelpModel

type HelpModel struct {
	// contains filtered or unexported fields
}

HelpModel is a scrollable full-screen help overlay: markdown rendered in a frame (a PagerModel, so it scrolls and offers the less-style "/" search), meant to be embedded in another program that opens it on "?" and dismisses it on esc/q. Unlike MarkdownViewportModel — which is its own program and quits on esc/q — HelpModel records the dismissal via Dismissed() so the host can route back to whatever it was showing rather than exiting.

Content is produced by a render callback so the markdown re-wraps to the live terminal width on resize. Build with NewHelp; the zero value is not usable.

func NewHelp

func NewHelp(render func(width int) string) HelpModel

NewHelp returns a help overlay that displays the markdown produced by render. render is given the column width the content must fit into and is re-invoked on every resize so the markdown re-wraps.

func (HelpModel) Dismissed

func (m HelpModel) Dismissed() bool

Dismissed reports whether the user closed the overlay (esc/q). The host checks this after Update to decide whether to route back to its previous view.

func (HelpModel) Init

func (m HelpModel) Init() tea.Cmd

func (HelpModel) Reopen

func (m HelpModel) Reopen() HelpModel

Reopen clears the dismissed flag, drops any prior search, and scrolls to the top, readying the overlay for a fresh open.

func (HelpModel) Searching

func (m HelpModel) Searching() bool

Searching reports whether the "/" search prompt is open, so the host can let esc/ctrl+c reach the prompt (cancel) instead of treating them as its own keys.

func (HelpModel) SetSize

func (m HelpModel) SetSize(width, height int) HelpModel

SetSize applies a terminal size to the underlying pager, re-wrapping the content to the new width.

func (HelpModel) Update

func (m HelpModel) Update(msg tea.Msg) (HelpModel, tea.Cmd)

Update handles scrolling and the "/" search via the pager, and binds esc/q to dismiss the overlay (esc first clears an active search). ctrl+c is left to the host. Guarding on Searching keeps esc/q typed into the "/" prompt while it is open rather than closing the overlay.

func (HelpModel) View

func (m HelpModel) View() tea.View

type LineChart added in v1.0.48122

type LineChart struct {
	Series []ChartSeries

	// Ceiling is the top of the y axis — typically a known limit, so that a line
	// near the top reads as approaching it. Values above it are clamped. Zero or
	// less means "the largest value across every series", which plots relative
	// shape instead.
	Ceiling float64

	// Width and Height are the total size in cells, including the y-axis gutter and
	// the two rows the x axis and its labels take. Zero means the
	// LineChartDefault… value.
	Width, Height int

	// YStep is the interval in rows between y-axis ticks. Zero means
	// [LineChartDefaultYStep].
	//
	// A tick's value is derived from the y range divided by the number of plot rows,
	// so a plot height that YStep does not divide cleanly yields ticks at unround
	// values like "2.29". Height less two — the rows the x axis occupies — wants to
	// be a multiple of YStep, and ideally a power of two.
	YStep int

	// XTicks is how many marks to place along the x axis, spread evenly with the
	// outermost two under the first and last samples. Zero means
	// [LineChartDefaultXTicks]. Marks whose labels will not fit are dropped, so this
	// is an upper bound rather than a promise.
	XTicks int

	// FormatY renders a y-axis tick value. Nil formats with two decimal places.
	FormatY func(float64) string

	// FormatX renders an x-axis tick, given how far along the axis it sits as a
	// fraction from 0 (the first sample) to 1 (the last). Taking a fraction rather
	// than a timestamp keeps this component free of any notion of what the x axis
	// measures — elapsed time, request count, anything. Nil labels the axis with the
	// fraction as a percentage.
	FormatX func(frac float64) string

	// Color enables the per-series colors that tell overlaid lines apart. Pass
	// iostream.ColorEnabled(ctx). With it off every line is drawn in the same ink,
	// so a caller overlaying more than one series should consider rendering a chart
	// per series instead.
	Color bool
}

LineChart is a multi-series line chart drawn with braille dots, for embedding in output rather than driving interactively: it renders to a plain string.

It wraps NimbleMarkets/ntcharts and adds what that library leaves to the caller — axis tick marks, x labels that stay on round values and always name both ends of the series, a legend, and per-series colors from theme.SeriesStyles.

Series are laid out on a common x domain: the longest spans the full width and a shorter one simply stops early, so overlaid series that ran for different lengths of time stay comparable and one finishing sooner shows as such rather than being stretched to match.

func (LineChart) Render added in v1.0.48122

func (c LineChart) Render() string

Render draws the chart as a newline-separated block with no trailing newline: the plot, the labelled axes, and a legend row when more than one series is named. A chart with no samples renders as the empty string, so the caller can substitute its own "no data" wording.

The result is plain text plus, when Color is set, the per-series escape sequences. It carries no cursor movement, so it is equally correct piped to a file, embedded in a markdown code fence, or printed to a terminal.

type PagerModel

type PagerModel struct {
	// contains filtered or unexported fields
}

PagerModel is a scrollable full-screen pager with less-style search built in. It wraps a viewport, owns the search engine, and renders a footer showing the scroll position, key hints and search state. It handles scrolling (↑/↓, page keys via the viewport), jump-to-top/bottom (g/G, home/end) and the whole "/" search interaction (typing, up/down to recall recent patterns, n/N navigation, match highlighting, scroll-to-match).

It deliberately does NOT handle the lifecycle keys (q, esc, ctrl+c): those mean different things to different hosts — quit here, go back to a picker there — so the embedding model owns them. The typical host loop is:

if !pager.Searching() {
    switch key {
    case "ctrl+c", "q": quit
    case "esc":
        if pager.SearchActive() { pager = pager.ClearSearch(); return }
        quit / go back
    }
}
pager, cmd = pager.Update(msg)

Guarding on Searching() first is important: while the "/" prompt is open those keys are text to be typed (or the prompt's own cancel/commit), not lifecycle actions, so they must fall through to Update.

Build with NewPager; the zero value is not usable. Content is supplied with SetContent / SetContentFollowingTail, or recomputed per width by a WithReflow callback on resize.

func NewPager

func NewPager() PagerModel

NewPager returns an empty pager. Chain WithKeys / WithReflow / WithContent to configure it before use.

func (PagerModel) ClearSearch

func (m PagerModel) ClearSearch() PagerModel

ClearSearch dismisses the committed search, dropping its highlights, and leaves the viewport scrolled where it is.

func (PagerModel) GotoBottom

func (m PagerModel) GotoBottom() PagerModel

GotoBottom scrolls to the bottom of the content.

func (PagerModel) GotoTop

func (m PagerModel) GotoTop() PagerModel

GotoTop scrolls to the top of the content.

func (PagerModel) Init

func (m PagerModel) Init() tea.Cmd

func (PagerModel) Ready

func (m PagerModel) Ready() bool

Ready reports whether a terminal size has been seen, so the pager can render.

func (PagerModel) ResetSearch

func (m PagerModel) ResetSearch() PagerModel

ResetSearch clears the committed search (query, matches, prompt) and re-applies plain content, while preserving the in-memory recall history. Use it when opening fresh content that should start unsearched but still offer previous patterns for recall at the "/" prompt.

func (PagerModel) ScrollPercent

func (m PagerModel) ScrollPercent() float64

ScrollPercent is the viewport's scroll position in the range [0, 1].

func (PagerModel) SearchActive

func (m PagerModel) SearchActive() bool

SearchActive reports whether a committed search is present to dismiss (a query with matches, or a not-found notice). Hosts use it to decide whether Esc should clear the search or fall through to their own quit/back action.

func (PagerModel) Searching

func (m PagerModel) Searching() bool

Searching reports whether the "/" input prompt is currently active.

func (PagerModel) SetContent

func (m PagerModel) SetContent(raw string) PagerModel

SetContent replaces the paged content, preserving the scroll position, and re-applies any active search highlight.

func (PagerModel) SetContentFollowingTail

func (m PagerModel) SetContentFollowingTail(raw string) PagerModel

SetContentFollowingTail is like SetContent but keeps the view pinned to the bottom when it was already there, so streamed output that a reader is watching live keeps scrolling while a reader who has scrolled up to read is left alone.

func (PagerModel) SetSize

func (m PagerModel) SetSize(width, height int) PagerModel

SetSize applies a terminal size, sizing the viewport (reserving the footer), re-wrapping via the reflow callback when set, and re-applying search highlights. It is equivalent to feeding the pager a tea.WindowSizeMsg.

func (PagerModel) Update

func (m PagerModel) Update(msg tea.Msg) (PagerModel, tea.Cmd)

Update handles scrolling, jump keys and the whole "/" search interaction. It leaves q/esc/ctrl+c untouched (except while the search prompt is open, where esc/enter cancel/commit the pattern) so the host can bind them. See PagerModel.

func (PagerModel) View

func (m PagerModel) View(status string) tea.View

View renders the viewport above the footer, on the alternate screen. status is an optional, already-styled segment shown at the left of the footer before the search state (e.g. a "streaming…" indicator); pass "" for none. It returns an empty view until a terminal size is known.

func (PagerModel) WithBorder

func (m PagerModel) WithBorder(c color.Color) PagerModel

WithBorder frames the viewport in a rounded border of the given color, offset from the text by a right pad. Use it to lift an overlay (e.g. the help view) off the content behind it. The border is drawn inside the pager's width, so the content re-wraps to the reduced interior — callers pass the full terminal size as usual. Unset (the default) renders flush, with no border.

func (PagerModel) WithContent

func (m PagerModel) WithContent(raw string) PagerModel

WithContent seeds the initial content. It is rendered once a size is known.

func (PagerModel) WithKeys

func (m PagerModel) WithKeys(keys ...key.Binding) PagerModel

WithKeys sets the footer key bindings (e.g. BindScroll, BindSearch, BindQuit). They are rendered as a muted, " · "-joined hint line at the right of the footer.

func (PagerModel) WithReflow

func (m PagerModel) WithReflow(reflow func(width int) string) PagerModel

WithReflow installs a callback that recomputes the content for a given width, invoked on every resize. Use it for content that must be re-wrapped to the terminal width (rendered markdown). Content that should merely soft-wrap needs no reflow; set it with SetContent instead.

type PreviewModel

type PreviewModel struct {
	// contains filtered or unexported fields
}

PreviewModel is a display-only pane that frames pre-rendered content inside a bordered box. It does no rendering of its own — callers supply already-styled content (e.g. glamour-rendered markdown) and the box size, and PreviewModel only draws the frame. It is used as the right-hand pane of the theme picker to show sample markdown rendered in the highlighted theme.

PreviewModel is not a standalone bubbletea program: it has no Update loop and View returns a plain string so a parent model can compose it horizontally with other panes.

func NewPreviewModel

func NewPreviewModel() PreviewModel

NewPreviewModel returns an empty preview pane.

func (PreviewModel) ContentHeight

func (m PreviewModel) ContentHeight() int

ContentHeight is the row count available for content inside the frame. Callers use it to vertically place content (e.g. to center a loading placeholder).

func (PreviewModel) ContentWidth

func (m PreviewModel) ContentWidth() int

ContentWidth is the column count available for content inside the frame. Callers wrap their content (e.g. via glamour) to this width.

func (PreviewModel) View

func (m PreviewModel) View() string

View renders the bordered content box as a plain string for a parent model to place. Returns "" until a size has been set.

func (PreviewModel) WithContent

func (m PreviewModel) WithContent(content string) PreviewModel

WithContent returns a copy of the pane displaying content. Content is assumed to be pre-wrapped to ContentWidth(); anything wider is clipped by the box.

func (PreviewModel) WithSize

func (m PreviewModel) WithSize(width, height int) PreviewModel

WithSize returns a copy of the pane sized to the given outer width and height (in terminal cells), border and padding included.

type SelectModel

type SelectModel struct {
	// contains filtered or unexported fields
}

SelectModel is a single-choice picker rendered as a vertical list with a cursor (›) marking the focused option. ↑/↓ or k/j move; Enter confirms; Esc/Ctrl+C cancels. When the option list is taller than the available height the list scrolls to keep the cursor visible (see WithHeight).

func NewSelectModel

func NewSelectModel(prompt string, options []string) SelectModel

func (SelectModel) Done

func (m SelectModel) Done() bool

Done reports whether the user has confirmed a selection.

func (SelectModel) Init

func (m SelectModel) Init() tea.Cmd

func (SelectModel) Selected

func (m SelectModel) Selected() int

Selected returns the index chosen by the user. Only valid when Done().

func (SelectModel) Update

func (m SelectModel) Update(msg tea.Msg) (tea.Model, tea.Cmd)

func (SelectModel) View

func (m SelectModel) View() tea.View

func (SelectModel) WithChildren

func (m SelectModel) WithChildren(children []string) SelectModel

WithChildren attaches an optional nested sub-entry to each option. children is parallel to the options; an empty string means "no child" for that row. A child renders as a muted, non-selectable "└ value" line indented beneath its option (e.g. a branch name under a trigger), keeping a long attribute off the option's own row. Children are meant for short lists shown in full; the list still scrolls by option when it overflows, but the window is sized to show every option and its children together, so a caller relying on children should leave enough height for them.

func (SelectModel) WithCursor

func (m SelectModel) WithCursor(i int) SelectModel

WithCursor returns a copy of the model with the initial cursor positioned at index i, clamped to the available options. Use this to pre-select a default choice.

func (SelectModel) WithHeight

func (m SelectModel) WithHeight(rows int) SelectModel

WithHeight sets the number of terminal rows available to the picker. When the option list is taller than this, the list scrolls to keep the cursor visible and a position indicator ("(3–12 of 40)") is appended to the hint. Zero (the default) imposes no limit and renders every option.

func (SelectModel) WithIcons

func (m SelectModel) WithIcons(icons []string) SelectModel

WithIcons attaches an optional status icon to each option. icons is parallel to the options passed to NewSelectModel; an empty string means "no icon" for that row. Each icon is rendered in a fixed column before the label and is emitted verbatim — already styled by the caller if color is wanted — outside the cursor/selection styling, so a status color survives even on the highlighted or chosen row. Rows align one column further in when any option carries an icon.

func (SelectModel) WithItemStyleFunc

func (m SelectModel) WithItemStyleFunc(fn func(i int) lipgloss.Style) SelectModel

WithItemStyleFunc returns a copy whose option labels are styled per-index by fn. The returned style applies to the label text (not the icon or cursor arrow); on the cursor row the accent foreground is layered on top so the highlighted row still stands out while keeping fn's other attributes (e.g. italic). Use it to give a special row a distinct look.

func (SelectModel) WithKeys

func (m SelectModel) WithKeys(keys ...key.Binding) SelectModel

WithKeys returns a copy of the model with custom footer key bindings, replacing the default (move / select / quit) hint line.

func (SelectModel) WithNote

func (m SelectModel) WithNote(note string) SelectModel

WithNote returns a copy of the model with an informational note rendered between the title and the options (e.g. a run's config error). An empty note renders nothing. The note is emitted verbatim — style it in the caller if color is wanted — and may span multiple lines, which are reserved for when the option list scrolls.

type Tabs

type Tabs struct {
	// contains filtered or unexported fields
}

Tabs is a horizontal tab bar seamed into a rounded-bordered window: the active tab's label reads as continuous with the body below it. It owns the active-tab index and the switch keys (←/→, tab/shift+tab); the host supplies the labels and, per frame, the active tab's body content (Tabs draws only the chrome, not the body — the host decides what each tab shows). Enter/esc and body navigation are left to the host.

func NewTabs

func NewTabs(labels []string, color bool) Tabs

NewTabs builds a tab bar over labels with the first tab active.

func (Tabs) Active

func (t Tabs) Active() int

Active returns the index of the active tab.

func (Tabs) Count

func (t Tabs) Count() int

Count returns the number of tabs.

func (Tabs) Next

func (t Tabs) Next() Tabs

Next / Prev return a copy with the following / preceding tab active, wrapping at the ends.

func (Tabs) Prev

func (t Tabs) Prev() Tabs

func (Tabs) SetActive

func (t Tabs) SetActive(i int) Tabs

SetActive returns a copy with tab i active (wrapped into range).

func (Tabs) Update

func (t Tabs) Update(msg tea.Msg) (Tabs, bool)

Update switches the active tab on a tab-switch key (←/→, tab/shift+tab). The returned bool reports whether the key was consumed, so the host can forward everything else to the active tab's body.

func (Tabs) View

func (t Tabs) View(body string) string

View renders the tab row over a rounded-bordered window wrapping body: the tab row supplies the window's top edge, and the row is split to the window's width so the two seam together. The window is sized to the body's width.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL