common

package
v1.5.0 Latest Latest
Warning

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

Go to latest
Published: Jan 12, 2026 License: MIT Imports: 23 Imported by: 0

README

common package

Defines common utilities for ui and file operations package everyone can use common package, but common package should not have any dependency on any other package. Currently, common package is a big monolith, but we plan to separate it into config,

Dependencies

  • src/config package

Documentation

Index

Constants

View Source
const (
	WheelRunTime          = 5
	DefaultCommandTimeout = 5000 * time.Millisecond
	DateModifiedOption    = "Date Modified"
	InvalidTypeString     = "InvalidType"
)
View Source
const (
	SameRenameWarnTitle   = "There is already a file or directory with that name"
	SameRenameWarnContent = "This operation will override the existing file"
)
View Source
const (
	TrashWarnTitle             = "Are you sure you want to move this to trash can"
	TrashWarnContent           = "This operation will move file or directory to trash can."
	PermanentDeleteWarnTitle   = "Are you sure you want to completely delete"
	PermanentDeleteWarnContent = "This operation cannot be undone and your data will be completely lost."
)
View Source
const (
	MinimumHeight = 24
	MinimumWidth  = 60

	// TODO : These are model object properties, not global properties
	// We are modifying them in the code many time. They need to be part of model struct.
	MinFooterHeight = 6
	ModalWidth      = 60
	ModalHeight     = 7
)
View Source
const (
	KilobyteSize      = 1000 // SI decimal unit
	KibibyteSize      = 1024 // Binary unit
	TabWidth          = 4    // Standard tab expansion width
	DefaultBufferSize = 1024 // Default buffer size for string operations
	NonBreakingSpace  = 0xa0 // Unicode non-breaking space
	EscapeChar        = 0x1b // ANSI escape character
	ASCIIMax          = 0x7f // Maximum ASCII character value
)

Size calculation constants

View Source
const (
	HelpKeyColumnWidth       = 55              // width of help key column in CLI help
	DefaultCLIContextTimeout = 5 * time.Second // default CLI context timeout for CLI ops

	PanelPadding    = 3 // rows reserved around file list (borders/header/footer)
	BorderPadding   = 2 // rows/cols for outer border frame
	InnerPadding    = 4 // cols for inner content padding (truncate widths)
	FooterGroupCols = 3 // columns per group in footer layout math

	DefaultFilePanelWidth    = 10 // default width for file panels
	FilePanelMax             = 10 // max number of file panels supported
	ResponsiveWidthThreshold = 95 // width breakpoint for layout behavior

	HeightBreakA = 30 // responsive height tiers
	HeightBreakB = 35
	HeightBreakC = 40
	HeightBreakD = 45

	ReRenderChunkDivisor = 100 // divisor for re-render throttling

	FilePanelWidthUnit    = 20                     // width unit used to calculate max file panels
	DefaultPreviewTimeout = 500 * time.Millisecond // preview operation timeout

	FileNameRatioMin = 25
	FileNameRatioMax = 100

	// UI positioning
	CenterDivisor = 2 // divisor for centering UI elements
)

Shared UI/layout constants to replace magic numbers flagged by mnd.

Variables

View Source
var (
	HotkeysTomlString  string
	ConfigTomlString   string
	DefaultThemeString string
)

Variables for holding default configurations of each settings

View Source
var (
	SideBarSuperfileTitle string
	SideBarPinnedDivider  string
	SideBarDisksDivider   string
	SideBarNoneText       string

	ProcessBarNoneText string
	ClipboardNoneText  string

	FilePanelTopDirectoryIcon string
	FilePanelNoneText         string

	FilePreviewNoFileInfoText               string
	FilePreviewNoContentText                string
	FilePreviewUnsupportedFormatText        string
	FilePreviewUnsupportedFileMode          string
	FilePreviewDirectoryUnreadableText      string
	FilePreviewEmptyText                    string
	FilePreviewError                        string
	FilePreviewPanelClosedText              string
	FilePreviewImagePreviewDisabledText     string
	FilePreviewUnsupportedImageFormatsText  string
	FilePreviewImageConversionErrorText     string
	FilePreviewBatNotInstalledText          string
	FilePreviewThumbnailGenerationErrorText string

	CheckboxChecked        string
	CheckboxCheckedFocused string
	CheckboxEmpty          string
	CheckboxEmptyFocused   string

	ModalConfirmInputText string
	ModalCancelInputText  string
	ModalOkayInputText    string
	ModalInputSpacingText string
	LipglossError         string
)
View Source
var (
	UnsupportedPreviewFormats = []string{".torrent"}
	ImageExtensions           = map[string]bool{
		".jpg":  true,
		".jpeg": true,
		".png":  true,
		".gif":  true,
		".bmp":  true,
		".tiff": true,
		".svg":  true,
		".webp": true,
		".ico":  true,
	}
	VideoExtensions = map[string]bool{
		".mkv":  true,
		".mp4":  true,
		".mov":  true,
		".avi":  true,
		".flv":  true,
		".webm": true,
		".wmv":  true,
		".m4v":  true,
		".mpeg": true,
		".3gp":  true,
		".ogv":  true,
	}
)
View Source
var (
	TerminalTooSmall    lipgloss.Style
	TerminalCorrectSize lipgloss.Style
)
View Source
var (
	MainStyle      lipgloss.Style
	FilePanelStyle lipgloss.Style
	SidebarStyle   lipgloss.Style
	FooterStyle    lipgloss.Style
	ModalStyle     lipgloss.Style
)
View Source
var (
	SidebarDividerStyle  lipgloss.Style
	SidebarTitleStyle    lipgloss.Style
	SidebarSelectedStyle lipgloss.Style
)
View Source
var (
	FilePanelCursorStyle lipgloss.Style
	FooterCursorStyle    lipgloss.Style
	ModalCursorStyle     lipgloss.Style
)
View Source
var (
	FilePanelTopDirectoryIconStyle lipgloss.Style
	FilePanelTopPathStyle          lipgloss.Style
	FilePanelItemSelectedStyle     lipgloss.Style
	FilePanelSelectBoxStyle        lipgloss.Style
)
View Source
var (
	ProcessErrorStyle       lipgloss.Style
	ProcessInOperationStyle lipgloss.Style
	ProcessCancelStyle      lipgloss.Style
	ProcessSuccessfulStyle  lipgloss.Style
)
View Source
var (
	ModalCancel     lipgloss.Style
	ModalConfirm    lipgloss.Style
	ModalTitleStyle lipgloss.Style
	ModalErrorStyle lipgloss.Style
)
View Source
var (
	HelpMenuHotkeyStyle lipgloss.Style
	HelpMenuTitleStyle  lipgloss.Style
)
View Source
var (
	PromptSuccessStyle lipgloss.Style
	PromptFailureStyle lipgloss.Style
)
View Source
var (
	FilePanelBorderColor lipgloss.Color
	SidebarBorderColor   lipgloss.Color
	FooterBorderColor    lipgloss.Color

	FilePanelBorderActiveColor lipgloss.Color
	SidebarBorderActiveColor   lipgloss.Color
	FooterBorderActiveColor    lipgloss.Color
	ModalBorderActiveColor     lipgloss.Color

	FullScreenBGColor lipgloss.Color
	FilePanelBGColor  lipgloss.Color
	SidebarBGColor    lipgloss.Color
	FooterBGColor     lipgloss.Color
	ModalBGColor      lipgloss.Color

	FullScreenFGColor lipgloss.Color
	FilePanelFGColor  lipgloss.Color
	SidebarFGColor    lipgloss.Color
	FooterFGColor     lipgloss.Color
	ModalFGColor      lipgloss.Color
)
View Source
var (
	BottomMiddleBorderSplit string
)
View Source
var TransparentBackgroundColor string

Functions

func ClipboardPrettierName

func ClipboardPrettierName(name string, width int, isDir bool, isLink bool, isSelected bool) string

func FileNameWithoutExtension

func FileNameWithoutExtension(fileName string) string

func FilePanelItemRender added in v1.5.0

func FilePanelItemRender(data string,
	width int,
	isSelected bool,
	bgColor lipgloss.Color,
	alignment lipgloss.Position,
) string

func FilePanelItemRenderWithIcon added in v1.5.0

func FilePanelItemRenderWithIcon(
	name string,
	width int,
	isDir bool,
	isLink bool,
	isSelected bool,
	bgColor lipgloss.Color,
) string

func FirstUseModal

func FirstUseModal(height int, width int) lipgloss.Style

Generate first use modal style (This modal pop up when user first use superfile)

func FormatFileSize

func FormatFileSize(size int64) string

func FullScreenStyle

func FullScreenStyle(height int, width int) lipgloss.Style

Generate full screen style for terminal size too small etc

func GenerateBorder

func GenerateBorder() lipgloss.Border

Generate border style

func GenerateFooterBorder

func GenerateFooterBorder(countString string, width int) string

func GenerateGradientColor

func GenerateGradientColor() progress.Option

func GenerateNewFileTextInput

func GenerateNewFileTextInput() textinput.Model

func GeneratePinnedRenameTextInput

func GeneratePinnedRenameTextInput(cursorPos int, defaultValue string) textinput.Model

func GeneratePromptTextInput

func GeneratePromptTextInput() textinput.Model

func GenerateRenameTextInput

func GenerateRenameTextInput(width int, cursorPos int, defaultValue string) textinput.Model

func GenerateSearchBar

func GenerateSearchBar() textinput.Model

TODO : Fix Code duplication in textInput.Model creation This eventually caused a bug, where we created new model for sidebar search, and Didn't set `Width` in that. Take Width and other parameters as input in one function Generate search bar for file panel

func GetElementIcon

func GetElementIcon(file string, isDir bool, isLink bool, nerdFont bool) icon.Style

func GetHelpMenuHotkeyString added in v1.5.0

func GetHelpMenuHotkeyString(hotkeys []string) string

func InitTrash added in v1.4.0

func InitTrash() bool

func IsBufferPrintable

func IsBufferPrintable(buffer []byte) bool

Separated this out out for easy testing

func IsExtensionExtractable

func IsExtensionExtractable(ext string) bool

IsExtensionExtractable checks if a string is a valid compressed archive file extension.

func IsTextFile

func IsTextFile(filename string) (bool, error)

Check file is text file or not

func LoadAllDefaultConfig

func LoadAllDefaultConfig(content embed.FS)

LoadAllDefaultConfig : Load all default configurations from embedded superfile_config folder into global configurations variables and write theme files if its needed.

func LoadConfigError

func LoadConfigError(value string) string

Generate config error style

func LoadConfigFile

func LoadConfigFile()

Load configurations from the configuration file. Compares the content with the default values and modify the config file to include default configs if the FixConfigFile flag is on TODO : Fix the code duplication with LoadHotkeysFile().

func LoadConfigStringGlobals

func LoadConfigStringGlobals(content embed.FS) error

func LoadHotkeysError

func LoadHotkeysError(value string) string

Generate config error style

func LoadHotkeysFile

func LoadHotkeysFile(ignoreMissingFields bool)

Load keybinds from the hotkeys file. Compares the content with the default values and modify the hotkeys if the FixHotkeys flag is on.

func LoadInitialPrerenderedVariables

func LoadInitialPrerenderedVariables()

No dependencies

func LoadPrerenderedVariables

func LoadPrerenderedVariables()

Dependecies - TODO We should programmatically guarantee these dependencies. And log error if its not satisfied. LoadThemeConfig() in style.go should be finished loadConfigFile() in config_types.go should be finished InitIcon() in config package in function.go should be finished

func LoadThemeConfig

func LoadThemeConfig()

func LoadThemeFile

func LoadThemeFile()

LoadThemeFile : Load configurations from theme file into &theme set default values if we cant read user's theme file

func MakePrintable

func MakePrintable(line string) string

func MakePrintableWithEscCheck

func MakePrintableWithEscCheck(line string, allowEsc bool) string

Although some characters like `\x0b`(vertical tab) are printable, previewing them breaks the layout. So, among the "non-graphic" printable characters, we only need \n and \t Space and NBSP are already considered graphic by unicode. Allow Any rune that is above ASCII control characters range 0x7f for valid unicodes like nerdfont \uf410 \U000f0868 Also allow \x0b that is for escape sequences This function should better not be broken into multiple functions

func ModalBorderStyle

func ModalBorderStyle(height int, width int) lipgloss.Style

func PopulateConfigFromFile

func PopulateConfigFromFile(configFilePath string) error

func PopulateGlobalConfigs

func PopulateGlobalConfigs() error

Used only in unit tests Populate config variables based on given file

func PopulateHotkeyFromFile

func PopulateHotkeyFromFile(hotkeyFilePath string) error

func PopulateThemeFromFile

func PopulateThemeFromFile(themeFilePath string) error

func SortOptionsModalBorderStyle

func SortOptionsModalBorderStyle(height int, width int, borderBottom string) lipgloss.Style

Generate sort options modal border style

func StringColorRender

func StringColorRender(fgColor lipgloss.Color, bgColor lipgloss.Color) lipgloss.Style

Return only fg and bg color style

func TransparentAllBackgroundColor

func TransparentAllBackgroundColor()

func TruncateMiddleText

func TruncateMiddleText(text string, maxChars int, tails string) string

func TruncateText

func TruncateText(text string, maxChars int, tails string) string

TODO: This has a bug. Remove its usage. Remove all custom truncation And audit and evaluate any problem The logic truncates to maxChars - len(tails) first, then checks if truncation occurred. This means: - "Hello" with maxChars=5 gets truncated to 2 chars (5-3=2), producing "He..." - "Hello" with maxChars=6 gets truncated to 3 chars (6-3=3), producing "Hel..." Both cases are wrong - "Hello" fits within 5 and 6 characters, so it shouldn't be truncated at all.

func TruncateTextBeginning

func TruncateTextBeginning(text string, maxChars int, tails string) string

func ValidateConfig

func ValidateConfig(c *ConfigType) error

func WriteThemeFiles

func WriteThemeFiles(content embed.FS) error

Types

type CDCurrentPanelAction

type CDCurrentPanelAction struct {
	Location string
}

func (CDCurrentPanelAction) String

func (c CDCurrentPanelAction) String() string

type ConfigType

type ConfigType struct {
	Theme string `toml:"theme" comment:"More details are at https://superfile.dev/configure/superfile-config/\nchange your theme"`

	Editor    string `toml:"editor" comment:"\nThe editor files will be opened with. (Leave blank to use the EDITOR environment variable)."`
	DirEditor string `toml:"dir_editor" comment:"\nThe editor directories will be opened with. (Leave blank to use the default editors)."`
	// The table (map) for editor by file extension
	OpenWith map[string]string `toml:"open_with" comment:"\nCustom open commands by file extension."`

	AutoCheckUpdate        bool   `toml:"auto_check_update" comment:"\nAuto check for update"`
	CdOnQuit               bool   `` /* 138-byte string literal not displayed */
	DefaultOpenFilePreview bool   `toml:"default_open_file_preview" comment:"\nWhether to open file preview automatically every time superfile is opened."`
	ShowImagePreview       bool   `toml:"show_image_preview" comment:"\nWhether to show image preview."`
	ShowPanelFooterInfo    bool   `toml:"show_panel_footer_info" comment:"\nWhether to show additional footer info for file panel."`
	DefaultDirectory       string `toml:"default_directory" comment:"\nThe path of the first file panel when superfile is opened."`
	FileSizeUseSI          bool   `` /* 131-byte string literal not displayed */
	DefaultSortType        int    `toml:"default_sort_type" comment:"\nDefault sort type (0: Name, 1: Size, 2: Date Modified, 3: Type)."`
	SortOrderReversed      bool   `toml:"sort_order_reversed" comment:"\nDefault sort order (false: Ascending, true: Descending)."`
	CaseSensitiveSort      bool   `toml:"case_sensitive_sort" comment:"\nCase sensitive sort by name (capital \"B\" comes before \"a\" if true)."`
	ShellCloseOnSuccess    bool   `toml:"shell_close_on_success" comment:"\nWhether to close the shell on successful command execution."`
	Debug                  bool   `toml:"debug" comment:"\nWhether to enable debug mode."`
	// IgnoreMissingFields controls whether warnings about missing TOML fields are suppressed.
	IgnoreMissingFields   bool `toml:"ignore_missing_fields" comment:"\nWhether to ignore warnings about missing fields in the config file."`
	PageScrollSize        int  `toml:"page_scroll_size" comment:"\nNumber of lines to scroll for PgUp/PgDown keys (0: full page, default behavior)."`
	FilePanelExtraColumns int  `` /* 152-byte string literal not displayed */
	FilePanelNamePercent  int  `` /* 170-byte string literal not displayed */

	Nerdfont                bool   `` /* 147-byte string literal not displayed */
	ShowSelectIcons         bool   `toml:"show_select_icons" comment:"\nShow checkbox icons in select mode (requires nerdfont)"`
	TransparentBackground   bool   `` /* 137-byte string literal not displayed */
	FilePreviewWidth        int    `` /* 227-byte string literal not displayed */
	EnableFilePreviewBorder bool   `toml:"enable_file_preview_border" comment:"\nEnable border around the file preview panel (default: false)"`
	CodePreviewer           string `` /* 153-byte string literal not displayed */
	SidebarWidth            int    `` /* 218-byte string literal not displayed */

	BorderTop         string `toml:"border_top" comment:"\nBorder style"`
	BorderBottom      string `toml:"border_bottom"`
	BorderLeft        string `toml:"border_left"`
	BorderRight       string `toml:"border_right"`
	BorderTopLeft     string `toml:"border_top_left"`
	BorderTopRight    string `toml:"border_top_right"`
	BorderBottomLeft  string `toml:"border_bottom_left"`
	BorderBottomRight string `toml:"border_bottom_right"`
	BorderMiddleLeft  string `toml:"border_middle_left"`
	BorderMiddleRight string `toml:"border_middle_right"`

	Metadata          bool `` /* 222-byte string literal not displayed */
	EnableMD5Checksum bool `toml:"enable_md5_checksum" comment:"Enable MD5 checksum generation for files"`
	ZoxideSupport     bool `toml:"zoxide_support" comment:"Zoxide support for the fast navigation"`
}

Configuration settings

var Config ConfigType

func (*ConfigType) GetIgnoreMissingFields added in v1.3.2

func (c *ConfigType) GetIgnoreMissingFields() bool

GetIgnoreMissingFields reports whether warnings about missing TOML fields should be ignored.

type HotkeysType

type HotkeysType struct {
	Confirm []string `` /* 175-byte string literal not displayed */
	Quit    []string `toml:"quit"`
	CdQuit  []string `toml:"cd_quit"`

	// movement
	ListUp   []string `toml:"list_up" comment:"movement"`
	ListDown []string `toml:"list_down"`
	PageUp   []string `toml:"page_up"`
	PageDown []string `toml:"page_down"`

	CloseFilePanel         []string `toml:"close_file_panel" comment:"file panel control"`
	CreateNewFilePanel     []string `toml:"create_new_file_panel"`
	NextFilePanel          []string `toml:"next_file_panel"`
	PreviousFilePanel      []string `toml:"previous_file_panel"`
	ToggleFilePreviewPanel []string `toml:"toggle_file_preview_panel"`
	OpenSortOptionsMenu    []string `toml:"open_sort_options_menu"`
	ToggleReverseSort      []string `toml:"toggle_reverse_sort"`

	FocusOnProcessBar []string `toml:"focus_on_process_bar" comment:"change focus"`
	FocusOnSidebar    []string `toml:"focus_on_sidebar"`
	FocusOnMetaData   []string `toml:"focus_on_metadata"`

	FilePanelItemCreate []string `toml:"file_panel_item_create" comment:"create file/directory and rename "`
	FilePanelItemRename []string `toml:"file_panel_item_rename"`

	CopyItems              []string `toml:"copy_items" comment:"file operate"`
	PasteItems             []string `toml:"paste_items"`
	CutItems               []string `toml:"cut_items"`
	DeleteItems            []string `toml:"delete_items"`
	PermanentlyDeleteItems []string `toml:"permanently_delete_items"`

	ExtractFile  []string `toml:"extract_file" comment:"compress and extract"`
	CompressFile []string `toml:"compress_file"`

	OpenFileWithEditor             []string `toml:"open_file_with_editor" comment:"editor"`
	OpenCurrentDirectoryWithEditor []string `toml:"open_current_directory_with_editor"`

	PinnedDirectory []string `toml:"pinned_directory" comment:"other"`
	ToggleDotFile   []string `toml:"toggle_dot_file"`
	ChangePanelMode []string `toml:"change_panel_mode"`
	OpenHelpMenu    []string `toml:"open_help_menu"`
	OpenCommandLine []string `toml:"open_command_line"`
	OpenSPFPrompt   []string `toml:"open_spf_prompt"`
	OpenZoxide      []string `toml:"open_zoxide"`

	CopyPath []string `toml:"copy_path"`
	CopyPWD  []string `toml:"copy_present_working_directory"`

	ToggleFooter []string `toml:"toggle_footer"`

	ConfirmTyping []string `` /* 177-byte string literal not displayed */
	CancelTyping  []string `toml:"cancel_typing"`

	ParentDirectory []string `` /* 221-byte string literal not displayed */
	SearchBar       []string `toml:"search_bar"`

	FilePanelSelectModeItemsSelectDown []string `` /* 245-byte string literal not displayed */
	FilePanelSelectModeItemsSelectUp   []string `toml:"file_panel_select_mode_items_select_up"`
	FilePanelSelectAllItem             []string `toml:"file_panel_select_all_items"`
}
var Hotkeys HotkeysType

type ModelAction

type ModelAction interface {
	String() string
}

Placeholder inteface for now, might later move 'model' type to commons and have and add an execute(model) function to this

type NoAction

type NoAction struct {
}

func (NoAction) String

func (n NoAction) String() string

type OpenPanelAction

type OpenPanelAction struct {
	Location string
}

func (OpenPanelAction) String

func (o OpenPanelAction) String() string

type ShellCommandAction

type ShellCommandAction struct {
	Command string
}

func (ShellCommandAction) String

func (s ShellCommandAction) String() string

type SplitPanelAction

type SplitPanelAction struct{}

We could later move 'model' type to commons and have these actions implement an execute(model) interface

func (SplitPanelAction) String

func (s SplitPanelAction) String() string

type ThemeType

type ThemeType struct {
	// Code syntax highlight theme
	CodeSyntaxHighlightTheme string `toml:"code_syntax_highlight"`

	// Border
	FilePanelBorder string `toml:"file_panel_border"`
	SidebarBorder   string `toml:"sidebar_border"`
	FooterBorder    string `toml:"footer_border"`

	// Border Active
	FilePanelBorderActive string `toml:"file_panel_border_active"`
	SidebarBorderActive   string `toml:"sidebar_border_active"`
	FooterBorderActive    string `toml:"footer_border_active"`
	ModalBorderActive     string `toml:"modal_border_active"`

	// Background (bg)
	FullScreenBG string `toml:"full_screen_bg"`
	FilePanelBG  string `toml:"file_panel_bg"`
	SidebarBG    string `toml:"sidebar_bg"`
	FooterBG     string `toml:"footer_bg"`
	ModalBG      string `toml:"modal_bg"`

	// Foreground (fg)
	FullScreenFG string `toml:"full_screen_fg"`
	FilePanelFG  string `toml:"file_panel_fg"`
	SidebarFG    string `toml:"sidebar_fg"`
	FooterFG     string `toml:"footer_fg"`
	ModalFG      string `toml:"modal_fg"`

	// Special Color
	Cursor             string   `toml:"cursor"`
	Correct            string   `toml:"correct"`
	Error              string   `toml:"error"`
	Hint               string   `toml:"hint"`
	Cancel             string   `toml:"cancel"`
	GradientColor      []string `toml:"gradient_color"`
	DirectoryIconColor string   `toml:"directory_icon_color"`

	// File Panel Special Items
	FilePanelTopDirectoryIcon string `toml:"file_panel_top_directory_icon"`
	FilePanelTopPath          string `toml:"file_panel_top_path"`
	FilePanelItemSelectedFG   string `toml:"file_panel_item_selected_fg"`
	FilePanelItemSelectedBG   string `toml:"file_panel_item_selected_bg"`

	// Sidebar Special Items
	SidebarTitle          string `toml:"sidebar_title"`
	SidebarItemSelectedFG string `toml:"sidebar_item_selected_fg"`
	SidebarItemSelectedBG string `toml:"sidebar_item_selected_bg"`
	SidebarDivider        string `toml:"sidebar_divider"`

	// Modal Special Items
	ModalCancelFG  string `toml:"modal_cancel_fg"`
	ModalCancelBG  string `toml:"modal_cancel_bg"`
	ModalConfirmFG string `toml:"modal_confirm_fg"`
	ModalConfirmBG string `toml:"modal_confirm_bg"`

	HelpMenuHotkey string `toml:"help_menu_hotkey"`
	HelpMenuTitle  string `toml:"help_menu_title"`
}

Theme configuration

var Theme ThemeType

Jump to

Keyboard shortcuts

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