From fc200ddb9599ebfccf4700b4209d844e0c032ddd Mon Sep 17 00:00:00 2001 From: Rasmus Lindroth Date: Mon, 23 Jan 2023 20:32:03 +0100 Subject: [PATCH] 2.0.0 (#253) * bump version * rename window to pane * Add internal editor * show chars left * don't assume xdg-open, use open on iOS and start on Windows * remove leaders and fix "switch" * support multiple accounts * update title on named feeds * terminal-title=3, no top bar and no terminal title * remove ini package * add commands-in-new-pane and dynamic-timeline-name * add delete-temp-files * add lang to toots * update docs * update modules * add clear-temp * upgrade modules * add gruvbox light --- README.md | 27 +- config.example.ini | 826 ------------ config.example.toml | 1268 ++++++++++++++++++ config/config.go | 1730 +++++++++++------------- config/default_config.go | 1380 ++++++++++++------- config/help.tmpl | 34 +- config/keys.go | 8 +- config/load.go | 4 +- config/themes/default.ini | 28 - config/themes/default.toml | 28 + config/themes/gruvbox-light.toml | 27 + config/themes/nord.ini | 14 - config/themes/nord.toml | 14 + config/themes/papercolor-light.ini | 22 - config/themes/papercolor-light.toml | 22 + config/themes/snow-white.toml | 27 + config/toml.go | 258 ++++ config/toml_default.go | 446 +++++++ config/toot.tmpl | 1 + docs/man/tut.1 | 14 +- docs/man/tut.1.md | 11 +- docs/man/tut.5 | 1926 ++++++++++++++++----------- docs/man/tut.5.md | 1416 ++++++++++++++------ docs/man/tut.7 | 37 +- docs/man/tut.7.md | 36 +- feed/feed.go | 80 +- go.mod | 17 +- go.sum | 55 +- main.go | 51 +- ui/cliview.go | 13 +- ui/cmdbar.go | 56 +- ui/commands.go | 271 ++-- ui/composeview.go | 216 ++- ui/editorview.go | 94 ++ ui/feed.go | 365 +++-- ui/input.go | 206 ++- ui/item_status.go | 13 +- ui/linkview.go | 28 +- ui/mainview.go | 97 +- ui/media.go | 14 +- ui/open.go | 22 +- ui/pollview.go | 28 +- ui/preferenceview.go | 80 +- ui/statusbar.go | 3 + ui/styled_elements.go | 38 + ui/timeline.go | 160 ++- ui/top.go | 4 +- ui/tutview.go | 96 +- ui/view.go | 9 + util/util.go | 39 +- util/xdg.go | 23 +- 51 files changed, 7406 insertions(+), 4276 deletions(-) delete mode 100644 config.example.ini create mode 100644 config.example.toml delete mode 100644 config/themes/default.ini create mode 100644 config/themes/default.toml create mode 100644 config/themes/gruvbox-light.toml delete mode 100644 config/themes/nord.ini create mode 100644 config/themes/nord.toml delete mode 100644 config/themes/papercolor-light.ini create mode 100644 config/themes/papercolor-light.toml create mode 100644 config/themes/snow-white.toml create mode 100644 config/toml.go create mode 100644 config/toml_default.go create mode 100644 ui/editorview.go diff --git a/README.md b/README.md index db1803a..74bf6cc 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,8 @@ You can find Linux binaries under [releases](https://github.com/RasmusLindroth/t * `:boosts` = Lists users that have boosted the toot * `:bookmarks` = List all your bookmarks * `:clear-notifications` = Remove all of your notifications -* `:close-window` = Closes the current window, including all the timelines in said window +* `:clear-temp` = Remove all of your media files that have been downloaded. Only needed if you have set delete-temp-files to false under [media] in your config. +* `:close-pane` = Closes the current pane, including all the timelines in said pane * `:compose` = Compose a new toot * `:edit` = Edit one of your toots * `:favorited` = Lists toots you've favorited @@ -60,14 +61,17 @@ You can find Linux binaries under [releases](https://github.com/RasmusLindroth/t * `:history` = Show edits of a toot * `:lists` = Show a list of your lists * `:list-placement top|right|bottom|left` = Place the list in choosen placement -* `:list-split row|column` = Split the timelines in window by row or column -* `:move-window left|right|up|down|home|end` = Moves the window in choosen direction -* `:mv l|r|u|d|h|e` = Shorter form of former command +* `:list-split row|column` = Split the timelines by row or column +* `:login` = Login to one more account +* `:move-pane left|right|up|down|home|end` = Moves the pane in choosen direction +* `:mp l|r|u|d|h|e` = Shorter form of former command * `:muting` = Lists users that you've muted * `:newer` = Force load newer toots in current timeline +* `:next-acct` = Go to the next account if you're logged in to multiple * `:preferences` = Update your profile and some other settings +* `:prev-acct` = Go to the prev account if you're logged in to multiple * `:profile` = Go to your profile -* `:proportions [int] [int]` = Sets the proportions of the windows and the content. The first integer is your windows and the other for content, e.g. :proportions 1 3 +* `:proportions [int] [int]` = Sets the proportions of the panes and the content. The first integer is your panes and the other for content, e.g. :proportions 1 3 * `:refetch` = Refetches the current item that you're viewing. Can be used to update poll results. * `:saved` = Alias for bookmarks * `:stick-to-top` = Toggle the stick-to-top setting that always shows the latest toot in all timelines @@ -75,7 +79,7 @@ You can find Linux binaries under [releases](https://github.com/RasmusLindroth/t * `:tags` = List of tags that you're following * `:unfollow-tag ` = Unfollow the hashtag named <tag>, e.g. :unfollow-tag tut * `:user ` = Search for users named <username>, e.g. :user rasmus. To narrow a search include the instance like this :user rasmus@mastodon.acc.sunet.se -* `:window ` = Switch window by index (zero indexed) e.g. :window 0 for the left/top window +* `:pane ` = Switch pane by index (zero indexed) e.g. :pane 0 for the left/top pane Keys without description in tut * `c` = Compose a new toot @@ -99,12 +103,12 @@ Tut is configurable, so you can change things like the colors, the default timel what image viewer to use and some more. Check out the configuration file to see all the options. -You find it in `XDG_CONFIG_HOME/tut/config.ini` on Linux which usually equals to `~/.config/tut/config.ini`. +You find it in `XDG_CONFIG_HOME/tut/config.toml` on Linux which usually equals to `~/.config/tut/config.toml`. If you don't run Linux it will use the path of [os#UserConfigDir](https://github.com/golang/go/blob/7dc9fcb13de7bb20b11f6a526865545cc9142c2c/src/os/file.go#L455-L461). But if you move the tut folder to `XDG_CONFIG_HOME/tut/` and have set the environment variable `XDG_CONFIG_HOME` it will look there instead of the standard place. -You can find an updated configuration file in this repo named `config.example.ini`. +You can find an updated configuration file in this repo named `config.example.toml`. If there are any new configurations options you can copy them frome that file. If you prefer a website you can read about all the options on [tut.anv.nu](https://tut.anv.nu/config) and if man pages are your thing use `tut(5)`. @@ -210,16 +214,17 @@ you will have to add `go/bin` to your `$PATH`. ## Flags and commands ``` Commands: - example-config - creates the default configuration file in the current directory and names it ./config.example.ini + example-config - creates the default configuration file in the current directory and names it ./config.example.toml Flags: -h --help prints this message -v --version prints the version -n --new-user add one more user to tut - -c --config load config.ini from + -c --config load config.toml from -d --config-dir load all config from -u --user login directly to user named - If two users are named the same. Use full name like tut@fosstodon.org + If you want to login to multiple accounts seperate them with a space and use quotation marks. E.g. -u "acc_one acc_two" + If two users are named the same. Use full name like tut@fosstodon.org ``` If you don't want to set `--config` or `--config-dir` everytime you can set diff --git a/config.example.ini b/config.example.ini deleted file mode 100644 index 96cfb9c..0000000 --- a/config.example.ini +++ /dev/null @@ -1,826 +0,0 @@ -# Configuration file for tut - -[general] -# Shows a confirmation view before actions such as favorite, delete toot, boost -# etc. -# default=true -confirmation=true - -# Enable support for using the mouse in tut to select items. -# default=false -mouse-support=false - -# Timelines adds windows of feeds. You can customize the number of feeds, what -# they should show and the key to activate them. -# -# Available timelines: home, direct, local, federated, special, bookmarks, -# saved, favorited, notifications, lists, mentions, tag -# -# The one named special are the home timeline with only boosts and/or replies. -# -# Tag is special as you need to add the tag after, see the example below. -# -# The syntax is: -# timelines=feed,[name],[keys...],[showBoosts],[showReplies] -# -# Tha values in brackets are optional. You can see the syntax for keys under the -# [input] section. -# -# showBoosts and showReplies must be formated as bools. So either true or false. -# They always defaults to true. -# -# Some examples: -# -# home timeline with the name Home -# timelines=home,Home -# -# local timeline with the name Local and it gets focus when you press 2. It will -# also hide boosts in the timeline, but show toots that are replies. -# timelines=local,Local,'2',false,true -# -# notification timeline with the name [N]otifications and it gets focus when you -# press n or N -# timelines=notifications,[N]otifications,'n','N' -# -# tag timeline for #linux with the name Linux and it gets focus when you press -# timelines=tag linux,Linux,"F2" -# -# -# If you don't set any timelines it will default to this: -# timelines=home -# timelines=notifications,[N]otifications,'n','N' -# - - -# The date format to be used. See https://godoc.org/time#Time.Format -# default=2006-01-02 15:04 -date-format=2006-01-02 15:04 - -# Format for dates the same day. See date-format for more info. -# default=15:04 -date-today-format=15:04 - -# This displays relative dates instead for statuses that are one day or older -# the output is 1y2m1d (1 year 2 months and 1 day) -# -# The value is an integear -# -1 = don't use relative dates -# 0 = always use relative dates, except for dates < 1 day -# 1 - ∞ = number of days to use relative dates -# -# Example: date-relative=28 will display a relative date for toots that are -# between 1-28 days old. Otherwhise it will use the short or long format. -# default=-1 -date-relative=-1 - -# The max width of text before it wraps when displaying toots. -# 0 = no restriction. -# default=0 -max-width=0 - -# Where do you want the list of toots to be placed? -# Valid values: left, right, top, bottom. -# default=left -list-placement=left - -# If you have notification-feed set to true you can display it under the main -# list of toots (row) or place it to the right of the main list of toots -# (column). -# default=row -list-split=row - -# You can change the proportions of the list view in relation to the content -# view list-proportion=1 and content-proportoin=3 will result in the content -# taking up 3 times more space. -# Must be n > 0 -# default=1 -list-proportion=1 - -# See list-proportion -# default=2 -content-proportion=2 - -# Hide notifications of this type. If you have multiple you separate them with a -# comma. Valid types: mention, status, boost, follow, follow_request, favorite, -# poll, edit. -# default= -notifications-to-hide= - -# If you always want to quote original message when replying. -# default=false -quote-reply=false - -# If you want to show icons in the list of toots. -# default=true -show-icons=true - -# If you've learnt all the shortcut keys you can remove the help text and only -# show the key in tui. So it gets less cluttered. -# default=false -short-hints=false - -# If you want to display the filter that filtered a toot. -# default=true -show-filter-phrase=true - -# If you want to show a message in the cmdbar on how to access the help text. -# default=true -show-help=true - -# If you always want tut to jump to the newest post. May ruin your reading -# experience. -# default=false -stick-to-top=false - -# If you want to display the username of the person being boosted instead of the -# person that boosted. -# default=false -show-boosted-user=false - -# 0 = No terminal title -# 1 = Show title in terminal and top bar -# 2 = Only show terminal title, and no top bar in tut. -# default=0 -terminal-title=0 - -# If you don't want the whole UI to update, and only the text content you can -# set this option to true. This will lead to some artifacts being left on the -# screen when emojis are present. But it will keep the UI from flashing on every -# single toot in some terminals. -# default=true -redraw-ui=true - -# The leader is used as a shortcut to run commands as you can do in Vim. By -# default this is disabled and you enable it by setting a leader-key. It can -# only consist of one char and I like to use comma as leader key. So to set it -# you write leader-key=, -# default= -leader-key= - -# Number of milliseconds before the leader command resets. So if you tap the -# leader-key by mistake or are to slow it empties all the input after X -# milliseconds. -# default=1000 -leader-timeout=1000 - -# You set actions for the leader-key with one or more leader-action. It consists -# of two parts first the action then the shortcut. And they're separated by a -# comma. -# -# Available commands: blocking, bookmarks, boosts, clear-notifications, -# close-window, compose, direct, edit, favorited, favorites, federated, -# followers, following, history, home, list-placement, list-split, lists, local, -# mentions, move-window-left, move-window-right, move-window-up, -# move-window-down, move-window-home, move-window-end, muting, newer, -# notifications, preferences, profile, proportions, refetch, saved, special-all, -# special-boosts, special-replies, stick-to-top, switch, tag, tags, window -# -# The ones named special-* are the home timeline with only boosts and/or -# replies. All contains both, -boosts only boosts and -replies only replies. -# -# The shortcuts are up to you, but keep them quite short and make sure they -# don't collide. If you have one shortcut that is "f" and an other one that is -# "fav", the one with "f" will always run and "fav" will never run. -# -# Some special leaders: -# tag is special as you need to add the tag after, e.g. tag linux -# window is special as it's a shortcut for switching between the timelines -# you've set under general and they are zero indexed. window 0 = your first -# timeline, window 1 = your second and so on. -# list-placement as it takes the argument top, right, bottom or left -# list-split as it takes the argument column or row -# proportions takes the arguments [int] [int], where the first integer is the -# list and the other content, e.g. proportions 1 3. See list-proportion above -# for more information. -# switch let's you go to a timeline if it already exists, if it doesn't it will -# open the timeline in a new window. The syntax is almost the same as in -# timelines= and is displayed under the examples. -# -# Some examples: -# leader-action=local,lo -# leader-action=lists,li -# leader-action=federated,fed -# leader-action=direct,d -# leader-action=history,h -# leader-action=tag linux,tl -# leader-action=window 0,h -# leader-action=list-placement bottom,b -# leader-action=list-split column,c -# leader-action=proportions 1 3,3 -# -# Syntax for switch: -# leader-action=switch feed,shortcut,[name],[showBoosts],[showReplies] -# showBoosts can be either true or false and they are both optional. Here are -# some examples: -# -# leader-action=switch home,h,false,true -# leader-action=switch tag tut,tt -# - - -[media] -# Your image viewer. -# default=xdg-open -image-viewer=xdg-open - -# Open the image viewer in the same terminal as toot. Only for terminal based -# viewers. -# default=false -image-terminal=false - -# If images should open one by one e.g. "imv image.png" multiple times. If set -# to false all images will open at the same time like this "imv image1.png -# image2.png image3.png". Not all image viewers support this, so try it first. -# default=true -image-single=true - -# If you want to open the images in reverse order. In some image viewers this -# will display the images in the "right" order. -# default=false -image-reverse=false - -# Your video viewer. -# default=xdg-open -video-viewer=xdg-open - -# Open the video viewer in the same terminal as toot. Only for terminal based -# viewers. -# default=false -video-terminal=false - -# If videos should open one by one. See image-single. -# default=true -video-single=true - -# If you want your videos in reverse order. In some video apps this will play -# the files in the "right" order. -# default=false -video-reverse=false - -# Your audio viewer. -# default=xdg-open -audio-viewer=xdg-open - -# Open the audio viewer in the same terminal as toot. Only for terminal based -# viewers. -# default=false -audio-terminal=false - -# If audio should open one by one. See image-single. -# default=true -audio-single=true - -# If you want to play the audio files in reverse order. In some audio apps this -# will play the files in the "right" order. -# default=false -audio-reverse=false - -# Your web browser. -# default=xdg-open -link-viewer=xdg-open - -# Open the browser in the same terminal as toot. Only for terminal based -# browsers. -# default=false -link-terminal=false - -[open-custom] -# This sections allows you to set up to five custom programs to open URLs with. -# If the url points to an image, you can set c1-name to img and c1-use to imv. -# If the program runs in a terminal and you want to run it in the same terminal -# as tut. Set cX-terminal to true. The name will show up in the UI, so keep it -# short so all five fits. -# -# c1-name=name -# c1-use=program -# c1-terminal=false -# -# c2-name=name -# c2-use=program -# c2-terminal=false -# -# c3-name=name -# c3-use=program -# c3-terminal=false -# -# c4-name=name -# c4-use=program -# c4-terminal=false -# -# c5-name=name -# c5-use=program -# c5-terminal=false - -[open-pattern] -# Here you can set your own glob patterns for opening matching URLs in the -# program you want them to open up in. You could for example open Youtube videos -# in your video player instead of your default browser. -# -# You must name the keys foo-pattern, foo-use and foo-terminal, where use is the -# program that will open up the URL. To see the syntax for glob pattern you can -# follow this URL https://github.com/gobwas/glob#syntax. foo-terminal is if the -# program runs in the terminal and should open in the same terminal as tut -# itself. -# -# Example for youtube.com and youtu.be to open up in mpv instead of the browser. -# -# y1-pattern=*youtube.com/watch* -# y1-use=mpv -# y1-terminal=false -# -# y2-pattern=*youtu.be/* -# y2-use=mpv -# y2-terminal=false - -[desktop-notification] -# Notification when someone follows you. -# default=false -followers=false - -# Notification when someone favorites one of your toots. -# default=false -favorite=false - -# Notification when someone mentions you. -# default=false -mention=false - -# Notification when someone edits their toot. -# default=false -update=false - -# Notification when someone boosts one of your toots. -# default=false -boost=false - -# Notification of poll results. -# default=false -poll=false - -# Notification when there is new posts in current timeline. -# default=false -posts=false - -[style] -# All styles can be represented in their HEX value like #ffffff or with their -# name, so in this case white. The only special value is "default" which equals -# to transparent, so it will be the same color as your terminal. -# -# You can also use xrdb colors like this xrdb:color1 The program will use colors -# prefixed with an * first then look for URxvt or XTerm if it can't find any -# color prefixed with an asterisk. If you don't want tut to guess the prefix you -# can set the prefix yourself. If the xrdb color can't be found a preset color -# will be used. You'll have to set theme=none for this to work. - -# The xrdb prefix used for colors in .Xresources. -# default=guess -xrdb-prefix=guess - -# You can use some themes that comes bundled with tut. Check out the themes -# available on the URL below. If a theme is named "nord.ini" you just write -# theme=nord -# -# https://github.com/RasmusLindroth/tut/tree/master/config/themes -# -# You can also create a theme file in your config directory e.g. -# ~/.config/tut/themes/foo.ini and then set theme=foo. -# -# If you want to use your own theme but don't want to create a new file, set -# theme=none and then you can create your own theme below. -# default=default -theme=default - -# The background color used on most elements. -# default= -background= - -# The text color used on most of the text. -# default= -text= - -# The color to display subtle elements or subtle text. Like lines and help text. -# default= -subtle= - -# The color for errors or warnings -# default= -warning-text= - -# This color is used to display username. -# default= -text-special-one= - -# This color is used to display username and key hints. -# default= -text-special-two= - -# The color of the bar at the top -# default= -top-bar-background= - -# The color of the text in the bar at the top. -# default= -top-bar-text= - -# The color of the bar at the bottom -# default= -status-bar-background= - -# The color of the text in the bar at the bottom. -# default= -status-bar-text= - -# The color of the bar at the bottom in view mode. -# default= -status-bar-view-background= - -# The color of the text in the bar at the bottom in view mode. -# default= -status-bar-view-text= - -# The color of the text in the command bar at the bottom. -# default= -command-text= - -# Background of selected list items. -# default= -list-selected-background= - -# The text color of selected list items. -# default= -list-selected-text= - -# The background color of selected list items that are out of focus. -# default= -list-selected-inactive-background= - -# The text color of selected list items that are out of focus. -# default= -list-selected-inactive-text= - -# The main color of the text for key hints -# default= -controls-text= - -# The highlight color of for key hints -# default= -controls-highlight= - -# The background color in dropdowns and autocompletions -# default= -autocomplete-background= - -# The text color in dropdowns at autocompletions -# default= -autocomplete-text= - -# The background color for selected value in dropdowns and autocompletions -# default= -autocomplete-selected-background= - -# The text color for selected value in dropdowns and autocompletions -# default= -autocomplete-selected-text= - -# The background color on selected button and the text color of unselected -# buttons -# default= -button-color-one= - -# The text color on selected button and the background color of unselected -# buttons -# default= -button-color-two= - -# The background on named timelines. -# default= -timeline-name-background= - -# The text color on named timelines -# default= -timeline-name-text= - -[input] -# You can edit the keys for tut below. -# -# The syntax is a bit weird, but it works. And I'll try to explain it as well as -# I can. -# -# Example: -# status-favorite="[F]avorite","Un[F]avorite",'f','F' -# status-delete="[D]elete",'d','D' -# -# status-favorite and status-delete differs because favorite can be in two -# states, so you will have to add two key hints. -# Most keys will only have on key hint. Look at the default value for reference. -# -# Key hints must be in some of the following formats. Remember the quotation -# marks. -# "" = empty -# "[D]elete" = Delete with a highlighted D -# "Un[F]ollow" = UnFollow with a highlighted F -# "[Enter]" = Enter where everything is highlighted -# "Yan[K]" = YanK with a highlighted K -# -# After the hint (or hints) you must set the keys. You can do this in two ways, -# with single quotation marks or double ones. -# -# The single ones are for single chars like 'a', 'b', 'c' and double marks are -# for special keys like "Enter". Remember that they are case sensitive. -# -# To find the names of special keys you have to go to the following site and -# look for "var KeyNames = map[Key]string{" -# -# https://github.com/gdamore/tcell/blob/master/key.go - -# Keys for moving down -# default="",'j','J',"Down" -global-down="",'j','J',"Down" - -# Keys for moving up -# default="",'k','K',"Up" -global-up="",'k','K',"Up" - -# To select items -# default="","Enter" -global-enter="","Enter" - -# To go back -# default="[Esc]","Esc" -global-back="[Esc]","Esc" - -# To go back and exit Tut -# default="[Q]uit",'q','Q' -global-exit="[Q]uit",'q','Q' - -# Move to the top -# default="",'g',"Home" -main-home="",'g',"Home" - -# Move to the bottom -# default="",'G',"End" -main-end="",'G',"End" - -# Go to previous feed -# default="",'h','H',"Left" -main-prev-feed="",'h','H',"Left" - -# Go to next feed -# default="",'l','L',"Right" -main-next-feed="",'l','L',"Right" - -# Focus on the previous feed window -# default="","Backtab" -main-prev-window="","Backtab" - -# Focus on the next feed window -# default="","Tab" -main-next-window="","Tab" - -# Focus on the notification list -# default="[N]otifications",'n','N' -main-notification-focus="[N]otifications",'n','N' - -# Compose a new toot -# default="",'c','C' -main-compose="",'c','C' - -# Open avatar -# default="[A]vatar",'a','A' -status-avatar="[A]vatar",'a','A' - -# Boost a toot -# default="[B]oost","Un[B]oost",'b','B' -status-boost="[B]oost","Un[B]oost",'b','B' - -# Edit a toot -# default="[E]dit",'e','E' -status-edit="[E]dit",'e','E' - -# Delete a toot -# default="[D]elete",'d','D' -status-delete="[D]elete",'d','D' - -# Favorite a toot -# default="[F]avorite","Un[F]avorite",'f','F' -status-favorite="[F]avorite","Un[F]avorite",'f','F' - -# Open toots media files -# default="[M]edia",'m','M' -status-media="[M]edia",'m','M' - -# Open links -# default="[O]pen",'o','O' -status-links="[O]pen",'o','O' - -# Open poll -# default="[P]oll",'p','P' -status-poll="[P]oll",'p','P' - -# Reply to toot -# default="[R]eply",'r','R' -status-reply="[R]eply",'r','R' - -# Save/bookmark a toot -# default="[S]ave","Un[S]ave",'s','S' -status-bookmark="[S]ave","Un[S]ave",'s','S' - -# View thread -# default="[T]hread",'t','T' -status-thread="[T]hread",'t','T' - -# Open user profile -# default="[U]ser",'u','U' -status-user="[U]ser",'u','U' - -# Open the view mode -# default="[V]iew",'v','V' -status-view-focus="[V]iew",'v','V' - -# Yank the url of the toot -# default="[Y]ank",'y','Y' -status-yank="[Y]ank",'y','Y' - -# Show the content in a content warning -# default="Press [Z] to toggle cw",'z','Z' -status-toggle-cw="Press [Z] to toggle cw",'z','Z' - -# Show the content of a filtered toot -# default="Press [Z] to view filtered toot",'z','Z' -status-show-filtered="Press [Z] to view filtered toot",'z','Z' - -# View avatar -# default="[A]vatar",'a','A' -user-avatar="[A]vatar",'a','A' - -# Block the user -# default="[B]lock","Un[B]lock",'b','B' -user-block="[B]lock","Un[B]lock",'b','B' - -# Follow user -# default="[F]ollow","Un[F]ollow",'f','F' -user-follow="[F]ollow","Un[F]ollow",'f','F' - -# Follow user -# default="Follow [R]equest","Follow [R]equest",'r','R' -user-follow-request-decide="Follow [R]equest","Follow [R]equest",'r','R' - -# Mute user -# default="[M]ute","Un[M]ute",'m','M' -user-mute="[M]ute","Un[M]ute",'m','M' - -# Open links -# default="[O]pen",'o','O' -user-links="[O]pen",'o','O' - -# View user profile -# default="[U]ser",'u','U' -user-user="[U]ser",'u','U' - -# Open view mode -# default="[V]iew",'v','V' -user-view-focus="[V]iew",'v','V' - -# Yank the user URL -# default="[Y]ank",'y','Y' -user-yank="[Y]ank",'y','Y' - -# Open list -# default="[O]pen",'o','O' -list-open-feed="[O]pen",'o','O' - -# List all users in a list -# default="[U]sers",'u','U' -list-user-list="[U]sers",'u','U' - -# Add user to list -# default="[A]dd",'a','A' -list-user-add="[A]dd",'a','A' - -# Delete user from list -# default="[D]elete",'d','D' -list-user-delete="[D]elete",'d','D' - -# Open URL -# default="[O]pen",'o','O' -link-open="[O]pen",'o','O' - -# Yank the URL -# default="[Y]ank",'y','Y' -link-yank="[Y]ank",'y','Y' - -# Open tag feed -# default="[O]pen",'o','O' -tag-open-feed="[O]pen",'o','O' - -# Toggle follow on tag -# default="[F]ollow","Un[F]ollow",'f','F' -tag-follow="[F]ollow","Un[F]ollow",'f','F' - -# Edit content warning text on new toot -# default="[C]W text",'c','C' -compose-edit-cw="[C]W text",'c','C' - -# Edit the text on new toot -# default="[E]dit text",'e','E' -compose-edit-text="[E]dit text",'e','E' - -# Include a quote when replying -# default="[I]nclude quote",'i','I' -compose-include-quote="[I]nclude quote",'i','I' - -# Focus on adding media to toot -# default="[M]edia",'m','M' -compose-media-focus="[M]edia",'m','M' - -# Post the new toot -# default="[P]ost",'p','P' -compose-post="[P]ost",'p','P' - -# Toggle content warning on toot -# default="[T]oggle CW",'t','T' -compose-toggle-content-warning="[T]oggle CW",'t','T' - -# Edit the visibility on new toot -# default="[V]isibility",'v','V' -compose-visibility="[V]isibility",'v','V' - -# Edit the language of a toot -# default="[L]ang",'l','L' -compose-language="[L]ang",'l','L' - -# Switch to creating a poll -# default="P[O]ll",'o','O' -compose-poll="P[O]ll",'o','O' - -# Delete media file -# default="[D]elete",'d','D' -media-delete="[D]elete",'d','D' - -# Edit the description on media file -# default="[E]dit desc",'e','E' -media-edit-desc="[E]dit desc",'e','E' - -# Add a new media file -# default="[A]dd",'a','A' -media-add="[A]dd",'a','A' - -# Vote on poll -# default="[V]ote",'v','V' -vote-vote="[V]ote",'v','V' - -# Select item to vote on -# default="[Enter] to select",' ', "Enter" -vote-select="[Enter] to select",' ', "Enter" - -# Add a new poll option -# default="[A]dd",'a','A' -poll-add="[A]dd",'a','A' - -# Edit a poll option -# default="[E]dit",'e','E' -poll-edit="[E]dit",'e','E' - -# Delete a poll option -# default="[D]elete",'d','D' -poll-delete="[D]elete",'d','D' - -# Toggle voting on multiple options -# default="Toggle [M]ultiple",'m','M' -poll-multi-toggle="Toggle [M]ultiple",'m','M' - -# Change the expiration of poll -# default="E[X]pires",'x','X' -poll-expiration="E[X]pires",'x','X' - -# Change display name -# default="[N]ame",'n','N' -preference-name="[N]ame",'n','N' - -# Change default visibility of toots -# default="[V]isibility",'v','V' -preference-visibility="[V]isibility",'v','V' - -# Change bio in profile -# default="[B]io",'b','B' -preference-bio="[B]io",'b','B' - -# Save your preferences -# default="[S]ave",'s','S' -preference-save="[S]ave",'s','S' - -# Edit profile fields -# default="[F]ields",'f','F' -preference-fields="[F]ields",'f','F' - -# Add new field -# default="[A]dd",'a','A' -preference-fields-add="[A]dd",'a','A' - -# Edit current field -# default="[E]dit",'e','E' -preference-fields-edit="[E]dit",'e','E' - -# Delete current field -# default="[D]elete",'d','D' -preference-fields-delete="[D]elete",'d','D' diff --git a/config.example.toml b/config.example.toml new file mode 100644 index 0000000..ce39c5c --- /dev/null +++ b/config.example.toml @@ -0,0 +1,1268 @@ +# Configuration file for tut + +[general] +# What editor to use. TUT_USE_INTERNAL will use the editor that comes with tut. +# If you want you can set this to $EDITOR to use your environment variable or +# vim if you want to specify the program directly. +# default="TUT_USE_INTERNAL" +editor="TUT_USE_INTERNAL" + +# You need to press yes in a confirmation dialog before favoriting, boosting, +# etc. +# default=true +confirmation=true + +# Enable mouse support in tut. +# default=false +mouse-support=false + +# The date format to be used. See https://pkg.go.dev/time#pkg-constants +# default="2006-01-02 15:04" +date-format="2006-01-02 15:04" + +# Format for dates the same day. See date-format for more info. +# default="15:04" +date-tody-format="15:04" + +# This displays relative dates instead for statuses that are one day or older +# the output is 1y2m1d (1 year 2 months and 1 day) +# +# The value is an integear +# -1 = don't use relative dates +# 0 = always use relative dates, except for dates < 1 day +# 1 - ∞ = number of days to use relative dates +# +# Value: 28 will display a relative date for toots that are between 1-28 days +# old. Otherwhise it will use the short or long format. +# default=-1 +date-relative=-1 + +# The max with of text before it wraps when displaying a toot. +# default=0 +max-width=0 + +# The placement of your panes. +# valid: left, right, top, bottom +# default="left" +list-placement="left" + +# How should panes be split? +# valid: row, column +# default="row" +list-split="row" + +# The proportion of panes vs. content. 1 on this and 3 on content below results +# in content taking up 3 times more space. +# default=1 +list-proportion=1 + +# See previous. +# default=2 +content-proportion=2 + +# Hide notifications of this type in your notification timelines. +# valid: mention, status, boost, follow, follow_request, favorite, poll, edit +# default=[] +notifications-to-hide=[] + +# Always include a quote of the message you're replying to. +# default=false +quote-reply=false + +# If you want to show icons in timelines. +# default=true +show-icons=true + +# If you only want to you the letter of keys instead of the full hint. +# default=false +short-hints=false + +# If you want to display the filter that filtered a toot. +# default=true +show-filter-phrase=true + +# Display a message in the commandbar on how to access the help text. +# default=true +show-help=true + +# Always jump to the newest post. May ruin your reading experience. +# default=false +stick-to-top=false + +# Display the username of the person being boosted insted of the person that +# boosted. +# default=false +show-boosted-user=false + +# Open a new pane when you run a command like :timeline home. +# default=true +commands-in-new-pane=true + +# Set a default name for the timeline if the name is empty. So if you run :tag +# linux the title of the pane will be set to #linux +# default=true +dynamic-timeline-name=true + +# 0 = No terminal title +# 1 = Show title in terminal and top bar +# 2 = Only show terminal title, and no top bar in tut +# 3 = No terminal title and no top bar in tut. +# valid: 0, 1, 2, 4 +# default=0 +terminal-title=0 + +# If you don't want the whole UI to update, and only update the text content you +# can disable this. This will lead to some artifacts being left on the screen +# when emojis are present. +# default=true +redraw-ui=true + +# The leader is used as a shortcut to run commands as you can do in Vim. By +# default this is disabled and you enable it by setting a key here. It can only +# consist of one char, so set it to something like a comma. +# default="" +leader-key="" + +# Number of milliseconds before the leader command resets. So if you tap the +# leader-key by mistake or are to slow it empties all the input after X +# milliseconds. +# default=1000 +leader-timeout=1000 + +# [[general.timelines]] +# Timelines adds panes of feeds. You can customize the number of feeds, what +# they should show and the key to activate them. + +# Example: +# [[general.timelines]] +# name="home" +# type="home" +# hide-boosts=false +# hide-replies=false +# +# [[general.timelines]] +# name="Notifications" +# type="notifications" +# keys=["n", "N"] +# closed=true +# on-creation-closed="new-pane" +# on-focus="focus-self" + +# The name to display above the timeline +# default="" +# name="" + +# The type of the timeline +# valid: home, direct, local, federated, bookmarks, saved, favorited, notifications, +# lists, mentions, tag +# default="" +# type="" + +# Used for the tag type, so here you set the tag. If you have multiple you +# seperate them with a space. +# default="" +# data="" + +# A list of keys to give this timeline focus. See under the input section to +# learn more about keys. +# default=[] +# keys=[] + +# A list of special-keys to give this timeline focus. See under the input +# section to learn more about special-keys. +# default=[] +# special-keys=[] + +# A shortcut to give this timeline focus with your leader-key + this shortcut. +# default="" +# shortcut="" + +# Hide boosts in this timeline. +# default="false" +# hide-boosts="false" + +# Hide replies in this timeline. +# default="false" +# hide-replies="false" + +# Don't open this timeline when you start tut. Use your keys or shortcut to open +# it. +# default="false" +# closed="false" + +# Don't open this timeline when you start tut. Use your keys or shortcut to open +# it. +# valid: new-pane, current-pane +# default="new-pane" +# on-creation-closed="new-pane" + +# Don't open this timeline when you start tut. Use your keys or shortcut to open +# it. +# valid: focus-pane, focus-self +# default="focus-pane" +# on-focus="focus-pane" + +# [[general.leader-actions]] +# You set actions leader-key with one or more leader-actions. +# +# The shortcuts are up to you, but keep them quite short and make sure they +# don't collide. If you have one shortcut that is "f" and an other one that is +# "fav", the one with "f" will always run and "fav" will never run. +# +# Some special actions that requires data to be set: +# pane is special as it's a shortcut for switching between the panes you've set +# under general and they are zero indexed. pane 0 = your first timeline, pane 1 +# = your second and so on. +# list-placement as it takes the argument top, right, bottom or left +# list-split as it takes the argument column or row +# proportions takes the arguments [int] [int], where the first integer is the +# list and the other content, e.g. proportions 1 3. See list-proportion above +# for more information. + +# Example: +# [[general.leader-actions]] +# type="close-pane" +# shortcut="q" +# +# [[general.leader-actions]] +# type="list-split" +# data="row" +# shortcut="r" +# +# [[general.leader-actions]] +# type="list-split" +# data="column" +# shortcut="c" +# + +# The action you want to run. +# valid: blocking, boosts, clear-notifications, close-pane, compose, edit, favorited, +# favorites, followers, following, history, list-placement, list-split, lists, +# move-pane-left, move-pane-right, move-pane-up, move-pane-down, move-pane-home, +# move-pane-end, muting, newer, pane, preferences, profile, proportions, +# refetch, stick-to-top, tags +# default="" +# type="" + +# Data to pass to the action. +# default="" +# data="" + +# A shortcut to run this action with your leader-key + this shortcut. +# default="" +# shortcut="" + +[media] +# Media files will be removed directly after they've been opened. Some programs +# doesn't like this, so if your media doesn't open, try set this to false. Tut +# will remove all files once you close the program. +# default=true +delete-temp-files=true + +[media.image] +# The program to open images. TUT_OS_DEFAULT equals xdg-open on Linux, open on +# MacOS and start on Windows. +# default="TUT_OS_DEFAULT" +program="TUT_OS_DEFAULT" + +# Arguments to pass to the program. +# default="" +args="" + +# If the program runs in the terminal set this to true. +# default=false +terminal=false + +# If the program should be called multiple times when there is multiple files. +# If set to false all files will be passed as an argument, but not all programs +# support this. +# default=true +single=true + +# If the files should be passed in reverse order. This will make some programs +# display the files in the correct order. +# default=false +reverse=false + +[media.video] +# The program to open videos. TUT_OS_DEFAULT equals xdg-open on Linux, open on +# MacOS and start on Windows. +# default="TUT_OS_DEFAULT" +program="TUT_OS_DEFAULT" + +# Arguments to pass to the program. +# default="" +args="" + +# If the program runs in the terminal set this to true. +# default=false +terminal=false + +# If the program should be called multiple times when there is multiple files. +# If set to false all files will be passed as an argument, but not all programs +# support this. +# default=true +single=true + +# If the files should be passed in reverse order. This will make some programs +# display the files in the correct order. +# default=false +reverse=false + +[media.audio] +# The program to open audio. TUT_OS_DEFAULT equals xdg-open on Linux, open on +# MacOS and start on Windows. +# default="TUT_OS_DEFAULT" +program="TUT_OS_DEFAULT" + +# Arguments to pass to the program. +# default="" +args="" + +# If the program runs in the terminal set this to true. +# default=false +terminal=false + +# If the program should be called multiple times when there is multiple files. +# If set to false all files will be passed as an argument, but not all programs +# support this. +# default=true +single=true + +# If the files should be passed in reverse order. This will make some programs +# display the files in the correct order. +# default=false +reverse=false + +[media.link] +# The program to open links. TUT_OS_DEFAULT equals xdg-open on Linux, open on +# MacOS and start on Windows. +# default="TUT_OS_DEFAULT" +program="TUT_OS_DEFAULT" + +# Arguments to pass to the program. +# default="" +args="" + +# If the program runs in the terminal set this to true. +# default=false +terminal=false + +[desktop-notification] +# Enable notifications when someone follows you. +# default=false +followers=false + +# Enable notifications when one of your toots gets favorited. +# default=false +favorite=false + +# Enable notifications when someone mentions you. +# default=false +mention=false + +# Enable notifications when a post you have interacted with gets edited. +# default=false +update=false + +# Enable notifications when one of your toots gets boosted. +# default=false +boost=false + +# Enable notifications when a poll ends. +# default=false +poll=false + +# Enable notifications for new posts. +# default=false +posts=false + +[open-custom] +# [[open-custom.programs]] +# The program to open the file with. +# default="" +# program="" + +# Arguments to pass to the program. +# default="" +# args="" + +# If the program runs in the terminal set this to true. +# default=false +# terminal=false + +# What should the key hint in tut be for this program. See under the input +# section to learn more about hint. +# default="" +# hint="" + +# A list of keys to to open files with this program. See under the input section +# to learn more about keys. +# default=[] +# keys=[] + +# A list of special-keys to open files with this program. See under the input +# section to learn more about special-keys. +# default=[] +# special-keys=[] + +[open-pattern] +# [[open-pattern.programs]] +# Here you can set your own glob patterns for opening matching URLs in the +# program you want them to open up in. You could for example open Youtube videos +# in your video player instead of your default browser. To see the syntax for +# glob pattern you can follow this URL https://github.com/gobwas/glob#syntax. +# default="" +# matching="" + +# The program to open the file with. +# default="" +# program="" + +# Arguments to pass to the program. +# default="" +# args="" + +# If the program runs in the terminal set this to true. +# default=false +# terminal=false + +[style] +# All styles can be represented in their HEX value like #ffffff or with their +# name, so in this case white. The only special value is "default" which equals +# to transparent, so it will be the same color as your terminal. +# You can also use xrdb colors like this xrdb:color1 The program will use colors +# prefixed with an * first then look for URxvt or XTerm if it can't find any +# color prefixed with an asterisk. If you don't want tut to guess the prefix you +# can set the prefix yourself. If the xrdb color can't be found a preset color +# will be used. You'll have to set theme="none" for this to work. + +# The theme to use. You can use some themes that comes bundled with tut. Check +# out the themes available on the URL below. If a theme is named nord.toml you +# just write theme="nord". +# +# https://github.com/RasmusLindroth/tut/tree/master/config/themes +# +# You can also create a theme file in your config directory e.g. +# ~/.config/tut/themes/foo.toml and then set theme=foo. +# +# If you want to use your own theme but don't want to create a new file, set +# theme="none" and then you can create your own theme below. +# +# default="default" +theme="default" + +# The xrdb prefix used for colors in .Xresources. +# default="guess" +xrdb-prefix="guess" + +# The background color used on most elements. +# default="" +background="" + +# The text color used on most of the text. +# default="" +text="" + +# The color to display subtle elements or subtle text. Like lines and help text. +# default="" +subtle="" + +# The color for errors or warnings +# default="" +warning-text="" + +# This color is used to display username. +# default="" +text-special-one="" + +# This color is used to display username and key hints. +# default="" +text-special-two="" + +# The color of the bar at the top +# default="" +top-bar-background="" + +# The color of the text in the bar at the top. +# default="" +top-bar-text="" + +# The color of the bar at the bottom +# default="" +status-bar-background="" + +# The color of the text in the bar at the bottom. +# default="" +status-bar-text="" + +# The color of the bar at the bottom in view mode. +# default="" +status-bar-view-background="" + +# The color of the text in the bar at the bottom in view mode. +# default="" +status-bar-view-text="" + +# The color of the text in the command bar at the bottom. +# default="" +command-text="" + +# Background of selected list items. +# default="" +list-selected-background="" + +# The text color of selected list items. +# default="" +list-selected-text="" + +# The background color of selected list items that are out of focus. +# default="" +list-selected-inactive-background="" + +# The text color of selected list items that are out of focus. +# default="" +list-selected-inactive-text="" + +# The main color of the text for key hints +# default="" +controls-text="" + +# The highlight color of for key hints +# default="" +controls-highlight="" + +# The background color in dropdowns and autocompletions +# default="" +autocomplete-background="" + +# The text color in dropdowns at autocompletions +# default="" +autocomplete-text="" + +# The background color for selected value in dropdowns and autocompletions +# default="" +autocomplete-selected-background="" + +# The text color for selected value in dropdowns and autocompletions +# default="" +autocomplete-selected-text="" + +# The background color on selected button and the text color of unselected +# buttons +# default="" +button-color-one="" + +# The text color on selected button and the background color of unselected +# buttons +# default="" +button-color-two="" + +# The background on named timelines. +# default="" +timeline-name-background="" + +# The text color on named timelines +# default="" +timeline-name-text="" + +[input] +# In this section you set the keys to be used in tut. +# +# The hint option lets you set which part of the hint that will be highlighted +# in tut. E.g. [F]avorite results in a highlighted F and the rest of the text is +# displayed normaly. +# Some of the options can be in two states, like favorites, so there you can set +# the hint-alt option to something like Un[F]avorite. +# +# Examples: +# "[D]elete" = Delete with a highlighted D +# "Un[F]ollow" = UnFollow with a highlighted F +# "[Enter]" = Enter where everything is highlighted +# "Yan[K]" = YanK with a highlighted K +# +# The keys option lets you define what key that should be pressed. This is +# limited to on character only and they are case sensetive. +# Example: +# keys=["j","J"] +# +# You can also set special-keys and they're for keys like Escape and Enter. To +# find the names of special keys you have to go to the following site and look +# for "var KeyNames = map[Key]string{" +# +# https://github.com/gdamore/tcell/blob/master/key.go + +[input.global-down] +# Keys for moving down + +# default=["j", "J"] +keys=["j","J"] + +# default=["Down"] +special-keys=["Down"] + +[input.global-up] +# Keys for moving down + +# default=["k", "K"] +keys=["k","K"] + +# default=["Up"] +special-keys=["Up"] + +[input.global-enter] +# To select items + +# default=["Enter"] +special-keys=["Enter"] + +[input.global-back] +# To go back + +# default="[Esc]" +hint="[Esc]" + +# default=["Esc"] +special-keys=["Esc"] + +[input.global-exit] +# To go back or exit + +# default="[Q]uit" +hint="[Q]uit" + +# default=["q", "Q"] +keys=["q","Q"] + +[input.main-home] +# Move to the top + +# default=["g"] +keys=["g"] + +# default=["Home"] +special-keys=["Home"] + +[input.main-end] +# Move to the bottom + +# default=["G"] +keys=["G"] + +# default=["End"] +special-keys=["End"] + +[input.main-prev-feed] +# Go to previous feed + +# default=["h", "H"] +keys=["h","H"] + +# default=["Left"] +special-keys=["Left"] + +[input.main-next-feed] +# Go to next feed + +# default=["l", "L"] +keys=["l","L"] + +# default=["Right"] +special-keys=["Right"] + +[input.main-prev-pane] +# Focus on the previous feed pane + +# default=["Backtab"] +special-keys=["Backtab"] + +[input.main-next-pane] +# Focus on the next feed pane + +# default=["Tab"] +special-keys=["Tab"] + +[input.main-next-account] +# Focus on the next account + +# default=["Ctrl-N"] +special-keys=["Ctrl-N"] + +[input.main-prev-account] +# Focus on the previous account + +# default=["Ctrl-P"] +special-keys=["Ctrl-P"] + +[input.main-compose] +# Compose a new toot + +# default=["c", "C"] +keys=["c","C"] + +[input.status-avatar] +# Open avatar + +# default="[A]vatar" +hint="[A]vatar" + +# default=["a", "A"] +keys=["a","A"] + +[input.status-boost] +# Boost a toot + +# default="[B]oost" +hint="[B]oost" + +# default=["b", "B"] +keys=["b","B"] + +[input.status-edit] +# Edit a toot + +# default="[E]dit" +hint="[E]dit" + +# default=["e", "E"] +keys=["e","E"] + +[input.status-delete] +# Delete a toot + +# default="[D]elete" +hint="[D]elete" + +# default=["d", "D"] +keys=["d","D"] + +[input.status-favorite] +# Favorite a toot + +# default="[F]avorite" +hint="[F]avorite" + +# default=["f", "F"] +keys=["f","F"] + +[input.status-media] +# Open toots media files + +# default="[M]edia" +hint="[M]edia" + +# default=["m", "M"] +keys=["m","M"] + +[input.status-links] +# Open links + +# default="[O]pen" +hint="[O]pen" + +# default=["o", "O"] +keys=["o","O"] + +[input.status-poll] +# Open poll + +# default="[P]oll" +hint="[P]oll" + +# default=["p", "P"] +keys=["p","P"] + +[input.status-reply] +# Reply to toot + +# default="[R]eply" +hint="[R]eply" + +# default=["r", "R"] +keys=["r","R"] + +[input.status-bookmark] +# Save/bookmark a toot + +# default="[S]ave" +hint="[S]ave" + +# default="Un[S]ave" +hint-alt="Un[S]ave" + +# default=["s", "S"] +keys=["s","S"] + +[input.status-thread] +# View thread + +# default="[T]hread" +hint="[T]hread" + +# default=["t", "T"] +keys=["t","T"] + +[input.status-user] +# Open user profile + +# default="[U]ser" +hint="[U]ser" + +# default=["u", "U"] +keys=["u","U"] + +[input.status-view-focus] +# Open the view mode + +# default="[V]iew" +hint="[V]iew" + +# default=["v", "V"] +keys=["v","V"] + +[input.status-yank] +# Yank the url of the toot + +# default="[Y]ank" +hint="[Y]ank" + +# default=["y", "Y"] +keys=["y","Y"] + +[input.status-toggle-cw] +# Show the content in a content warning + +# default="Press [Z] to toggle cw" +hint="Press [Z] to toggle cw" + +# default=["z", "Z"] +keys=["z","Z"] + +[input.status-show-filtered] +# Show the content of a filtered toot + +# default="Press [Z] to view filtered toot" +hint="Press [Z] to view filtered toot" + +# default=["z", "Z"] +keys=["z","Z"] + +[input.user-avatar] +# View avatar + +# default="[A]vatar" +hint="[A]vatar" + +# default=["a", "A"] +keys=["a","A"] + +[input.user-block] +# Block the user + +# default="[B]lock" +hint="[B]lock" + +# default="Un[B]lock" +hint-alt="Un[B]lock" + +# default=["b", "B"] +keys=["b","B"] + +[input.user-follow] +# Follow user + +# default="[F]ollow" +hint="[F]ollow" + +# default="Un[F]ollow" +hint-alt="Un[F]ollow" + +# default=["f", "F"] +keys=["f","F"] + +[input.user-follow-request-decide] +# Follow user + +# default="Follow [R]equest" +hint="Follow [R]equest" + +# default="Follow [R]equest" +hint-alt="Follow [R]equest" + +# default=["r", "R"] +keys=["r","R"] + +[input.user-mute] +# Mute user + +# default="[M]ute" +hint="[M]ute" + +# default="Un[M]ute" +hint-alt="Un[M]ute" + +# default=["m", "M"] +keys=["m","M"] + +[input.user-links] +# Open links + +# default="[O]pen" +hint="[O]pen" + +# default=["o", "O"] +keys=["o","O"] + +[input.user-user] +# View user profile + +# default="[U]ser" +hint="[U]ser" + +# default=["u", "U"] +keys=["u","U"] + +[input.user-view-focus] +# Open view mode + +# default="[V]iew" +hint="[V]iew" + +# default=["v", "V"] +keys=["v","V"] + +[input.user-yank] +# Yank the user URL + +# default="[Y]ank" +hint="[Y]ank" + +# default=["y", "Y"] +keys=["y","Y"] + +[input.list-open-feed] +# Open list + +# default="[O]pen" +hint="[O]pen" + +# default=["o", "O"] +keys=["o","O"] + +[input.list-user-list] +# List all users in a list + +# default="[U]sers" +hint="[U]sers" + +# default=["u", "U"] +keys=["u","U"] + +[input.list-user-add] +# Add user to list + +# default="[A]dd" +hint="[A]dd" + +# default=["a", "A"] +keys=["a","A"] + +[input.list-user-delete] +# Delete user from list + +# default="[D]elete" +hint="[D]elete" + +# default=["d", "D"] +keys=["d","D"] + +[input.link-open] +# Open URL + +# default="[O]pen" +hint="[O]pen" + +# default=["o", "O"] +keys=["o","O"] + +[input.link-yank] +# Yank the URL + +# default="[Y]ank" +hint="[Y]ank" + +# default=["y", "Y"] +keys=["y","Y"] + +[input.tag-open-feed] +# Open tag feed + +# default="[O]pen" +hint="[O]pen" + +# default=["o", "O"] +keys=["o","O"] + +[input.tag-follow] +# Toggle follow on tag + +# default="[F]ollow" +hint="[F]ollow" + +# default="Un[F]ollow" +hint-alt="Un[F]ollow" + +# default=["f", "F"] +keys=["f","F"] + +[input.compose-edit-cw] +# Edit content warning text on new toot + +# default="[C]W text" +hint="[C]W text" + +# default=["c", "C"] +keys=["c","C"] + +[input.compose-edit-text] +# Edit the text on new toot + +# default="[E]dit text" +hint="[E]dit text" + +# default=["e", "E"] +keys=["e","E"] + +[input.compose-include-quote] +# Include a quote when replying + +# default="[I]nclude quote" +hint="[I]nclude quote" + +# default=["i", "I"] +keys=["i","I"] + +[input.compose-media-focus] +# Focus on adding media to toot + +# default="[M]edia" +hint="[M]edia" + +# default=["m", "M"] +keys=["m","M"] + +[input.compose-post] +# Post the new toot + +# default="[P]ost" +hint="[P]ost" + +# default=["p", "P"] +keys=["p","P"] + +[input.compose-toggle-content-warning] +# Toggle content warning on toot + +# default="[T]oggle CW" +hint="[T]oggle CW" + +# default=["t", "T"] +keys=["t","T"] + +[input.compose-visibility] +# Edit the visibility on new toot + +# default="[V]isibility" +hint="[V]isibility" + +# default=["v", "V"] +keys=["v","V"] + +[input.compose-language] +# Edit the language of a toot + +# default="[L]ang" +hint="[L]ang" + +# default=["l", "L"] +keys=["l","L"] + +[input.compose-poll] +# Switch to creating a poll + +# default="P[O]ll" +hint="P[O]ll" + +# default=["o", "O"] +keys=["o","O"] + +[input.media-delete] +# Delete media file + +# default="[D]elete" +hint="[D]elete" + +# default=["d", "D"] +keys=["d","D"] + +[input.media-edit-desc] +# Edit the description on media file + +# default="[E]dit desc" +hint="[E]dit desc" + +# default=["e", "E"] +keys=["e","E"] + +[input.media-add] +# Add a new media file + +# default="[A]dd" +hint="[A]dd" + +# default=["a", "A"] +keys=["a","A"] + +[input.vote-vote] +# Vote on poll + +# default="[V]ote" +hint="[V]ote" + +# default=["v", "V"] +keys=["v","V"] + +[input.vote-select] +# Select item to vote on + +# default="[Enter] to select" +hint="[Enter] to select" + +# default=["Enter"] +special-keys=["Enter"] + +[input.poll-add] +# Add a new poll option + +# default="[A]dd" +hint="[A]dd" + +# default=["a", "A"] +keys=["a","A"] + +[input.poll-edit] +# Edit a poll option + +# default="[E]dit" +hint="[E]dit" + +# default=["e", "E"] +keys=["e","E"] + +[input.poll-delete] +# Delete a poll option + +# default="[D]elete" +hint="[D]elete" + +# default=["d", "D"] +keys=["d","D"] + +[input.poll-multi-toggle] +# Toggle voting on multiple options + +# default="Toggle [M]ultiple" +hint="Toggle [M]ultiple" + +# default=["m", "M"] +keys=["m","M"] + +[input.poll-expiration] +# Change the expiration of poll + +# default="E[X]pires" +hint="E[X]pires" + +# default=["x", "X"] +keys=["x","X"] + +[input.preference-name] +# Change display name + +# default="[N]ame" +hint="[N]ame" + +# default=["n", "N"] +keys=["n","N"] + +[input.preference-visibility] +# Change default visibility of toots + +# default="[V]isibility" +hint="[V]isibility" + +# default=["v", "V"] +keys=["v","V"] + +[input.preference-bio] +# Change bio in profile + +# default="[B]io" +hint="[B]io" + +# default=["b", "B"] +keys=["b","B"] + +[input.preference-save] +# Save your preferences + +# default="[S]ave" +hint="[S]ave" + +# default=["s", "S"] +keys=["s","S"] + +[input.preference-fields] +# Edit profile fields + +# default="[F]ields" +hint="[F]ields" + +# default=["f", "F"] +keys=["f","F"] + +[input.preference-fields-add] +# Add new field + +# default="[A]dd" +hint="[A]dd" + +# default=["a", "A"] +keys=["a","A"] + +[input.preference-fields-edit] +# Edit current field + +# default="[E]dit" +hint="[E]dit" + +# default=["e", "E"] +keys=["e","E"] + +[input.preference-fields-delete] +# Delete current field + +# default="[D]elete" +hint="[D]elete" + +# default=["d", "D"] +keys=["d","D"] + +[input.editor-exit] +# Exit the editor + +# default="[Esc] when done" +hint="[Esc] when done" + +# default=["Esc"] +special-keys=["Esc"] diff --git a/config/config.go b/config/config.go index ea08cc6..894e556 100644 --- a/config/config.go +++ b/config/config.go @@ -2,7 +2,6 @@ package config import ( "embed" - "errors" "fmt" "io" "log" @@ -10,13 +9,13 @@ import ( "path/filepath" "regexp" "strings" + "sync" "text/template" "github.com/RasmusLindroth/tut/util" "github.com/gdamore/tcell/v2" "github.com/gobwas/glob" - "golang.org/x/exp/slices" - "gopkg.in/ini.v1" + "github.com/pelletier/go-toml/v2" ) //go:embed toot.tmpl @@ -52,47 +51,33 @@ type LeaderCommand uint const ( LeaderNone LeaderCommand = iota - LeaderHome - LeaderDirect - LeaderLocal - LeaderFederated - LeaderSpecialAll - LeaderSpecialBoosts - LeaderSpecialReplies LeaderClearNotifications LeaderCompose LeaderEdit LeaderBlocking - LeaderBookmarks - LeaderSaved LeaderFavorited LeaderBoosts LeaderFavorites LeaderFollowing LeaderFollowers + LeaderTags LeaderListPlacement LeaderListSplit LeaderMuting LeaderPreferences LeaderProfile LeaderProportions - LeaderNotifications LeaderMentions - LeaderLists LeaderRefetch - LeaderTag - LeaderTags LeaderStickToTop LeaderHistory - LeaderUser LeaderLoadNewer - LeaderWindow - LeaderCloseWindow - LeaderSwitch - LeaderMoveWindowLeft - LeaderMoveWindowRight - LeaderMoveWindowHome - LeaderMoveWindowEnd + LeaderPane + LeaderClosePane + LeaderMovePaneLeft + LeaderMovePaneRight + LeaderMovePaneHome + LeaderMovePaneEnd ) type FeedType uint @@ -140,23 +125,59 @@ const ( HideEdited NotificationToHide = "update" ) +var timelineID uint = 0 +var timelineIDMux sync.Mutex + +func newTimelineID() uint { + timelineIDMux.Lock() + defer timelineIDMux.Unlock() + timelineID = timelineID + 1 + return timelineID +} + +func NewTimeline(tl Timeline) *Timeline { + tl.ID = newTimelineID() + return &tl +} + +type OnTimelineFocus uint + +const ( + TimelineFocusPane OnTimelineFocus = iota + TimelineFocusTimeline +) + +type OnTimelineCreationClosed uint + +const ( + TimelineCreationClosedNewPane OnTimelineCreationClosed = iota + TimelineCreationClosedCurrentPane +) + type Timeline struct { + ID uint FeedType FeedType Subaction string Name string Key Key - ShowBoosts bool - ShowReplies bool + Shortcut string + HideBoosts bool + HideReplies bool + + Closed bool + OnFocus OnTimelineFocus + OnCreationClosed OnTimelineCreationClosed } type General struct { + Editor string + UseInternalEditor bool Confirmation bool MouseSupport bool DateTodayFormat string DateFormat string DateRelative int MaxWidth int - NotificationFeed bool QuoteReply bool ShortHints bool ShowFilterPhrase bool @@ -171,11 +192,12 @@ type General struct { LeaderKey rune LeaderTimeout int64 LeaderActions []LeaderAction - TimelineName bool - Timelines []Timeline + Timelines []*Timeline StickToTop bool NotificationsToHide []NotificationToHide ShowBoostedUser bool + DynamicTimelineName bool + CommandsInNewPane bool } type Style struct { @@ -226,29 +248,28 @@ type Style struct { } type Media struct { - ImageViewer string - ImageArgs []string - ImageTerminal bool - ImageSingle bool - ImageReverse bool - VideoViewer string - VideoArgs []string - VideoTerminal bool - VideoSingle bool - VideoReverse bool - AudioViewer string - AudioArgs []string - AudioTerminal bool - AudioSingle bool - AudioReverse bool - LinkViewer string - LinkArgs []string - LinkTerminal bool + DeleteTmpFiles bool + ImageViewer string + ImageArgs []string + ImageTerminal bool + ImageSingle bool + ImageReverse bool + VideoViewer string + VideoArgs []string + VideoTerminal bool + VideoSingle bool + VideoReverse bool + AudioViewer string + AudioArgs []string + AudioTerminal bool + AudioSingle bool + AudioReverse bool + LinkViewer string + LinkArgs []string + LinkTerminal bool } type Pattern struct { - Pattern string - Open string Compiled glob.Glob Program string Args []string @@ -265,6 +286,7 @@ type Custom struct { Program string Args []string Terminal bool + Key Key } type OpenCustom struct { OpenCustoms []Custom @@ -314,7 +336,35 @@ type Templates struct { Help *template.Template } -var keyMatch = regexp.MustCompile("^\"(.*?)\\[(.*?)\\](.*?)\"$") +func NilDefaultBool(x *bool, def *bool) bool { + if x == nil { + return *def + } + return *x +} + +func NilDefaultString(x *string, def *string) string { + if x == nil { + return *def + } + return *x +} + +func NilDefaultInt(x *int, def *int) int { + if x == nil { + return *def + } + return *x +} + +func NilDefaultInt64(x *int64, def *int64) int64 { + if x == nil { + return *def + } + return *x +} + +var keyMatch = regexp.MustCompile(`^(.*?)\[(.*?)\](.*?)$`) func newHint(s string) []string { matches := keyMatch.FindAllStringSubmatch(s, -1) @@ -327,60 +377,42 @@ func newHint(s string) []string { return []string{matches[0][1], matches[0][2], matches[0][3]} } -func NewKey(s []string, double bool) (Key, error) { - var k Key - if len(s) < 2 { - return k, errors.New("key must have a minimum length of 2") +func NewKey(hint string, hintAlt string, keys []string, special []string) (Key, error) { + k := Key{} + if len(hint) > 0 && len(hintAlt) > 0 { + k.Hint = [][]string{newHint(hint), newHint(hintAlt)} + } else if len(hint) > 0 { + k.Hint = [][]string{newHint(hint), newHint(hintAlt)} } - var start int - if double { - start = 1 - k = Key{ - Hint: [][]string{newHint(s[0]), newHint(s[1])}, + var runes []rune + var keysTcell []tcell.Key + for _, r := range keys { + if len(r) > 1 { + return k, fmt.Errorf("key %s can only be one char", r) } - } else { - start = 0 - k = Key{ - Hint: [][]string{newHint(s[0])}, + if len(r) == 0 { + continue } + runes = append(runes, rune(r[0])) } - var runes []rune - var keys []tcell.Key - for _, v := range s[start+1:] { - value := []rune(strings.TrimSpace(v)) - if len(value) < 3 { - return k, errors.New("key value must have a minimum length of 3") - } - if value[0] == '\'' { - if len(value) != 3 { - return k, fmt.Errorf("rune %s must only contain one char", string(value)) - } - runes = append(runes, value[1]) - } else if value[0] == '"' { - if value[len(value)-1] != '"' { - return k, fmt.Errorf("key %s must end with \"", string(value)) - } - keyName := string(value[1 : len(value)-1]) - found := false - var fk tcell.Key - for tk, tv := range tcell.KeyNames { - if tv == keyName { - found = true - fk = tk - break - } - } - if found { - keys = append(keys, fk) - } else { - return k, fmt.Errorf("no key named %s", keyName) + for _, s := range special { + found := false + var fk tcell.Key + for tk, tv := range tcell.KeyNames { + if tv == s { + found = true + fk = tk + break } + } + if found { + keysTcell = append(keysTcell, fk) } else { - return k, fmt.Errorf("input %s is in the wrong format", string(value)) + return k, fmt.Errorf("no key named %s", s) } } k.Runes = runes - k.Keys = keys + k.Keys = keysTcell return k, nil } @@ -412,13 +444,15 @@ type Input struct { GlobalBack Key GlobalExit Key - MainHome Key - MainEnd Key - MainPrevFeed Key - MainNextFeed Key - MainPrevWindow Key - MainNextWindow Key - MainCompose Key + MainHome Key + MainEnd Key + MainPrevFeed Key + MainNextFeed Key + MainPrevPane Key + MainNextPane Key + MainCompose Key + MainNextAccount Key + MainPrevAccount Key StatusAvatar Key StatusBoost Key @@ -489,6 +523,8 @@ type Input struct { PreferenceFieldsAdd Key PreferenceFieldsEdit Key PreferenceFieldsDelete Key + + EditorExit Key } func parseColor(input string, def string, xrdb map[string]string) tcell.Color { @@ -507,10 +543,142 @@ func parseColor(input string, def string, xrdb map[string]string) tcell.Color { return tcell.GetColor(input) } -func parseStyle(cfg *ini.File, cnfPath string, cnfDir string) Style { +func parseTheme(cfg StyleTOML, xrdbColors map[string]string) Style { + var style Style + def := ConfigDefault.Style + s := NilDefaultString(cfg.Background, def.Background) + style.Background = parseColor(s, "#27822", xrdbColors) + + s = NilDefaultString(cfg.Text, def.Text) + style.Text = parseColor(s, "#f8f8f2", xrdbColors) + + s = NilDefaultString(cfg.Subtle, def.Subtle) + style.Subtle = parseColor(s, "#808080", xrdbColors) + + s = NilDefaultString(cfg.WarningText, def.WarningText) + style.WarningText = parseColor(s, "#f92672", xrdbColors) + + s = NilDefaultString(cfg.TextSpecial1, def.TextSpecial1) + style.TextSpecial1 = parseColor(s, "#ae81ff", xrdbColors) + + s = NilDefaultString(cfg.TextSpecial2, def.TextSpecial2) + style.TextSpecial2 = parseColor(s, "#a6e22e", xrdbColors) + + s = NilDefaultString(cfg.TopBarBackground, def.TopBarBackground) + style.TopBarBackground = parseColor(s, "#f92672", xrdbColors) + + s = NilDefaultString(cfg.TopBarText, def.TopBarText) + style.TopBarText = parseColor(s, "#f8f8f2", xrdbColors) + + s = NilDefaultString(cfg.StatusBarBackground, def.StatusBarBackground) + style.StatusBarBackground = parseColor(s, "#f92672", xrdbColors) + + s = NilDefaultString(cfg.StatusBarText, def.StatusBarText) + style.StatusBarText = parseColor(s, "#f8f8f3", xrdbColors) + + s = NilDefaultString(cfg.StatusBarViewBackground, def.StatusBarViewBackground) + style.StatusBarViewBackground = parseColor(s, "#ae81ff", xrdbColors) + + s = NilDefaultString(cfg.StatusBarViewText, def.StatusBarViewText) + style.StatusBarViewText = parseColor(s, "#f8f8f2", xrdbColors) + + s = NilDefaultString(cfg.ListSelectedBackground, def.ListSelectedBackground) + style.ListSelectedBackground = parseColor(s, "#f92672", xrdbColors) + + s = NilDefaultString(cfg.ListSelectedText, def.ListSelectedText) + style.ListSelectedText = parseColor(s, "#f8f8f2", xrdbColors) + + s = NilDefaultString(cfg.ListSelectedInactiveBackground, sp("")) + if len(s) > 0 { + style.ListSelectedInactiveBackground = parseColor(s, "#ae81ff", xrdbColors) + } else { + style.ListSelectedInactiveBackground = style.StatusBarViewBackground + } + s = NilDefaultString(cfg.ListSelectedInactiveText, def.ListSelectedInactiveText) + if len(s) > 0 { + style.ListSelectedInactiveText = parseColor(s, "#f8f8f2", xrdbColors) + } else { + style.ListSelectedInactiveText = style.StatusBarViewText + } + + s = NilDefaultString(cfg.ControlsText, sp("")) + if len(s) > 0 { + style.ControlsText = parseColor(s, "#f8f8f2", xrdbColors) + } else { + style.ControlsText = style.Text + } + s = NilDefaultString(cfg.ControlsHighlight, sp("")) + if len(s) > 0 { + style.ControlsHighlight = parseColor(s, "#a6e22e", xrdbColors) + } else { + style.ControlsHighlight = style.TextSpecial2 + } + + s = NilDefaultString(cfg.AutocompleteBackground, sp("")) + if len(s) > 0 { + style.AutocompleteBackground = parseColor(s, "#272822", xrdbColors) + } else { + style.AutocompleteBackground = style.Background + } + s = NilDefaultString(cfg.AutocompleteText, sp("")) + if len(s) > 0 { + style.AutocompleteText = parseColor(s, "#f8f8f2", xrdbColors) + } else { + style.AutocompleteText = style.Text + } + s = NilDefaultString(cfg.AutocompleteSelectedBackground, sp("")) + if len(s) > 0 { + style.AutocompleteSelectedBackground = parseColor(s, "#ae81ff", xrdbColors) + } else { + style.AutocompleteSelectedBackground = style.StatusBarViewBackground + } + s = NilDefaultString(cfg.AutocompleteSelectedText, sp("")) + if len(s) > 0 { + style.AutocompleteSelectedText = parseColor(s, "#f8f8f2", xrdbColors) + } else { + style.AutocompleteSelectedText = style.StatusBarViewText + } + + s = NilDefaultString(cfg.ButtonColorOne, sp("")) + if len(s) > 0 { + style.ButtonColorOne = parseColor(s, "#ae81ff", xrdbColors) + } else { + style.ButtonColorOne = style.StatusBarViewBackground + } + s = NilDefaultString(cfg.ButtonColorTwo, sp("")) + if len(s) > 0 { + style.ButtonColorTwo = parseColor(s, "#272822", xrdbColors) + } else { + style.ButtonColorTwo = style.Background + } + + s = NilDefaultString(cfg.TimelineNameBackground, sp("")) + if len(s) > 0 { + style.TimelineNameBackground = parseColor(s, "#272822", xrdbColors) + } else { + style.TimelineNameBackground = style.Background + } + s = NilDefaultString(cfg.TimelineNameText, sp("")) + if len(s) > 0 { + style.TimelineNameText = parseColor(s, "#808080", xrdbColors) + } else { + style.TimelineNameText = style.Subtle + } + + s = NilDefaultString(cfg.CommandText, sp("")) + if len(s) > 0 { + style.CommandText = parseColor(s, "#f8f8f2", xrdbColors) + } else { + style.CommandText = style.StatusBarText + } + return style +} + +func parseStyle(cfg StyleTOML, cnfPath string, cnfDir string) Style { var xrdbColors map[string]string xrdbMap, _ := GetXrdbColors() - prefix := cfg.Section("style").Key("xrdb-prefix").String() + def := ConfigDefault.Style + prefix := NilDefaultString(cfg.XrdbPrefix, def.XrdbPrefix) if prefix == "" { prefix = "guess" } @@ -530,7 +698,7 @@ func parseStyle(cfg *ini.File, cnfPath string, cnfDir string) Style { } style := Style{} - theme := cfg.Section("style").Key("theme").String() + theme := NilDefaultString(cfg.Theme, def.Theme) if theme != "none" && theme != "" { bundled, local, err := getThemes(cnfPath, cnfDir) if err != nil { @@ -539,7 +707,7 @@ func parseStyle(cfg *ini.File, cnfPath string, cnfDir string) Style { found := false isLocal := false for _, t := range local { - if filepath.Base(t) == fmt.Sprintf("%s.ini", theme) { + if filepath.Base(t) == fmt.Sprintf("%s.toml", theme) { found = true isLocal = true break @@ -547,7 +715,7 @@ func parseStyle(cfg *ini.File, cnfPath string, cnfDir string) Style { } if !found { for _, t := range bundled { - if filepath.Base(t) == fmt.Sprintf("%s.ini", theme) { + if filepath.Base(t) == fmt.Sprintf("%s.toml", theme) { found = true break } @@ -560,303 +728,120 @@ func parseStyle(cfg *ini.File, cnfPath string, cnfDir string) Style { if err != nil { log.Fatalf("Couldn't load theme. Error: %s\n", err) } - s := tcfg.Section("").Key("background").String() - style.Background = parseColor(s, "default", xrdbColors) - - s = tcfg.Section("").Key("text").String() - style.Text = tcell.GetColor(s) - - s = tcfg.Section("").Key("subtle").String() - style.Subtle = tcell.GetColor(s) - - s = tcfg.Section("").Key("warning-text").String() - style.WarningText = tcell.GetColor(s) - - s = tcfg.Section("").Key("text-special-one").String() - style.TextSpecial1 = tcell.GetColor(s) - - s = tcfg.Section("").Key("text-special-two").String() - style.TextSpecial2 = tcell.GetColor(s) - - s = tcfg.Section("").Key("top-bar-background").String() - style.TopBarBackground = tcell.GetColor(s) - - s = tcfg.Section("").Key("top-bar-text").String() - style.TopBarText = tcell.GetColor(s) + style = parseTheme(tcfg, xrdbColors) - s = tcfg.Section("").Key("status-bar-background").String() - style.StatusBarBackground = tcell.GetColor(s) - - s = tcfg.Section("").Key("status-bar-text").String() - style.StatusBarText = tcell.GetColor(s) - - s = tcfg.Section("").Key("status-bar-view-background").String() - style.StatusBarViewBackground = tcell.GetColor(s) - - s = tcfg.Section("").Key("status-bar-view-text").String() - style.StatusBarViewText = tcell.GetColor(s) - - s = tcfg.Section("").Key("list-selected-background").String() - style.ListSelectedBackground = tcell.GetColor(s) - - s = tcfg.Section("").Key("list-selected-text").String() - style.ListSelectedText = tcell.GetColor(s) - - s = tcfg.Section("").Key("list-selected-inactive-background").String() - if len(s) > 0 { - style.ListSelectedInactiveBackground = tcell.GetColor(s) - } else { - style.ListSelectedInactiveBackground = style.StatusBarViewBackground - } - s = tcfg.Section("").Key("list-selected-inactive-text").String() - if len(s) > 0 { - style.ListSelectedInactiveText = tcell.GetColor(s) - } else { - style.ListSelectedInactiveText = style.StatusBarViewText - } - - s = tcfg.Section("").Key("controls-highlight").String() - if len(s) > 0 { - style.ControlsHighlight = tcell.GetColor(s) - } else { - style.ControlsHighlight = style.TextSpecial2 - } - - s = tcfg.Section("").Key("controls-text").String() - if len(s) > 0 { - style.ControlsText = tcell.GetColor(s) - } else { - style.ControlsText = style.Text - } - s = tcfg.Section("").Key("controls-highlight").String() - if len(s) > 0 { - style.ControlsHighlight = tcell.GetColor(s) - } else { - style.ControlsHighlight = style.TextSpecial2 - } - - s = tcfg.Section("").Key("autocomplete-background").String() - if len(s) > 0 { - style.AutocompleteBackground = tcell.GetColor(s) - } else { - style.AutocompleteBackground = style.Background - } - s = tcfg.Section("").Key("autocomplete-text").String() - if len(s) > 0 { - style.AutocompleteText = tcell.GetColor(s) - } else { - style.AutocompleteText = style.Text - } - s = tcfg.Section("").Key("autocomplete-selected-background").String() - if len(s) > 0 { - style.AutocompleteSelectedBackground = tcell.GetColor(s) - } else { - style.AutocompleteSelectedBackground = style.StatusBarViewBackground - } - s = tcfg.Section("").Key("autocomplete-selected-text").String() - if len(s) > 0 { - style.AutocompleteSelectedText = tcell.GetColor(s) - } else { - style.AutocompleteSelectedText = style.StatusBarViewText - } - - s = tcfg.Section("").Key("button-color-one").String() - if len(s) > 0 { - style.ButtonColorOne = tcell.GetColor(s) - } else { - style.ButtonColorOne = style.StatusBarViewBackground - } - s = tcfg.Section("").Key("button-color-two").String() - if len(s) > 0 { - style.ButtonColorTwo = tcell.GetColor(s) - } else { - style.ButtonColorTwo = style.Background - } - - s = tcfg.Section("").Key("timeline-name-background").String() - if len(s) > 0 { - style.TimelineNameBackground = tcell.GetColor(s) - } else { - style.TimelineNameBackground = style.Background - } - s = tcfg.Section("").Key("timeline-name-text").String() - if len(s) > 0 { - style.TimelineNameText = tcell.GetColor(s) - } else { - style.TimelineNameText = style.Subtle - } - s = tcfg.Section("").Key("command-text").String() - if len(s) > 0 { - style.CommandText = tcell.GetColor(s) - } else { - style.CommandText = style.StatusBarText - } } else { - s := cfg.Section("style").Key("background").String() - style.Background = parseColor(s, "#27822", xrdbColors) - - s = cfg.Section("style").Key("text").String() - style.Text = parseColor(s, "#f8f8f2", xrdbColors) - - s = cfg.Section("style").Key("subtle").String() - style.Subtle = parseColor(s, "#808080", xrdbColors) - - s = cfg.Section("style").Key("warning-text").String() - style.WarningText = parseColor(s, "#f92672", xrdbColors) - - s = cfg.Section("style").Key("text-special-one").String() - style.TextSpecial1 = parseColor(s, "#ae81ff", xrdbColors) - - s = cfg.Section("style").Key("text-special-two").String() - style.TextSpecial2 = parseColor(s, "#a6e22e", xrdbColors) - - s = cfg.Section("style").Key("top-bar-background").String() - style.TopBarBackground = parseColor(s, "#f92672", xrdbColors) - - s = cfg.Section("style").Key("top-bar-text").String() - style.TopBarText = parseColor(s, "white", xrdbColors) - - s = cfg.Section("style").Key("status-bar-background").String() - style.StatusBarBackground = parseColor(s, "#f92672", xrdbColors) - - s = cfg.Section("style").Key("status-bar-text").String() - style.StatusBarText = parseColor(s, "white", xrdbColors) - - s = cfg.Section("style").Key("status-bar-view-background").String() - style.StatusBarViewBackground = parseColor(s, "#ae81ff", xrdbColors) - - s = cfg.Section("style").Key("status-bar-view-text").String() - style.StatusBarViewText = parseColor(s, "white", xrdbColors) - - s = cfg.Section("style").Key("list-selected-background").String() - style.ListSelectedBackground = parseColor(s, "#f92672", xrdbColors) - - s = cfg.Section("style").Key("list-selected-text").String() - style.ListSelectedText = parseColor(s, "white", xrdbColors) - - s = cfg.Section("style").Key("list-selected-inactive-background").String() - if len(s) > 0 { - style.ListSelectedInactiveBackground = parseColor(s, "#ae81ff", xrdbColors) - } else { - style.ListSelectedInactiveBackground = style.StatusBarViewBackground - } - s = cfg.Section("style").Key("list-selected-inactive-text").String() - if len(s) > 0 { - style.ListSelectedInactiveText = parseColor(s, "#f8f8f2", xrdbColors) - } else { - style.ListSelectedInactiveText = style.StatusBarViewText - } - - s = cfg.Section("style").Key("controls-text").String() - if len(s) > 0 { - style.ControlsText = parseColor(s, "#f8f8f2", xrdbColors) - } else { - style.ControlsText = style.Text - } - s = cfg.Section("style").Key("controls-highlight").String() - if len(s) > 0 { - style.ControlsHighlight = parseColor(s, "#a6e22e", xrdbColors) - } else { - style.ControlsHighlight = style.TextSpecial2 - } - - s = cfg.Section("style").Key("autocomplete-background").String() - if len(s) > 0 { - style.AutocompleteBackground = parseColor(s, "#272822", xrdbColors) - } else { - style.AutocompleteBackground = style.Background - } - s = cfg.Section("style").Key("autocomplete-text").String() - if len(s) > 0 { - style.AutocompleteText = parseColor(s, "#f8f8f2", xrdbColors) - } else { - style.AutocompleteText = style.Text - } - s = cfg.Section("style").Key("autocomplete-selected-background").String() - if len(s) > 0 { - style.AutocompleteSelectedBackground = parseColor(s, "#ae81ff", xrdbColors) - } else { - style.AutocompleteSelectedBackground = style.StatusBarViewBackground - } - s = cfg.Section("style").Key("autocomplete-selected-text").String() - if len(s) > 0 { - style.AutocompleteSelectedText = parseColor(s, "#f8f8f2", xrdbColors) - } else { - style.AutocompleteSelectedText = style.StatusBarViewText - } - - s = cfg.Section("style").Key("button-color-one").String() - if len(s) > 0 { - style.ButtonColorOne = parseColor(s, "#ae81ff", xrdbColors) - } else { - style.ButtonColorOne = style.StatusBarViewBackground - } - s = cfg.Section("style").Key("button-color-two").String() - if len(s) > 0 { - style.ButtonColorTwo = parseColor(s, "#272822", xrdbColors) - } else { - style.ButtonColorTwo = style.Background - } + style = parseTheme(cfg, xrdbColors) + } - s = cfg.Section("style").Key("timeline-name-background").String() - if len(s) > 0 { - style.TimelineNameBackground = parseColor(s, "#272822", xrdbColors) - } else { - style.TimelineNameBackground = style.Background - } - s = cfg.Section("style").Key("timeline-name-text").String() - if len(s) > 0 { - style.TimelineNameText = parseColor(s, "gray", xrdbColors) - } else { - style.TimelineNameText = style.Subtle - } + return style +} - s = cfg.Section("style").Key("command-text").String() - if len(s) > 0 { - style.CommandText = parseColor(s, "white", xrdbColors) - } else { - style.CommandText = style.StatusBarText - } +func getViewer(v *ViewerTOML, def *ViewerTOML) (program, args string, terminal, single, reverse bool) { + program = *def.Program + args = *def.Args + terminal = *def.Terminal + single = *def.Single + reverse = *def.Reverse + if v == nil { + return + } + if v.Program != nil { + program = *v.Program + } + if v.Args != nil { + args = *v.Args + } + if v.Terminal != nil { + terminal = *v.Terminal + } + if v.Single != nil { + single = *v.Single } + if v.Reverse != nil { + reverse = *v.Reverse + } + if *v.Program == "TUT_OS_DEFAULT" { + var argsSlice []string + program, argsSlice = util.GetDefaultForOS() + args = strings.Join(argsSlice, " ") + } + return +} - return style +func parseMedia(cfg MediaTOML) Media { + media := Media{} + media.DeleteTmpFiles = NilDefaultBool(cfg.DeleteTmpFiles, ConfigDefault.Media.DeleteTmpFiles) + var program, args string + var terminal, single, reverse bool + + program, args, terminal, single, reverse = getViewer(cfg.Image, ConfigDefault.Media.Image) + media.ImageViewer = program + media.ImageArgs = strings.Fields(args) + media.ImageTerminal = terminal + media.ImageSingle = single + media.ImageReverse = reverse + + program, args, terminal, single, reverse = getViewer(cfg.Video, ConfigDefault.Media.Video) + media.VideoViewer = program + media.VideoArgs = strings.Fields(args) + media.VideoTerminal = terminal + media.VideoSingle = single + media.VideoReverse = reverse + + program, args, terminal, single, reverse = getViewer(cfg.Audio, ConfigDefault.Media.Audio) + media.AudioViewer = program + media.AudioArgs = strings.Fields(args) + media.AudioTerminal = terminal + media.AudioSingle = single + media.AudioReverse = reverse + + program, args, terminal, _, _ = getViewer(cfg.Link, ConfigDefault.Media.Link) + media.LinkViewer = program + media.LinkArgs = strings.Fields(args) + media.LinkTerminal = terminal + + return media } -func parseGeneral(cfg *ini.File) General { +func parseGeneral(cfg GeneralTOML) General { general := General{} - general.Confirmation = cfg.Section("general").Key("confirmation").MustBool(true) - general.MouseSupport = cfg.Section("general").Key("mouse-support").MustBool(false) - dateFormat := cfg.Section("general").Key("date-format").String() + def := ConfigDefault.General + general.Editor = NilDefaultString(cfg.Editor, def.Editor) + if general.Editor == "TUT_USE_INTERNAL" { + general.UseInternalEditor = true + } + general.Confirmation = NilDefaultBool(cfg.Confirmation, def.Confirmation) + general.MouseSupport = NilDefaultBool(cfg.MouseSupport, def.MouseSupport) + + dateFormat := NilDefaultString(cfg.DateFormat, def.DateFormat) if dateFormat == "" { dateFormat = "2006-01-02 15:04" } general.DateFormat = dateFormat - dateTodayFormat := cfg.Section("general").Key("date-today-format").String() + dateTodayFormat := NilDefaultString(cfg.DateTodayFormat, def.DateTodayFormat) if dateTodayFormat == "" { dateTodayFormat = "15:04" } general.DateTodayFormat = dateTodayFormat - dateRelative, err := cfg.Section("general").Key("date-relative").Int() - if err != nil { - dateRelative = -1 - } - general.DateRelative = dateRelative - - general.NotificationFeed = cfg.Section("general").Key("notification-feed").MustBool(true) - general.QuoteReply = cfg.Section("general").Key("quote-reply").MustBool(false) - general.MaxWidth = cfg.Section("general").Key("max-width").MustInt(0) - general.ShortHints = cfg.Section("general").Key("short-hints").MustBool(false) - general.ShowFilterPhrase = cfg.Section("general").Key("show-filter-phrase").MustBool(true) - general.ShowIcons = cfg.Section("general").Key("show-icons").MustBool(true) - general.ShowHelp = cfg.Section("general").Key("show-help").MustBool(true) - general.RedrawUI = cfg.Section("general").Key("redraw-ui").MustBool(true) - general.StickToTop = cfg.Section("general").Key("stick-to-top").MustBool(false) - general.ShowBoostedUser = cfg.Section("general").Key("show-boosted-user").MustBool(false) - - lp := cfg.Section("general").Key("list-placement").In("left", []string{"left", "right", "top", "bottom"}) + general.DateRelative = NilDefaultInt(cfg.DateRelative, def.DateRelative) + + general.QuoteReply = NilDefaultBool(cfg.QuoteReply, def.QuoteReply) + general.MaxWidth = NilDefaultInt(cfg.MaxWidth, def.MaxWidth) + general.ShortHints = NilDefaultBool(cfg.ShortHints, def.ShortHints) + general.ShowFilterPhrase = NilDefaultBool(cfg.ShowFilterPhrase, def.ShowFilterPhrase) + general.ShowIcons = NilDefaultBool(cfg.ShowIcons, def.ShowIcons) + general.ShowHelp = NilDefaultBool(cfg.ShowHelp, def.ShowHelp) + general.RedrawUI = NilDefaultBool(cfg.RedrawUI, def.RedrawUI) + general.StickToTop = NilDefaultBool(cfg.StickToTop, def.StickToTop) + general.ShowBoostedUser = NilDefaultBool(cfg.ShowBoostedUser, def.ShowBoostedUser) + general.DynamicTimelineName = NilDefaultBool(cfg.DynamicTimelineName, def.DynamicTimelineName) + general.CommandsInNewPane = NilDefaultBool(cfg.CommandsInNewPane, def.CommandsInNewPane) + + lp := NilDefaultString(cfg.ListPlacement, def.ListPlacement) switch lp { case "left": general.ListPlacement = ListPlacementLeft @@ -866,8 +851,10 @@ func parseGeneral(cfg *ini.File) General { general.ListPlacement = ListPlacementTop case "bottom": general.ListPlacement = ListPlacementBottom + default: + general.ListPlacement = ListPlacementLeft } - ls := cfg.Section("general").Key("list-split").In("row", []string{"row", "column"}) + ls := NilDefaultString(cfg.ListSplit, def.ListSplit) switch ls { case "row": general.ListSplit = ListRow @@ -875,18 +862,18 @@ func parseGeneral(cfg *ini.File) General { general.ListSplit = ListColumn } - listProp := cfg.Section("general").Key("list-proportion").MustInt(1) + listProp := NilDefaultInt(cfg.ListProportion, def.ListProportion) if listProp < 1 { listProp = 1 } - contentProp := cfg.Section("general").Key("content-proportion").MustInt(2) + contentProp := NilDefaultInt(cfg.ContentProportion, def.ContentProportion) if contentProp < 1 { contentProp = 1 } general.ListProportion = listProp general.ContentProportion = contentProp - leaderString := cfg.Section("general").Key("leader-key").MustString("") + leaderString := NilDefaultString(cfg.LeaderKey, def.LeaderKey) leaderRunes := []rune(leaderString) if len(leaderRunes) > 1 { leaderRunes = []rune(strings.TrimSpace(leaderString)) @@ -898,429 +885,319 @@ func parseGeneral(cfg *ini.File) General { if len(leaderRunes) == 1 { general.LeaderKey = leaderRunes[0] } + general.LeaderTimeout = NilDefaultInt64(cfg.LeaderTimeout, def.LeaderTimeout) if general.LeaderKey != rune(0) { - general.LeaderTimeout = cfg.Section("general").Key("leader-timeout").MustInt64(1000) - lactions := cfg.Section("general").Key("leader-action").ValueWithShadows() var las []LeaderAction - for _, l := range lactions { - parts := strings.Split(l, ",") - if len(parts) < 2 { - fmt.Printf("leader-action must consist of atleast two parts separated by a comma. Your value is: %s\n", strings.Join(parts, ",")) - os.Exit(1) - } - for i, p := range parts { - parts[i] = strings.TrimSpace(p) + if cfg.LeaderActions != nil { + lactions := *cfg.LeaderActions + for _, l := range lactions { + la := LeaderAction{} + ltype := NilDefaultString(l.Type, sp("")) + ldata := NilDefaultString(l.Data, sp("")) + lshortcut := NilDefaultString(l.Shortcut, sp("")) + switch ltype { + case "clear-notifications": + la.Command = LeaderClearNotifications + case "compose": + la.Command = LeaderCompose + case "edit": + la.Command = LeaderEdit + case "blocking": + la.Command = LeaderBlocking + case "favorited": + la.Command = LeaderFavorited + case "history": + la.Command = LeaderHistory + case "boosts": + la.Command = LeaderBoosts + case "favorites": + la.Command = LeaderFavorites + case "following": + la.Command = LeaderFollowing + case "followers": + la.Command = LeaderFollowers + case "muting": + la.Command = LeaderMuting + case "preferences": + la.Command = LeaderPreferences + case "profile": + la.Command = LeaderProfile + case "mentions": + la.Command = LeaderMentions + case "stick-to-top": + la.Command = LeaderStickToTop + case "refetch": + la.Command = LeaderRefetch + case "tags": + la.Command = LeaderTags + case "list-placement": + la.Command = LeaderListPlacement + la.Subaction = ldata + case "list-split": + la.Command = LeaderListSplit + la.Subaction = ldata + case "proportions": + la.Command = LeaderProportions + la.Subaction = ldata + case "pane": + la.Command = LeaderPane + la.Subaction = ldata + case "close-pane": + la.Command = LeaderClosePane + case "move-pane-left", "move-pane-up": + la.Command = LeaderMovePaneLeft + case "move-pane-right", "move-pane-down": + la.Command = LeaderMovePaneRight + case "move-pane-home": + la.Command = LeaderMovePaneHome + case "move-pane-end": + la.Command = LeaderMovePaneEnd + case "newer": + la.Command = LeaderLoadNewer + default: + fmt.Printf("leader-action %s is invalid\n", ltype) + os.Exit(1) + } + la.Shortcut = lshortcut + las = append(las, la) } - cmd := parts[0] - var subaction string - if strings.Contains(parts[0], " ") { - p := strings.Split(cmd, " ") - cmd = p[0] - subaction = strings.Join(p[1:], " ") + } + general.LeaderActions = las + } + + var tls []*Timeline + timelines := cfg.Timelines + if cfg.Timelines != nil { + for _, l := range *timelines { + tl := NewTimeline(Timeline{}) + if l.Type == nil { + fmt.Println("timelines must have a type") + os.Exit(1) } - la := LeaderAction{} - switch cmd { + switch *l.Type { case "home": - la.Command = LeaderHome + tl.FeedType = TimelineHome + case "special": + tl.FeedType = TimelineHomeSpecial case "direct": - la.Command = LeaderDirect + tl.FeedType = Conversations case "local": - la.Command = LeaderLocal + tl.FeedType = TimelineLocal case "federated": - la.Command = LeaderFederated - case "special-all": - la.Command = LeaderSpecialAll - case "special-boosts": - la.Command = LeaderSpecialBoosts - case "special-replies": - la.Command = LeaderSpecialReplies - case "clear-notifications": - la.Command = LeaderClearNotifications - case "compose": - la.Command = LeaderCompose - case "edit": - la.Command = LeaderEdit - case "blocking": - la.Command = LeaderBlocking + tl.FeedType = TimelineFederated case "bookmarks": - la.Command = LeaderBookmarks + tl.FeedType = Saved case "saved": - la.Command = LeaderSaved + tl.FeedType = Saved case "favorited": - la.Command = LeaderFavorited - case "history": - la.Command = LeaderHistory - case "boosts": - la.Command = LeaderBoosts - case "favorites": - la.Command = LeaderFavorites - case "following": - la.Command = LeaderFollowing - case "followers": - la.Command = LeaderFollowers - case "muting": - la.Command = LeaderMuting - case "preferences": - la.Command = LeaderPreferences - case "profile": - la.Command = LeaderProfile + tl.FeedType = Favorited case "notifications": - la.Command = LeaderNotifications + tl.FeedType = Notifications case "mentions": - la.Command = LeaderMentions + tl.FeedType = Mentions case "lists": - la.Command = LeaderLists - case "stick-to-top": - la.Command = LeaderStickToTop - case "refetch": - la.Command = LeaderRefetch + tl.FeedType = Lists case "tag": - la.Command = LeaderTag - la.Subaction = subaction - case "tags": - la.Command = LeaderTags - case "list-placement": - la.Command = LeaderListPlacement - la.Subaction = subaction - case "list-split": - la.Command = LeaderListSplit - la.Subaction = subaction - case "proportions": - la.Command = LeaderProportions - la.Subaction = subaction - case "window": - la.Command = LeaderWindow - la.Subaction = subaction - case "close-window": - la.Command = LeaderCloseWindow - case "move-window-left", "move-window-up": - la.Command = LeaderMoveWindowLeft - case "move-window-right", "move-window-down": - la.Command = LeaderMoveWindowRight - case "move-window-home": - la.Command = LeaderMoveWindowHome - case "move-window-end": - la.Command = LeaderMoveWindowEnd - case "switch": - la.Command = LeaderSwitch - sa := "" - if len(parts) > 2 { - sa = strings.Join(parts[2:], ",") - } - if len(sa) > 0 { - la.Subaction = fmt.Sprintf("%s,%s", subaction, sa) - } else { - la.Subaction = subaction - } - case "newer": - la.Command = LeaderLoadNewer + tl.FeedType = Tag + tl.Subaction = NilDefaultString(l.Data, sp("")) default: - fmt.Printf("leader-action %s is invalid\n", parts[0]) + fmt.Printf("timeline %s is invalid\n", *l.Type) os.Exit(1) } - la.Shortcut = parts[1] - las = append(las, la) - } - general.LeaderActions = las - } - - general.TimelineName = cfg.Section("general").Key("timeline-show-name").MustBool(true) - var tls []Timeline - timelines := cfg.Section("general").Key("timelines").ValueWithShadows() - for _, l := range timelines { - parts := strings.Split(l, ",") - for i, p := range parts { - parts[i] = strings.TrimSpace(p) - } - if len(parts) == 0 { - fmt.Printf("timelines must consist of atleast one part seperated by a comma. Your value is: %s\n", strings.Join(parts, ",")) - os.Exit(1) - } - if len(parts) == 1 { - parts = append(parts, "") - } - cmd := parts[0] - var subaction string - if strings.Contains(parts[0], " ") { - p := strings.Split(cmd, " ") - cmd = p[0] - subaction = strings.Join(p[1:], " ") - } - tl := Timeline{} - switch cmd { - case "home": - tl.FeedType = TimelineHome - case "special": - tl.FeedType = TimelineHomeSpecial - case "direct": - tl.FeedType = Conversations - case "local": - tl.FeedType = TimelineLocal - case "federated": - tl.FeedType = TimelineFederated - case "bookmarks": - tl.FeedType = Saved - case "saved": - tl.FeedType = Saved - case "favorited": - tl.FeedType = Favorited - case "notifications": - tl.FeedType = Notifications - case "mentions": - tl.FeedType = Mentions - case "lists": - tl.FeedType = Lists - case "tag": - tl.FeedType = Tag - tl.Subaction = subaction - default: - fmt.Printf("timeline %s is invalid\n", parts[0]) - os.Exit(1) - } - tfStr := []string{"true", "false"} - tl.Name = parts[1] - if slices.Contains(tfStr, tl.Name) || (strings.HasPrefix(parts[1], "\"") && strings.HasSuffix(parts[1], "\"")) || - (strings.HasPrefix(parts[1], "'") && strings.HasSuffix(parts[1], "'")) { - tl.Name = "" - } - tfs := []bool{true, true} - stop := len(parts) - if len(parts) > 1 { - if len(parts) > 2 && slices.Contains(tfStr, parts[len(parts)-2]) && - slices.Contains(tfStr, parts[len(parts)-1]) && - len(parts)-2 > 0 { - tfs[0] = parts[len(parts)-2] == "true" - tfs[1] = parts[len(parts)-1] == "true" - stop = len(parts) - 2 - } else if slices.Contains(tfStr, parts[len(parts)-1]) && - len(parts)-1 > 0 { - tfs[0] = parts[len(parts)-1] == "true" - stop = len(parts) - 1 + tl.Name = NilDefaultString(l.Name, sp("")) + tl.HideBoosts = NilDefaultBool(l.HideBoosts, bf) + tl.HideReplies = NilDefaultBool(l.HideReplies, bf) + tl.Closed = NilDefaultBool(l.Closed, bf) + tl.Shortcut = NilDefaultString(l.Shortcut, sp("")) + onFocus := NilDefaultString(l.OnFocus, sp("")) + if onFocus != "" { + switch onFocus { + case "focus-pane": + tl.OnFocus = TimelineFocusPane + case "focus-self": + tl.OnFocus = TimelineFocusTimeline + default: + fmt.Printf("on-focus: %s in timelines is invalid\n", onFocus) + os.Exit(1) + } } - if stop > 2 { - vals := []string{""} - start := 2 - if tl.Name == "" { - start = 1 + onCreation := NilDefaultString(l.OnCreationClosed, sp("")) + if onCreation != "" { + switch onCreation { + case "current-pane": + tl.OnCreationClosed = TimelineCreationClosedCurrentPane + case "new-pane": + tl.OnCreationClosed = TimelineCreationClosedNewPane + default: + fmt.Printf("on-creation-closed: %s in timelines is invalid\n", onCreation) + os.Exit(1) } - vals = append(vals, parts[start:stop]...) - tl.Key = inputStrOrErr(vals, false) } + if l.Keys != nil { + var keys []string + var special []string + if l.Keys != nil { + keys = *l.Keys + } + if l.SpecialKeys != nil { + special = *l.SpecialKeys + } + var err error + tl.Key, err = NewKey("", "", keys, special) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + } + tls = append(tls, tl) } - tl.ShowBoosts = tfs[0] - tl.ShowReplies = tfs[1] - tls = append(tls, tl) } - if len(tls) == 0 { + shown := 0 + for _, tl := range tls { + if !tl.Closed { + shown += 1 + } + } + if len(tls) == 0 || shown == 0 { tls = append(tls, - Timeline{ - FeedType: TimelineHome, - Name: "", - ShowBoosts: true, - ShowReplies: true, - }, + NewTimeline(Timeline{ + FeedType: TimelineHome, + Name: "Home", + }), ) + kn, err := NewKey("", "", []string{"n", "N"}, []string{}) + if err != nil { + fmt.Println(err) + os.Exit(1) + } tls = append(tls, - Timeline{ - FeedType: Notifications, - Name: "[N]otifications", - Key: inputStrOrErr([]string{"", "'n'", "'N'"}, false), - ShowBoosts: true, - ShowReplies: true, - }, + NewTimeline(Timeline{ + FeedType: Notifications, + Name: "[N]otifications", + Key: kn, + }), ) } general.Timelines = tls - general.TerminalTitle = cfg.Section("general").Key("terminal-title").MustInt(0) + general.TerminalTitle = NilDefaultInt(cfg.TerminalTitle, def.TerminalTitle) /* 0 = No terminal title 1 = Show title in terminal and top bar 2 = Only show terminal title, and no top bar */ - if general.TerminalTitle < 0 || general.TerminalTitle > 2 { + if general.TerminalTitle < 0 || general.TerminalTitle > 3 { general.TerminalTitle = 0 } nths := []NotificationToHide{} - nth := cfg.Section("general").Key("notifications-to-hide").MustString("") - parts := strings.Split(nth, ",") - for _, p := range parts { - s := strings.TrimSpace(p) - switch s { - case "mention": - nths = append(nths, HideMention) - case "status": - nths = append(nths, HideStatus) - case "boost": - nths = append(nths, HideBoost) - case "follow": - nths = append(nths, HideFollow) - case "follow_request": - nths = append(nths, HideFollowRequest) - case "favorite": - nths = append(nths, HideFavorite) - case "poll": - nths = append(nths, HidePoll) - case "edit": - nths = append(nths, HideEdited) - default: - if len(s) > 0 { - log.Fatalf("%s in notifications-to-hide is invalid\n", s) + nth := cfg.NotificationsToHide + if nth != nil { + for _, n := range *nth { + switch n { + case "mention": + nths = append(nths, HideMention) + case "status": + nths = append(nths, HideStatus) + case "boost": + nths = append(nths, HideBoost) + case "follow": + nths = append(nths, HideFollow) + case "follow_request": + nths = append(nths, HideFollowRequest) + case "favorite": + nths = append(nths, HideFavorite) + case "poll": + nths = append(nths, HidePoll) + case "edit": + nths = append(nths, HideEdited) + default: + log.Fatalf("%s in notifications-to-hide is invalid\n", n) os.Exit(1) } + general.NotificationsToHide = nths } - general.NotificationsToHide = nths } - return general } -func parseMedia(cfg *ini.File) Media { - media := Media{} - imageViewerComponents := strings.Fields(cfg.Section("media").Key("image-viewer").String()) - if len(imageViewerComponents) == 0 { - media.ImageViewer = "xdg-open" - media.ImageArgs = []string{} - } else { - media.ImageViewer = imageViewerComponents[0] - media.ImageArgs = imageViewerComponents[1:] - } - media.ImageTerminal = cfg.Section("media").Key("image-terminal").MustBool(false) - media.ImageSingle = cfg.Section("media").Key("image-single").MustBool(true) - media.ImageReverse = cfg.Section("media").Key("image-reverse").MustBool(false) - - videoViewerComponents := strings.Fields(cfg.Section("media").Key("video-viewer").String()) - if len(videoViewerComponents) == 0 { - media.VideoViewer = "xdg-open" - media.VideoArgs = []string{} - } else { - media.VideoViewer = videoViewerComponents[0] - media.VideoArgs = videoViewerComponents[1:] - } - media.VideoTerminal = cfg.Section("media").Key("video-terminal").MustBool(false) - media.VideoSingle = cfg.Section("media").Key("video-single").MustBool(true) - media.VideoReverse = cfg.Section("media").Key("video-reverse").MustBool(false) - - audioViewerComponents := strings.Fields(cfg.Section("media").Key("audio-viewer").String()) - if len(audioViewerComponents) == 0 { - media.AudioViewer = "xdg-open" - media.AudioArgs = []string{} - } else { - media.AudioViewer = audioViewerComponents[0] - media.AudioArgs = audioViewerComponents[1:] - } - media.AudioTerminal = cfg.Section("media").Key("audio-terminal").MustBool(false) - media.AudioSingle = cfg.Section("media").Key("audio-single").MustBool(true) - media.AudioReverse = cfg.Section("media").Key("audio-reverse").MustBool(false) - - linkViewerComponents := strings.Fields(cfg.Section("media").Key("link-viewer").String()) - if len(linkViewerComponents) == 0 { - media.LinkViewer = "xdg-open" - media.LinkArgs = []string{} - } else { - media.LinkViewer = linkViewerComponents[0] - media.LinkArgs = linkViewerComponents[1:] - } - media.LinkTerminal = cfg.Section("media").Key("link-terminal").MustBool(false) - - return media -} - -func parseOpenPattern(cfg *ini.File) OpenPattern { +func parseOpenPattern(cfg OpenPatternTOML) OpenPattern { om := OpenPattern{} - - keys := cfg.Section("open-pattern").KeyStrings() - pairs := make(map[string]Pattern) - for _, s := range keys { - parts := strings.Split(s, "-") - if len(parts) < 2 { - panic(fmt.Sprintf("Invalid key %s in config. Must end in -pattern, -use or -terminal", s)) - } - last := parts[len(parts)-1] - if last != "pattern" && last != "use" && last != "terminal" { - panic(fmt.Sprintf("Invalid key %s in config. Must end in -pattern, -use or -terminal", s)) - } - - name := strings.Join(parts[:len(parts)-1], "-") - if _, ok := pairs[name]; !ok { - pairs[name] = Pattern{} - } - if last == "pattern" { - tmp := pairs[name] - tmp.Pattern = cfg.Section("open-pattern").Key(s).MustString("") - pairs[name] = tmp - } - if last == "use" { - tmp := pairs[name] - tmp.Open = cfg.Section("open-pattern").Key(s).MustString("") - pairs[name] = tmp - } - if last == "terminal" { - tmp := pairs[name] - tmp.Terminal = cfg.Section("open-pattern").Key(s).MustBool(false) - pairs[name] = tmp - } + if cfg.Patterns == nil { + return om } - - for key := range pairs { - if pairs[key].Pattern == "" { - panic(fmt.Sprintf("Invalid value for key %s in config. Can't be empty", key+"-pattern")) + for _, p := range *cfg.Patterns { + pattern := Pattern{ + Program: NilDefaultString(p.Program, sp("")), + Terminal: NilDefaultBool(p.Terminal, bf), } - if pairs[key].Open == "" { - panic(fmt.Sprintf("Invalid value for key %s in config. Can't be empty", key+"-use")) + if p.Args != nil { + pattern.Args = strings.Fields(*p.Args) } - - compiled, err := glob.Compile(pairs[key].Pattern) + pg := NilDefaultString(p.Matching, sp("")) + compiled, err := glob.Compile(pg) if err != nil { - panic(fmt.Sprintf("Couldn't compile pattern for key %s in config. Error: %v", key+"-pattern", err)) + panic(fmt.Sprintf("Couldn't compile pattern %s in config. Error: %v", pg, err)) } - tmp := pairs[key] - tmp.Compiled = compiled - comp := strings.Fields(tmp.Open) - tmp.Program = comp[0] - tmp.Args = comp[1:] - om.Patterns = append(om.Patterns, tmp) + pattern.Compiled = compiled + om.Patterns = append(om.Patterns, pattern) } return om } -func parseCustom(cfg *ini.File) OpenCustom { +func parseCustom(cfg OpenCustomTOML) OpenCustom { oc := OpenCustom{} - - for i := 1; i < 6; i++ { - name := cfg.Section("open-custom").Key(fmt.Sprintf("c%d-name", i)).MustString("") - use := cfg.Section("open-custom").Key(fmt.Sprintf("c%d-use", i)).MustString("") - terminal := cfg.Section("open-custom").Key(fmt.Sprintf("c%d-terminal", i)).MustBool(false) + if cfg.Programs == nil { + return oc + } + for _, x := range *cfg.Programs { + keys, special := []string{}, []string{} + if x.Keys != nil { + keys = *x.Keys + } + if x.SpecialKeys != nil { + special = *x.SpecialKeys + } + key, err := NewKey( + NilDefaultString(x.Hint, sp("")), + "", keys, special, + ) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + use := NilDefaultString(x.Program, sp("")) + terminal := NilDefaultBool(x.Terminal, bf) if use == "" { continue } - comp := strings.Fields(use) - c := Custom{} - c.Index = i - c.Name = name - c.Program = comp[0] - c.Args = comp[1:] - c.Terminal = terminal + args := strings.Fields(NilDefaultString(x.Args, sp(""))) + c := Custom{ + Program: use, + Args: args, + Terminal: terminal, + Key: key, + } oc.OpenCustoms = append(oc.OpenCustoms, c) } return oc } -func parseNotifications(cfg *ini.File) Notification { +func parseNotifications(cfg NotificationsTOML) Notification { nc := Notification{} - nc.NotificationFollower = cfg.Section("desktop-notification").Key("followers").MustBool(false) - nc.NotificationFavorite = cfg.Section("desktop-notification").Key("favorite").MustBool(false) - nc.NotificationMention = cfg.Section("desktop-notification").Key("mention").MustBool(false) - nc.NotificationUpdate = cfg.Section("desktop-notification").Key("update").MustBool(false) - nc.NotificationBoost = cfg.Section("desktop-notification").Key("boost").MustBool(false) - nc.NotificationPoll = cfg.Section("desktop-notification").Key("poll").MustBool(false) - nc.NotificationPost = cfg.Section("desktop-notification").Key("posts").MustBool(false) + def := ConfigDefault.NotificationConfig + nc.NotificationFollower = NilDefaultBool(cfg.Followers, def.Followers) + nc.NotificationFavorite = NilDefaultBool(cfg.Favorite, def.Favorite) + nc.NotificationMention = NilDefaultBool(cfg.Mention, def.Mention) + nc.NotificationUpdate = NilDefaultBool(cfg.Update, def.Update) + nc.NotificationBoost = NilDefaultBool(cfg.Boost, def.Followers) + nc.NotificationPoll = NilDefaultBool(cfg.Poll, def.Poll) + nc.NotificationPost = NilDefaultBool(cfg.Posts, def.Posts) return nc } -func parseTemplates(cfg *ini.File, cnfPath string, cnfDir string) Templates { +func parseTemplates(cfg ConfigTOML, cnfPath string, cnfDir string) Templates { var tootTmpl *template.Template tootTmplPath, exists, err := checkConfig("toot.tmpl", cnfPath, cnfDir) if err != nil { @@ -1380,224 +1257,143 @@ func parseTemplates(cfg *ini.File, cnfPath string, cnfDir string) Templates { } } -func inputOrErr(cfg *ini.File, key string, double bool, def Key) Key { - if !cfg.Section("input").HasKey(key) { - return def +func inputOrDef(keyName string, user *KeyHintTOML, def *KeyHintTOML, double bool) Key { + values := *def + if user != nil { + values = *user } - vals := cfg.Section("input").Key(key).Strings(",") - k, err := NewKey(vals, double) - if err != nil { - fmt.Printf("error parsing config for key %s. Error: %v\n", key, err) - os.Exit(1) + keys, special := []string{}, []string{} + if values.Keys != nil { + keys = *values.Keys } - return k -} -func inputStrOrErr(vals []string, double bool) Key { - k, err := NewKey(vals, double) + if values.SpecialKeys != nil { + special = *values.SpecialKeys + } + key, err := NewKey( + NilDefaultString(values.Hint, sp("")), + NilDefaultString(values.HintAlt, sp("")), + keys, special, + ) if err != nil { - fmt.Printf("error parsing config. Error: %v\n", err) + fmt.Printf("error parsing config for key %s. Error: %v\n", keyName, err) os.Exit(1) } - return k + return key } -func parseInput(cfg *ini.File) Input { - ic := Input{ - GlobalDown: inputStrOrErr([]string{"\"\"", "'j'", "'J'", "\"Down\""}, false), - GlobalUp: inputStrOrErr([]string{"\"\"", "'k'", "'k'", "\"Up\""}, false), - GlobalEnter: inputStrOrErr([]string{"\"\"", "\"Enter\""}, false), - GlobalBack: inputStrOrErr([]string{"\"[Esc]\"", "\"Esc\""}, false), - GlobalExit: inputStrOrErr([]string{"\"[Q]uit\"", "'q'", "'Q'"}, false), - - MainHome: inputStrOrErr([]string{"\"\"", "'g'", "\"Home\""}, false), - MainEnd: inputStrOrErr([]string{"\"\"", "'G'", "\"End\""}, false), - MainPrevFeed: inputStrOrErr([]string{"\"\"", "'h'", "'H'", "\"Left\""}, false), - MainNextFeed: inputStrOrErr([]string{"\"\"", "'l'", "'L'", "\"Right\""}, false), - MainPrevWindow: inputStrOrErr([]string{"\"\"", "\"Backtab\""}, false), - MainNextWindow: inputStrOrErr([]string{"\"\"", "\"Tab\""}, false), - MainCompose: inputStrOrErr([]string{"\"\"", "'c'", "'C'"}, false), - - StatusAvatar: inputStrOrErr([]string{"\"[A]vatar\"", "'a'", "'A'"}, false), - StatusBoost: inputStrOrErr([]string{"\"[B]oost\"", "\"Un[B]oost\"", "'b'", "'B'"}, true), - StatusDelete: inputStrOrErr([]string{"\"[D]elete\"", "'d'", "'D'"}, false), - StatusEdit: inputStrOrErr([]string{"\"[E]dit\"", "'e'", "'E'"}, false), - StatusFavorite: inputStrOrErr([]string{"\"[F]avorite\"", "\"Un[F]avorite\"", "'f'", "'F'"}, true), - StatusMedia: inputStrOrErr([]string{"\"[M]edia\"", "'m'", "'M'"}, false), - StatusLinks: inputStrOrErr([]string{"\"[O]pen\"", "'o'", "'O'"}, false), - StatusPoll: inputStrOrErr([]string{"\"[P]oll\"", "'p'", "'P'"}, false), - StatusReply: inputStrOrErr([]string{"\"[R]eply\"", "'r'", "'R'"}, false), - StatusBookmark: inputStrOrErr([]string{"\"[S]ave\"", "\"Un[S]ave\"", "'s'", "'S'"}, true), - StatusThread: inputStrOrErr([]string{"\"[T]hread\"", "'t'", "'T'"}, false), - StatusUser: inputStrOrErr([]string{"\"[U]ser\"", "'u'", "'U'"}, false), - StatusViewFocus: inputStrOrErr([]string{"\"[V]iew\"", "'v'", "'V'"}, false), - StatusYank: inputStrOrErr([]string{"\"[Y]ank\"", "'y'", "'Y'"}, false), - StatusToggleCW: inputStrOrErr([]string{"\"Press [Z] to toggle CW\"", "'z'", "'Z'"}, false), - StatusShowFiltered: inputStrOrErr([]string{"\"Press [Z] to view filtered toot\"", "'z'", "'Z'"}, false), - - UserAvatar: inputStrOrErr([]string{"\"[A]vatar\"", "'a'", "'A'"}, false), - UserBlock: inputStrOrErr([]string{"\"[B]lock\"", "\"Un[B]lock\"", "'b'", "'B'"}, true), - UserFollow: inputStrOrErr([]string{"\"[F]ollow\"", "\"Un[F]ollow\"", "'f'", "'F'"}, true), - UserFollowRequestDecide: inputStrOrErr([]string{"\"Follow [R]equest\"", "\"Follow [R]equest\"", "'r'", "'R'"}, true), - UserMute: inputStrOrErr([]string{"\"[M]ute\"", "\"Un[M]ute\"", "'m'", "'M'"}, true), - UserLinks: inputStrOrErr([]string{"\"[O]pen\"", "'o'", "'O'"}, false), - UserUser: inputStrOrErr([]string{"\"[U]ser\"", "'u'", "'U'"}, false), - UserViewFocus: inputStrOrErr([]string{"\"[V]iew\"", "'v'", "'V'"}, false), - UserYank: inputStrOrErr([]string{"\"[Y]ank\"", "'y'", "'Y'"}, false), - - ListOpenFeed: inputStrOrErr([]string{"\"[O]pen\"", "'o'", "'O'"}, false), - ListUserList: inputStrOrErr([]string{"\"[U]sers\"", "'u'", "'U'"}, false), - ListUserAdd: inputStrOrErr([]string{"\"[A]dd\"", "'a'", "'A'"}, false), - ListUserDelete: inputStrOrErr([]string{"\"[D]elete\"", "'d'", "'D'"}, false), - - TagOpenFeed: inputStrOrErr([]string{"\"[O]pen\"", "'o'", "'O'"}, false), - TagFollow: inputStrOrErr([]string{"\"[F]ollow\"", "\"Un[F]ollow\"", "'f'", "'F'"}, true), - - LinkOpen: inputStrOrErr([]string{"\"[O]pen\"", "'o'", "'O'"}, false), - LinkYank: inputStrOrErr([]string{"\"[Y]ank\"", "'y'", "'Y'"}, false), - - ComposeEditCW: inputStrOrErr([]string{"\"[C]W Text\"", "'c'", "'C'"}, false), - ComposeEditText: inputStrOrErr([]string{"\"[E]dit text\"", "'e'", "'E'"}, false), - ComposeIncludeQuote: inputStrOrErr([]string{"\"[I]nclude quote\"", "'i'", "'I'"}, false), - ComposeMediaFocus: inputStrOrErr([]string{"\"[M]edia\"", "'m'", "'M'"}, false), - ComposePost: inputStrOrErr([]string{"\"[P]ost\"", "'p'", "'P'"}, false), - ComposeToggleContentWarning: inputStrOrErr([]string{"\"[T]oggle CW\"", "'t'", "'T'"}, false), - ComposeVisibility: inputStrOrErr([]string{"\"[V]isibility\"", "'v'", "'V'"}, false), - ComposeLanguage: inputStrOrErr([]string{"\"[L]ang\"", "'l'", "'L'"}, false), - ComposePoll: inputStrOrErr([]string{"\"P[O]ll\"", "'o'", "'O'"}, false), - - MediaDelete: inputStrOrErr([]string{"\"[D]elete\"", "'d'", "'D'"}, false), - MediaEditDesc: inputStrOrErr([]string{"\"[E]dit desc\"", "'e'", "'E'"}, false), - MediaAdd: inputStrOrErr([]string{"\"[A]dd\"", "'a'", "'A'"}, false), - - VoteVote: inputStrOrErr([]string{"\"[V]ote\"", "'v'", "'V'"}, false), - VoteSelect: inputStrOrErr([]string{"\"[Enter] to select\"", "' '", "\"Enter\""}, false), - - PollAdd: inputStrOrErr([]string{"\"[A]dd\"", "'a'", "'A'"}, false), - PollEdit: inputStrOrErr([]string{"\"[E]dit\"", "'e'", "'E'"}, false), - PollDelete: inputStrOrErr([]string{"\"[D]elete\"", "'d'", "'D'"}, false), - PollMultiToggle: inputStrOrErr([]string{"\"Toggle [M]ultiple\"", "'m'", "'M'"}, false), - PollExpiration: inputStrOrErr([]string{"\"E[X]pires\"", "'x'", "'X'"}, false), - - PreferenceName: inputStrOrErr([]string{"\"[N]ame\"", "'n'", "'N'"}, false), - PreferenceBio: inputStrOrErr([]string{"\"[B]io\"", "'b'", "'B'"}, false), - PreferenceVisibility: inputStrOrErr([]string{"\"[V]isibility\"", "'v'", "'V'"}, false), - PreferenceSave: inputStrOrErr([]string{"\"[S]ave\"", "'s'", "'S'"}, false), - PreferenceFields: inputStrOrErr([]string{"\"[F]ields\"", "'f'", "'F'"}, false), - PreferenceFieldsAdd: inputStrOrErr([]string{"\"[A]dd\"", "'a'", "'A'"}, false), - PreferenceFieldsEdit: inputStrOrErr([]string{"\"[E]dit\"", "'e'", "'E'"}, false), - PreferenceFieldsDelete: inputStrOrErr([]string{"\"[D]elete\"", "'d'", "'D'"}, false), - } - ic.GlobalDown = inputOrErr(cfg, "global-down", false, ic.GlobalDown) - ic.GlobalUp = inputOrErr(cfg, "global-up", false, ic.GlobalUp) - ic.GlobalEnter = inputOrErr(cfg, "global-enter", false, ic.GlobalEnter) - ic.GlobalBack = inputOrErr(cfg, "global-back", false, ic.GlobalBack) - ic.GlobalExit = inputOrErr(cfg, "global-exit", false, ic.GlobalExit) - - ic.MainHome = inputOrErr(cfg, "main-home", false, ic.MainHome) - ic.MainEnd = inputOrErr(cfg, "main-end", false, ic.MainEnd) - ic.MainPrevFeed = inputOrErr(cfg, "main-prev-feed", false, ic.MainPrevFeed) - ic.MainNextFeed = inputOrErr(cfg, "main-next-feed", false, ic.MainNextFeed) - ic.MainCompose = inputOrErr(cfg, "main-compose", false, ic.MainCompose) - - ic.StatusAvatar = inputOrErr(cfg, "status-avatar", false, ic.StatusAvatar) - ic.StatusBoost = inputOrErr(cfg, "status-boost", true, ic.StatusBoost) - ic.StatusDelete = inputOrErr(cfg, "status-delete", false, ic.StatusDelete) - ic.StatusEdit = inputOrErr(cfg, "status-edit", false, ic.StatusEdit) - ic.StatusFavorite = inputOrErr(cfg, "status-favorite", true, ic.StatusFavorite) - ic.StatusMedia = inputOrErr(cfg, "status-media", false, ic.StatusMedia) - ic.StatusLinks = inputOrErr(cfg, "status-links", false, ic.StatusLinks) - ic.StatusPoll = inputOrErr(cfg, "status-poll", false, ic.StatusPoll) - ic.StatusReply = inputOrErr(cfg, "status-reply", false, ic.StatusReply) - ic.StatusBookmark = inputOrErr(cfg, "status-bookmark", true, ic.StatusBookmark) - ic.StatusThread = inputOrErr(cfg, "status-thread", false, ic.StatusThread) - ic.StatusUser = inputOrErr(cfg, "status-user", false, ic.StatusUser) - ic.StatusViewFocus = inputOrErr(cfg, "status-view-focus", false, ic.StatusViewFocus) - ic.StatusYank = inputOrErr(cfg, "status-yank", false, ic.StatusYank) - ic.StatusToggleCW = inputOrErr(cfg, "status-toggle-spoiler", false, ic.StatusToggleCW) - ts := cfg.Section("input").Key("status-toggle-spoiler").MustString("") - if ts != "" { - ic.StatusToggleCW = inputOrErr(cfg, "status-toggle-spoiler", false, ic.StatusToggleCW) - } else { - ic.StatusToggleCW = inputOrErr(cfg, "status-toggle-cw", false, ic.StatusToggleCW) - } - - ic.UserAvatar = inputOrErr(cfg, "user-avatar", false, ic.UserAvatar) - ic.UserBlock = inputOrErr(cfg, "user-block", true, ic.UserBlock) - ic.UserFollow = inputOrErr(cfg, "user-follow", true, ic.UserFollow) - ic.UserFollowRequestDecide = inputOrErr(cfg, "user-follow-request-decide", true, ic.UserFollowRequestDecide) - ic.UserMute = inputOrErr(cfg, "user-mute", true, ic.UserMute) - ic.UserLinks = inputOrErr(cfg, "user-links", false, ic.UserLinks) - ic.UserUser = inputOrErr(cfg, "user-user", false, ic.UserUser) - ic.UserViewFocus = inputOrErr(cfg, "user-view-focus", false, ic.UserViewFocus) - ic.UserYank = inputOrErr(cfg, "user-yank", false, ic.UserYank) - - ic.ListOpenFeed = inputOrErr(cfg, "list-open-feed", false, ic.ListOpenFeed) - ic.ListUserList = inputOrErr(cfg, "list-user-list", false, ic.ListUserList) - ic.ListUserAdd = inputOrErr(cfg, "list-user-add", false, ic.ListUserAdd) - ic.ListUserDelete = inputOrErr(cfg, "list-user-delete", false, ic.ListUserDelete) - - ic.TagOpenFeed = inputOrErr(cfg, "tag-open-feed", false, ic.TagOpenFeed) - ic.TagFollow = inputOrErr(cfg, "tag-follow", true, ic.TagFollow) - - ic.LinkOpen = inputOrErr(cfg, "link-open", false, ic.LinkOpen) - ic.LinkYank = inputOrErr(cfg, "link-yank", false, ic.LinkYank) - - es := cfg.Section("input").Key("compose-edit-spoiler").MustString("") - if es != "" { - ic.ComposeEditCW = inputOrErr(cfg, "compose-edit-spoiler", false, ic.ComposeEditCW) - } else { - ic.ComposeEditCW = inputOrErr(cfg, "compose-edit-cw", false, ic.ComposeEditCW) - } - ic.ComposeEditText = inputOrErr(cfg, "compose-edit-text", false, ic.ComposeEditText) - ic.ComposeIncludeQuote = inputOrErr(cfg, "compose-include-quote", false, ic.ComposeIncludeQuote) - ic.ComposeMediaFocus = inputOrErr(cfg, "compose-media-focus", false, ic.ComposeMediaFocus) - ic.ComposePost = inputOrErr(cfg, "compose-post", false, ic.ComposePost) - ic.ComposeToggleContentWarning = inputOrErr(cfg, "compose-toggle-content-warning", false, ic.ComposeToggleContentWarning) - ic.ComposeVisibility = inputOrErr(cfg, "compose-visibility", false, ic.ComposeVisibility) - ic.ComposeLanguage = inputOrErr(cfg, "compose-language", false, ic.ComposeLanguage) - ic.ComposePoll = inputOrErr(cfg, "compose-poll", false, ic.ComposePoll) - - ic.MediaDelete = inputOrErr(cfg, "media-delete", false, ic.MediaDelete) - ic.MediaEditDesc = inputOrErr(cfg, "media-edit-desc", false, ic.MediaEditDesc) - ic.MediaAdd = inputOrErr(cfg, "media-add", false, ic.MediaAdd) - - ic.VoteVote = inputOrErr(cfg, "vote-vote", false, ic.VoteVote) - ic.VoteSelect = inputOrErr(cfg, "vote-select", false, ic.VoteSelect) - - ic.PollAdd = inputOrErr(cfg, "poll-add", false, ic.PollAdd) - ic.PollEdit = inputOrErr(cfg, "poll-edit", false, ic.PollEdit) - ic.PollDelete = inputOrErr(cfg, "poll-delete", false, ic.PollDelete) - ic.PollMultiToggle = inputOrErr(cfg, "poll-multi-toggle", false, ic.PollMultiToggle) - ic.PollExpiration = inputOrErr(cfg, "poll-expiration", false, ic.PollExpiration) - - ic.PreferenceName = inputOrErr(cfg, "preference-name", false, ic.PreferenceName) - ic.PreferenceVisibility = inputOrErr(cfg, "preference-visibility", false, ic.PreferenceVisibility) - ic.PreferenceBio = inputOrErr(cfg, "preference-bio", false, ic.PreferenceBio) - ic.PreferenceSave = inputOrErr(cfg, "preference-save", false, ic.PreferenceSave) - ic.PreferenceFields = inputOrErr(cfg, "preference-fields", false, ic.PreferenceFields) - ic.PreferenceFieldsAdd = inputOrErr(cfg, "preference-fields-add", false, ic.PreferenceFieldsAdd) - ic.PreferenceFieldsEdit = inputOrErr(cfg, "preference-fields-edit", false, ic.PreferenceFieldsEdit) - ic.PreferenceFieldsDelete = inputOrErr(cfg, "preference-fields-delete", false, ic.PreferenceFieldsDelete) +func parseInput(cfg InputTOML) Input { + def := ConfigDefault.Input + ic := Input{} + ic.GlobalDown = inputOrDef("global-down", cfg.GlobalDown, def.GlobalDown, false) + ic.GlobalUp = inputOrDef("global-up", cfg.GlobalUp, def.GlobalUp, false) + ic.GlobalEnter = inputOrDef("global-enter", cfg.GlobalEnter, def.GlobalEnter, false) + ic.GlobalBack = inputOrDef("global-back", cfg.GlobalBack, def.GlobalBack, false) + ic.GlobalExit = inputOrDef("global-exit", cfg.GlobalExit, def.GlobalExit, false) + + ic.MainHome = inputOrDef("main-home", cfg.MainHome, def.MainHome, false) + ic.MainEnd = inputOrDef("main-end", cfg.MainEnd, def.MainEnd, false) + ic.MainPrevFeed = inputOrDef("main-prev-feed", cfg.MainPrevFeed, def.MainPrevFeed, false) + ic.MainNextFeed = inputOrDef("main-next-feed", cfg.MainNextFeed, def.MainNextFeed, false) + ic.MainNextPane = inputOrDef("main-next-pane", cfg.MainNextPane, def.MainNextPane, false) + ic.MainPrevPane = inputOrDef("main-prev-pane", cfg.MainPrevPane, def.MainPrevPane, false) + ic.MainCompose = inputOrDef("main-compose", cfg.MainCompose, def.MainCompose, false) + ic.MainNextAccount = inputOrDef("main-next-account", cfg.MainNextAccount, def.MainNextAccount, false) + ic.MainPrevAccount = inputOrDef("main-prev-account", cfg.MainPrevAccount, def.MainPrevAccount, false) + + ic.StatusAvatar = inputOrDef("status-avatar", cfg.StatusAvatar, def.StatusAvatar, false) + ic.StatusBoost = inputOrDef("status-boost", cfg.StatusBoost, def.StatusBoost, true) + ic.StatusDelete = inputOrDef("status-delete", cfg.StatusDelete, def.StatusDelete, false) + ic.StatusEdit = inputOrDef("status-edit", cfg.StatusEdit, def.StatusEdit, false) + ic.StatusFavorite = inputOrDef("status-favorite", cfg.StatusFavorite, def.StatusFavorite, true) + ic.StatusMedia = inputOrDef("status-media", cfg.StatusMedia, def.StatusMedia, false) + ic.StatusLinks = inputOrDef("status-links", cfg.StatusLinks, def.StatusLinks, false) + ic.StatusPoll = inputOrDef("status-poll", cfg.StatusPoll, def.StatusPoll, false) + ic.StatusReply = inputOrDef("status-reply", cfg.StatusReply, def.StatusReply, false) + ic.StatusBookmark = inputOrDef("status-bookmark", cfg.StatusBookmark, def.StatusBookmark, true) + ic.StatusThread = inputOrDef("status-thread", cfg.StatusThread, def.StatusThread, false) + ic.StatusUser = inputOrDef("status-user", cfg.StatusUser, def.StatusUser, false) + ic.StatusViewFocus = inputOrDef("status-view-focus", cfg.StatusViewFocus, def.StatusViewFocus, false) + ic.StatusYank = inputOrDef("status-yank", cfg.StatusYank, def.StatusYank, false) + ic.StatusToggleCW = inputOrDef("status-toggle-cw", cfg.StatusToggleCW, def.StatusToggleCW, false) + ic.StatusShowFiltered = inputOrDef("status-show-filtered", cfg.StatusShowFiltered, def.StatusShowFiltered, false) + + ic.UserAvatar = inputOrDef("user-avatar", cfg.UserAvatar, def.UserAvatar, false) + ic.UserBlock = inputOrDef("user-block", cfg.UserBlock, def.UserBlock, true) + ic.UserFollow = inputOrDef("user-follow", cfg.UserFollow, def.UserFollow, true) + ic.UserFollowRequestDecide = inputOrDef("user-follow-request-decide", cfg.UserFollowRequestDecide, def.UserFollowRequestDecide, true) + ic.UserMute = inputOrDef("user-mute", cfg.UserMute, def.UserMute, true) + ic.UserLinks = inputOrDef("user-links", cfg.UserLinks, def.UserLinks, false) + ic.UserUser = inputOrDef("user-user", cfg.UserUser, def.UserUser, false) + ic.UserViewFocus = inputOrDef("user-view-focus", cfg.UserViewFocus, def.UserViewFocus, false) + ic.UserYank = inputOrDef("user-yank", cfg.UserYank, def.UserYank, false) + + ic.ListOpenFeed = inputOrDef("list-open-feed", cfg.ListOpenFeed, def.ListOpenFeed, false) + ic.ListUserList = inputOrDef("list-user-list", cfg.ListUserList, def.ListUserList, false) + ic.ListUserAdd = inputOrDef("list-user-add", cfg.ListUserAdd, def.ListUserAdd, false) + ic.ListUserDelete = inputOrDef("list-user-delete", cfg.ListUserDelete, def.ListUserDelete, false) + + ic.TagOpenFeed = inputOrDef("tag-open-feed", cfg.TagOpenFeed, def.TagOpenFeed, false) + ic.TagFollow = inputOrDef("tag-follow", cfg.TagFollow, def.TagFollow, true) + ic.LinkOpen = inputOrDef("link-open", cfg.LinkOpen, def.LinkOpen, false) + ic.LinkYank = inputOrDef("link-yank", cfg.LinkYank, def.LinkYank, false) + + ic.ComposeEditCW = inputOrDef("compose-edit-cw", cfg.ComposeEditCW, def.ComposeEditCW, false) + ic.ComposeEditText = inputOrDef("compose-edit-text", cfg.ComposeEditText, def.ComposeEditText, false) + ic.ComposeIncludeQuote = inputOrDef("compose-include-quote", cfg.ComposeIncludeQuote, def.ComposeIncludeQuote, false) + ic.ComposeMediaFocus = inputOrDef("compose-media-focus", cfg.ComposeMediaFocus, def.ComposeMediaFocus, false) + ic.ComposePost = inputOrDef("compose-post", cfg.ComposePost, def.ComposePost, false) + ic.ComposeToggleContentWarning = inputOrDef("compose-toggle-content-warning", cfg.ComposeToggleContentWarning, def.ComposeToggleContentWarning, false) + ic.ComposeVisibility = inputOrDef("compose-visibility", cfg.ComposeVisibility, def.ComposeVisibility, false) + ic.ComposeLanguage = inputOrDef("compose-language", cfg.ComposeLanguage, def.ComposeLanguage, false) + ic.ComposePoll = inputOrDef("compose-poll", cfg.ComposePoll, def.ComposePoll, false) + + ic.MediaDelete = inputOrDef("media-delete", cfg.MediaDelete, def.MediaDelete, false) + ic.MediaEditDesc = inputOrDef("media-edit-desc", cfg.MediaEditDesc, def.MediaEditDesc, false) + ic.MediaAdd = inputOrDef("media-add", cfg.MediaAdd, def.MediaAdd, false) + + ic.VoteVote = inputOrDef("vote-vote", cfg.VoteVote, def.VoteVote, false) + ic.VoteSelect = inputOrDef("vote-select", cfg.VoteSelect, def.VoteSelect, false) + + ic.PollAdd = inputOrDef("poll-add", cfg.PollAdd, def.PollAdd, false) + ic.PollEdit = inputOrDef("poll-edit", cfg.PollEdit, def.PollEdit, false) + ic.PollDelete = inputOrDef("poll-delete", cfg.PollDelete, def.PollDelete, false) + ic.PollMultiToggle = inputOrDef("poll-multi-toggle", cfg.PollMultiToggle, def.PollMultiToggle, false) + ic.PollExpiration = inputOrDef("poll-expiration", cfg.PollExpiration, def.PollExpiration, false) + + ic.PreferenceName = inputOrDef("preference-name", cfg.PreferenceName, def.PreferenceName, false) + ic.PreferenceVisibility = inputOrDef("preference-visibility", cfg.PreferenceVisibility, def.PreferenceVisibility, false) + ic.PreferenceBio = inputOrDef("preference-bio", cfg.PreferenceBio, def.PreferenceBio, false) + ic.PreferenceSave = inputOrDef("preference-save", cfg.PreferenceSave, def.PreferenceSave, false) + ic.PreferenceFields = inputOrDef("preference-fields", cfg.PreferenceFields, def.PreferenceFields, false) + ic.PreferenceFieldsAdd = inputOrDef("preference-fields-add", cfg.PreferenceFieldsAdd, def.PreferenceFieldsAdd, false) + ic.PreferenceFieldsEdit = inputOrDef("preference-fields-edit", cfg.PreferenceFieldsEdit, def.PreferenceFieldsEdit, false) + ic.PreferenceFieldsDelete = inputOrDef("preference-fields-delete", cfg.PreferenceFieldsDelete, def.PreferenceFieldsDelete, false) + + ic.EditorExit = inputOrDef("editor-exit", cfg.EditorExit, def.EditorExit, false) return ic } func parseConfig(filepath string, cnfPath string, cnfDir string) (Config, error) { - cfg, err := ini.LoadSources(ini.LoadOptions{ - SpaceBeforeInlineComment: true, - AllowShadows: true, - }, filepath) conf := Config{} + f, err := os.Open(filepath) if err != nil { + log.Fatalln(err) return conf, err } - conf.General = parseGeneral(cfg) - conf.Media = parseMedia(cfg) - conf.Style = parseStyle(cfg, cnfPath, cnfDir) - conf.OpenPattern = parseOpenPattern(cfg) - conf.OpenCustom = parseCustom(cfg) - conf.NotificationConfig = parseNotifications(cfg) - conf.Templates = parseTemplates(cfg, cnfPath, cnfDir) - conf.Input = parseInput(cfg) + var cnf ConfigTOML + toml.NewDecoder(f).Decode(&cnf) + if err != nil { + log.Fatalln(err) + } + f.Close() + conf.General = parseGeneral(cnf.General) + conf.Media = parseMedia(cnf.Media) + conf.Style = parseStyle(cnf.Style, cnfPath, cnfDir) + conf.OpenPattern = parseOpenPattern(cnf.OpenPattern) + conf.OpenCustom = parseCustom(cnf.OpenCustom) + conf.NotificationConfig = parseNotifications(cnf.NotificationConfig) + conf.Templates = parseTemplates(cnf, cnfPath, cnfDir) + conf.Input = parseInput(cnf.Input) return conf, nil } @@ -1612,7 +1408,7 @@ func createConfigDir() error { } func checkConfig(filename string, cnfPath string, cnfDir string) (path string, exists bool, err error) { - if cnfPath != "" && filename == "config.ini" { + if cnfPath != "" && filename == "config.toml" { _, err = os.Stat(cnfPath) if os.IsNotExist(err) { return cnfPath, false, nil @@ -1701,7 +1497,7 @@ func getThemes(cnfPath string, cnfDir string) (bundled []string, local []string, return bundled, local, nil } -func getTheme(fname string, isLocal bool, cnfDir string) (*ini.File, error) { +func getTheme(fname string, isLocal bool, cnfDir string) (StyleTOML, error) { var f io.Reader var err error if isLocal { @@ -1716,44 +1512,22 @@ func getTheme(fname string, isLocal bool, cnfDir string) (*ini.File, error) { dir = filepath.Join(cd, "/tut/themes") } f, err = os.Open( - filepath.Join(dir, fmt.Sprintf("%s.ini", strings.TrimSpace(fname))), + filepath.Join(dir, fmt.Sprintf("%s.toml", strings.TrimSpace(fname))), ) } else { - f, err = themesFS.Open(fmt.Sprintf("themes/%s.ini", strings.TrimSpace(fname))) + f, err = themesFS.Open(fmt.Sprintf("themes/%s.toml", strings.TrimSpace(fname))) } if err != nil { - return nil, err + return StyleTOML{}, err } - content, err := io.ReadAll(f) + var style StyleTOML + toml.NewDecoder(f).Decode(&style) if err != nil { - return nil, err + return style, err } - cfg, err := ini.LoadSources(ini.LoadOptions{ - SpaceBeforeInlineComment: true, - }, content) - if err != nil { - return nil, err - } - keys := []string{ - "background", - "text", - "subtle", - "warning-text", - "text-special-one", - "text-special-two", - "top-bar-background", - "top-bar-text", - "status-bar-background", - "status-bar-text", - "status-bar-view-background", - "status-bar-view-text", - "list-selected-background", - "list-selected-text", - } - for _, k := range keys { - if !cfg.Section("").HasKey(k) { - return nil, fmt.Errorf("theme %s is missing %s", fname, k) - } + switch x := f.(type) { + case *os.File: + x.Close() } - return cfg, nil + return style, nil } diff --git a/config/default_config.go b/config/default_config.go index 88962dc..182a3a1 100644 --- a/config/default_config.go +++ b/config/default_config.go @@ -3,64 +3,28 @@ package config var conftext = `# Configuration file for tut [general] -# Shows a confirmation view before actions such as favorite, delete toot, boost +# What editor to use. TUT_USE_INTERNAL will use the editor that comes with tut. +# If you want you can set this to $EDITOR to use your environment variable or +# vim if you want to specify the program directly. +# default="TUT_USE_INTERNAL" +editor="TUT_USE_INTERNAL" + +# You need to press yes in a confirmation dialog before favoriting, boosting, # etc. # default=true confirmation=true -# Enable support for using the mouse in tut to select items. +# Enable mouse support in tut. # default=false mouse-support=false -# Timelines adds windows of feeds. You can customize the number of feeds, what -# they should show and the key to activate them. -# -# Available timelines: home, direct, local, federated, special, bookmarks, -# saved, favorited, notifications, lists, mentions, tag -# -# The one named special are the home timeline with only boosts and/or replies. -# -# Tag is special as you need to add the tag after, see the example below. -# -# The syntax is: -# timelines=feed,[name],[keys...],[showBoosts],[showReplies] -# -# Tha values in brackets are optional. You can see the syntax for keys under the -# [input] section. -# -# showBoosts and showReplies must be formated as bools. So either true or false. -# They always defaults to true. -# -# Some examples: -# -# home timeline with the name Home -# timelines=home,Home -# -# local timeline with the name Local and it gets focus when you press 2. It will -# also hide boosts in the timeline, but show toots that are replies. -# timelines=local,Local,'2',false,true -# -# notification timeline with the name [N]otifications and it gets focus when you -# press n or N -# timelines=notifications,[N]otifications,'n','N' -# -# tag timeline for #linux with the name Linux and it gets focus when you press -# timelines=tag linux,Linux,"F2" -# -# -# If you don't set any timelines it will default to this: -# timelines=home -# timelines=notifications,[N]otifications,'n','N' -# - - -# The date format to be used. See https://godoc.org/time#Time.Format -# default=2006-01-02 15:04 -date-format=2006-01-02 15:04 +# The date format to be used. See https://pkg.go.dev/time#pkg-constants +# default="2006-01-02 15:04" +date-format="2006-01-02 15:04" # Format for dates the same day. See date-format for more info. -# default=15:04 -date-today-format=15:04 +# default="15:04" +date-tody-format="15:04" # This displays relative dates instead for statuses that are one day or older # the output is 1y2m1d (1 year 2 months and 1 day) @@ -69,55 +33,49 @@ date-today-format=15:04 # -1 = don't use relative dates # 0 = always use relative dates, except for dates < 1 day # 1 - ∞ = number of days to use relative dates -# -# Example: date-relative=28 will display a relative date for toots that are -# between 1-28 days old. Otherwhise it will use the short or long format. +# +# Value: 28 will display a relative date for toots that are between 1-28 days +# old. Otherwhise it will use the short or long format. # default=-1 date-relative=-1 -# The max width of text before it wraps when displaying toots. -# 0 = no restriction. +# The max with of text before it wraps when displaying a toot. # default=0 max-width=0 -# Where do you want the list of toots to be placed? -# Valid values: left, right, top, bottom. -# default=left -list-placement=left - -# If you have notification-feed set to true you can display it under the main -# list of toots (row) or place it to the right of the main list of toots -# (column). -# default=row -list-split=row - -# You can change the proportions of the list view in relation to the content -# view list-proportion=1 and content-proportoin=3 will result in the content -# taking up 3 times more space. -# Must be n > 0 +# The placement of your panes. +# valid: left, right, top, bottom +# default="left" +list-placement="left" + +# How should panes be split? +# valid: row, column +# default="row" +list-split="row" + +# The proportion of panes vs. content. 1 on this and 3 on content below results +# in content taking up 3 times more space. # default=1 list-proportion=1 -# See list-proportion +# See previous. # default=2 content-proportion=2 -# Hide notifications of this type. If you have multiple you separate them with a -# comma. Valid types: mention, status, boost, follow, follow_request, favorite, -# poll, edit. -# default= -notifications-to-hide= +# Hide notifications of this type in your notification timelines. +# valid: mention, status, boost, follow, follow_request, favorite, poll, edit +# default=[] +notifications-to-hide=[] -# If you always want to quote original message when replying. +# Always include a quote of the message you're replying to. # default=false quote-reply=false -# If you want to show icons in the list of toots. +# If you want to show icons in timelines. # default=true show-icons=true -# If you've learnt all the shortcut keys you can remove the help text and only -# show the key in tui. So it gets less cluttered. +# If you only want to you the letter of keys instead of the full hint. # default=false short-hints=false @@ -125,39 +83,47 @@ short-hints=false # default=true show-filter-phrase=true -# If you want to show a message in the cmdbar on how to access the help text. +# Display a message in the commandbar on how to access the help text. # default=true show-help=true -# If you always want tut to jump to the newest post. May ruin your reading -# experience. +# Always jump to the newest post. May ruin your reading experience. # default=false stick-to-top=false -# If you want to display the username of the person being boosted instead of the -# person that boosted. +# Display the username of the person being boosted insted of the person that +# boosted. # default=false show-boosted-user=false +# Open a new pane when you run a command like :timeline home. +# default=true +commands-in-new-pane=true + +# Set a default name for the timeline if the name is empty. So if you run :tag +# linux the title of the pane will be set to #linux +# default=true +dynamic-timeline-name=true + # 0 = No terminal title # 1 = Show title in terminal and top bar -# 2 = Only show terminal title, and no top bar in tut. +# 2 = Only show terminal title, and no top bar in tut +# 3 = No terminal title and no top bar in tut. +# valid: 0, 1, 2, 4 # default=0 terminal-title=0 -# If you don't want the whole UI to update, and only the text content you can -# set this option to true. This will lead to some artifacts being left on the -# screen when emojis are present. But it will keep the UI from flashing on every -# single toot in some terminals. +# If you don't want the whole UI to update, and only update the text content you +# can disable this. This will lead to some artifacts being left on the screen +# when emojis are present. # default=true redraw-ui=true # The leader is used as a shortcut to run commands as you can do in Vim. By -# default this is disabled and you enable it by setting a leader-key. It can -# only consist of one char and I like to use comma as leader key. So to set it -# you write leader-key=, -# default= -leader-key= +# default this is disabled and you enable it by setting a key here. It can only +# consist of one char, so set it to something like a comma. +# default="" +leader-key="" # Number of milliseconds before the leader command resets. So if you tap the # leader-key by mistake or are to slow it empties all the input after X @@ -165,665 +131,1141 @@ leader-key= # default=1000 leader-timeout=1000 -# You set actions for the leader-key with one or more leader-action. It consists -# of two parts first the action then the shortcut. And they're separated by a -# comma. -# -# Available commands: blocking, bookmarks, boosts, clear-notifications, -# close-window, compose, direct, edit, favorited, favorites, federated, -# followers, following, history, home, list-placement, list-split, lists, local, -# mentions, move-window-left, move-window-right, move-window-up, -# move-window-down, move-window-home, move-window-end, muting, newer, -# notifications, preferences, profile, proportions, refetch, saved, special-all, -# special-boosts, special-replies, stick-to-top, switch, tag, tags, window +# [[general.timelines]] +# Timelines adds panes of feeds. You can customize the number of feeds, what +# they should show and the key to activate them. + +# Example: +# [[general.timelines]] +# name="home" +# type="home" +# hide-boosts=false +# hide-replies=false # -# The ones named special-* are the home timeline with only boosts and/or -# replies. All contains both, -boosts only boosts and -replies only replies. +# [[general.timelines]] +# name="Notifications" +# type="notifications" +# keys=["n", "N"] +# closed=true +# on-creation-closed="new-pane" +# on-focus="focus-self" + +# The name to display above the timeline +# default="" +# name="" + +# The type of the timeline +# valid: home, direct, local, federated, bookmarks, saved, favorited, notifications, +# lists, mentions, tag +# default="" +# type="" + +# Used for the tag type, so here you set the tag. If you have multiple you +# seperate them with a space. +# default="" +# data="" + +# A list of keys to give this timeline focus. See under the input section to +# learn more about keys. +# default=[] +# keys=[] + +# A list of special-keys to give this timeline focus. See under the input +# section to learn more about special-keys. +# default=[] +# special-keys=[] + +# A shortcut to give this timeline focus with your leader-key + this shortcut. +# default="" +# shortcut="" + +# Hide boosts in this timeline. +# default="false" +# hide-boosts="false" + +# Hide replies in this timeline. +# default="false" +# hide-replies="false" + +# Don't open this timeline when you start tut. Use your keys or shortcut to open +# it. +# default="false" +# closed="false" + +# Don't open this timeline when you start tut. Use your keys or shortcut to open +# it. +# valid: new-pane, current-pane +# default="new-pane" +# on-creation-closed="new-pane" + +# Don't open this timeline when you start tut. Use your keys or shortcut to open +# it. +# valid: focus-pane, focus-self +# default="focus-pane" +# on-focus="focus-pane" + +# [[general.leader-actions]] +# You set actions leader-key with one or more leader-actions. # # The shortcuts are up to you, but keep them quite short and make sure they # don't collide. If you have one shortcut that is "f" and an other one that is # "fav", the one with "f" will always run and "fav" will never run. # -# Some special leaders: -# tag is special as you need to add the tag after, e.g. tag linux -# window is special as it's a shortcut for switching between the timelines -# you've set under general and they are zero indexed. window 0 = your first -# timeline, window 1 = your second and so on. +# Some special actions that requires data to be set: +# pane is special as it's a shortcut for switching between the panes you've set +# under general and they are zero indexed. pane 0 = your first timeline, pane 1 +# = your second and so on. # list-placement as it takes the argument top, right, bottom or left # list-split as it takes the argument column or row # proportions takes the arguments [int] [int], where the first integer is the # list and the other content, e.g. proportions 1 3. See list-proportion above # for more information. -# switch let's you go to a timeline if it already exists, if it doesn't it will -# open the timeline in a new window. The syntax is almost the same as in -# timelines= and is displayed under the examples. -# -# Some examples: -# leader-action=local,lo -# leader-action=lists,li -# leader-action=federated,fed -# leader-action=direct,d -# leader-action=history,h -# leader-action=tag linux,tl -# leader-action=window 0,h -# leader-action=list-placement bottom,b -# leader-action=list-split column,c -# leader-action=proportions 1 3,3 + +# Example: +# [[general.leader-actions]] +# type="close-pane" +# shortcut="q" # -# Syntax for switch: -# leader-action=switch feed,shortcut,[name],[showBoosts],[showReplies] -# showBoosts can be either true or false and they are both optional. Here are -# some examples: +# [[general.leader-actions]] +# type="list-split" +# data="row" +# shortcut="r" # -# leader-action=switch home,h,false,true -# leader-action=switch tag tut,tt +# [[general.leader-actions]] +# type="list-split" +# data="column" +# shortcut="c" # +# The action you want to run. +# valid: blocking, boosts, clear-notifications, close-pane, compose, edit, favorited, +# favorites, followers, following, history, list-placement, list-split, lists, +# move-pane-left, move-pane-right, move-pane-up, move-pane-down, move-pane-home, +# move-pane-end, muting, newer, pane, preferences, profile, proportions, +# refetch, stick-to-top, tags +# default="" +# type="" + +# Data to pass to the action. +# default="" +# data="" + +# A shortcut to run this action with your leader-key + this shortcut. +# default="" +# shortcut="" [media] -# Your image viewer. -# default=xdg-open -image-viewer=xdg-open +# Media files will be removed directly after they've been opened. Some programs +# doesn't like this, so if your media doesn't open, try set this to false. Tut +# will remove all files once you close the program. +# default=true +delete-temp-files=true + +[media.image] +# The program to open images. TUT_OS_DEFAULT equals xdg-open on Linux, open on +# MacOS and start on Windows. +# default="TUT_OS_DEFAULT" +program="TUT_OS_DEFAULT" -# Open the image viewer in the same terminal as toot. Only for terminal based -# viewers. +# Arguments to pass to the program. +# default="" +args="" + +# If the program runs in the terminal set this to true. # default=false -image-terminal=false +terminal=false -# If images should open one by one e.g. "imv image.png" multiple times. If set -# to false all images will open at the same time like this "imv image1.png -# image2.png image3.png". Not all image viewers support this, so try it first. +# If the program should be called multiple times when there is multiple files. +# If set to false all files will be passed as an argument, but not all programs +# support this. # default=true -image-single=true +single=true -# If you want to open the images in reverse order. In some image viewers this -# will display the images in the "right" order. +# If the files should be passed in reverse order. This will make some programs +# display the files in the correct order. # default=false -image-reverse=false +reverse=false + +[media.video] +# The program to open videos. TUT_OS_DEFAULT equals xdg-open on Linux, open on +# MacOS and start on Windows. +# default="TUT_OS_DEFAULT" +program="TUT_OS_DEFAULT" -# Your video viewer. -# default=xdg-open -video-viewer=xdg-open +# Arguments to pass to the program. +# default="" +args="" -# Open the video viewer in the same terminal as toot. Only for terminal based -# viewers. +# If the program runs in the terminal set this to true. # default=false -video-terminal=false +terminal=false -# If videos should open one by one. See image-single. +# If the program should be called multiple times when there is multiple files. +# If set to false all files will be passed as an argument, but not all programs +# support this. # default=true -video-single=true +single=true -# If you want your videos in reverse order. In some video apps this will play -# the files in the "right" order. +# If the files should be passed in reverse order. This will make some programs +# display the files in the correct order. # default=false -video-reverse=false +reverse=false -# Your audio viewer. -# default=xdg-open -audio-viewer=xdg-open +[media.audio] +# The program to open audio. TUT_OS_DEFAULT equals xdg-open on Linux, open on +# MacOS and start on Windows. +# default="TUT_OS_DEFAULT" +program="TUT_OS_DEFAULT" -# Open the audio viewer in the same terminal as toot. Only for terminal based -# viewers. +# Arguments to pass to the program. +# default="" +args="" + +# If the program runs in the terminal set this to true. # default=false -audio-terminal=false +terminal=false -# If audio should open one by one. See image-single. +# If the program should be called multiple times when there is multiple files. +# If set to false all files will be passed as an argument, but not all programs +# support this. # default=true -audio-single=true +single=true -# If you want to play the audio files in reverse order. In some audio apps this -# will play the files in the "right" order. +# If the files should be passed in reverse order. This will make some programs +# display the files in the correct order. # default=false -audio-reverse=false +reverse=false -# Your web browser. -# default=xdg-open -link-viewer=xdg-open +[media.link] +# The program to open links. TUT_OS_DEFAULT equals xdg-open on Linux, open on +# MacOS and start on Windows. +# default="TUT_OS_DEFAULT" +program="TUT_OS_DEFAULT" -# Open the browser in the same terminal as toot. Only for terminal based -# browsers. -# default=false -link-terminal=false +# Arguments to pass to the program. +# default="" +args="" -[open-custom] -# This sections allows you to set up to five custom programs to open URLs with. -# If the url points to an image, you can set c1-name to img and c1-use to imv. -# If the program runs in a terminal and you want to run it in the same terminal -# as tut. Set cX-terminal to true. The name will show up in the UI, so keep it -# short so all five fits. -# -# c1-name=name -# c1-use=program -# c1-terminal=false -# -# c2-name=name -# c2-use=program -# c2-terminal=false -# -# c3-name=name -# c3-use=program -# c3-terminal=false -# -# c4-name=name -# c4-use=program -# c4-terminal=false -# -# c5-name=name -# c5-use=program -# c5-terminal=false - -[open-pattern] -# Here you can set your own glob patterns for opening matching URLs in the -# program you want them to open up in. You could for example open Youtube videos -# in your video player instead of your default browser. -# -# You must name the keys foo-pattern, foo-use and foo-terminal, where use is the -# program that will open up the URL. To see the syntax for glob pattern you can -# follow this URL https://github.com/gobwas/glob#syntax. foo-terminal is if the -# program runs in the terminal and should open in the same terminal as tut -# itself. -# -# Example for youtube.com and youtu.be to open up in mpv instead of the browser. -# -# y1-pattern=*youtube.com/watch* -# y1-use=mpv -# y1-terminal=false -# -# y2-pattern=*youtu.be/* -# y2-use=mpv -# y2-terminal=false +# If the program runs in the terminal set this to true. +# default=false +terminal=false [desktop-notification] -# Notification when someone follows you. +# Enable notifications when someone follows you. # default=false followers=false -# Notification when someone favorites one of your toots. +# Enable notifications when one of your toots gets favorited. # default=false favorite=false -# Notification when someone mentions you. +# Enable notifications when someone mentions you. # default=false mention=false -# Notification when someone edits their toot. +# Enable notifications when a post you have interacted with gets edited. # default=false update=false -# Notification when someone boosts one of your toots. +# Enable notifications when one of your toots gets boosted. # default=false boost=false -# Notification of poll results. +# Enable notifications when a poll ends. # default=false poll=false -# Notification when there is new posts in current timeline. +# Enable notifications for new posts. # default=false posts=false +[open-custom] +# [[open-custom.programs]] +# The program to open the file with. +# default="" +# program="" + +# Arguments to pass to the program. +# default="" +# args="" + +# If the program runs in the terminal set this to true. +# default=false +# terminal=false + +# What should the key hint in tut be for this program. See under the input +# section to learn more about hint. +# default="" +# hint="" + +# A list of keys to to open files with this program. See under the input section +# to learn more about keys. +# default=[] +# keys=[] + +# A list of special-keys to open files with this program. See under the input +# section to learn more about special-keys. +# default=[] +# special-keys=[] + +[open-pattern] +# [[open-pattern.programs]] +# Here you can set your own glob patterns for opening matching URLs in the +# program you want them to open up in. You could for example open Youtube videos +# in your video player instead of your default browser. To see the syntax for +# glob pattern you can follow this URL https://github.com/gobwas/glob#syntax. +# default="" +# matching="" + +# The program to open the file with. +# default="" +# program="" + +# Arguments to pass to the program. +# default="" +# args="" + +# If the program runs in the terminal set this to true. +# default=false +# terminal=false + [style] # All styles can be represented in their HEX value like #ffffff or with their # name, so in this case white. The only special value is "default" which equals # to transparent, so it will be the same color as your terminal. -# # You can also use xrdb colors like this xrdb:color1 The program will use colors # prefixed with an * first then look for URxvt or XTerm if it can't find any # color prefixed with an asterisk. If you don't want tut to guess the prefix you # can set the prefix yourself. If the xrdb color can't be found a preset color -# will be used. You'll have to set theme=none for this to work. - -# The xrdb prefix used for colors in .Xresources. -# default=guess -xrdb-prefix=guess +# will be used. You'll have to set theme="none" for this to work. -# You can use some themes that comes bundled with tut. Check out the themes -# available on the URL below. If a theme is named "nord.ini" you just write -# theme=nord +# The theme to use. You can use some themes that comes bundled with tut. Check +# out the themes available on the URL below. If a theme is named nord.toml you +# just write theme="nord". # # https://github.com/RasmusLindroth/tut/tree/master/config/themes # # You can also create a theme file in your config directory e.g. -# ~/.config/tut/themes/foo.ini and then set theme=foo. +# ~/.config/tut/themes/foo.toml and then set theme=foo. # # If you want to use your own theme but don't want to create a new file, set -# theme=none and then you can create your own theme below. -# default=default -theme=default +# theme="none" and then you can create your own theme below. +# +# default="default" +theme="default" + +# The xrdb prefix used for colors in .Xresources. +# default="guess" +xrdb-prefix="guess" # The background color used on most elements. -# default= -background= +# default="" +background="" # The text color used on most of the text. -# default= -text= +# default="" +text="" # The color to display subtle elements or subtle text. Like lines and help text. -# default= -subtle= +# default="" +subtle="" # The color for errors or warnings -# default= -warning-text= +# default="" +warning-text="" # This color is used to display username. -# default= -text-special-one= +# default="" +text-special-one="" # This color is used to display username and key hints. -# default= -text-special-two= +# default="" +text-special-two="" # The color of the bar at the top -# default= -top-bar-background= +# default="" +top-bar-background="" # The color of the text in the bar at the top. -# default= -top-bar-text= +# default="" +top-bar-text="" # The color of the bar at the bottom -# default= -status-bar-background= +# default="" +status-bar-background="" # The color of the text in the bar at the bottom. -# default= -status-bar-text= +# default="" +status-bar-text="" # The color of the bar at the bottom in view mode. -# default= -status-bar-view-background= +# default="" +status-bar-view-background="" # The color of the text in the bar at the bottom in view mode. -# default= -status-bar-view-text= +# default="" +status-bar-view-text="" # The color of the text in the command bar at the bottom. -# default= -command-text= +# default="" +command-text="" # Background of selected list items. -# default= -list-selected-background= +# default="" +list-selected-background="" # The text color of selected list items. -# default= -list-selected-text= +# default="" +list-selected-text="" # The background color of selected list items that are out of focus. -# default= -list-selected-inactive-background= +# default="" +list-selected-inactive-background="" # The text color of selected list items that are out of focus. -# default= -list-selected-inactive-text= +# default="" +list-selected-inactive-text="" # The main color of the text for key hints -# default= -controls-text= +# default="" +controls-text="" # The highlight color of for key hints -# default= -controls-highlight= +# default="" +controls-highlight="" # The background color in dropdowns and autocompletions -# default= -autocomplete-background= +# default="" +autocomplete-background="" # The text color in dropdowns at autocompletions -# default= -autocomplete-text= +# default="" +autocomplete-text="" # The background color for selected value in dropdowns and autocompletions -# default= -autocomplete-selected-background= +# default="" +autocomplete-selected-background="" # The text color for selected value in dropdowns and autocompletions -# default= -autocomplete-selected-text= +# default="" +autocomplete-selected-text="" # The background color on selected button and the text color of unselected # buttons -# default= -button-color-one= +# default="" +button-color-one="" # The text color on selected button and the background color of unselected # buttons -# default= -button-color-two= +# default="" +button-color-two="" # The background on named timelines. -# default= -timeline-name-background= +# default="" +timeline-name-background="" # The text color on named timelines -# default= -timeline-name-text= +# default="" +timeline-name-text="" [input] -# You can edit the keys for tut below. -# -# The syntax is a bit weird, but it works. And I'll try to explain it as well as -# I can. +# In this section you set the keys to be used in tut. +# +# The hint option lets you set which part of the hint that will be highlighted +# in tut. E.g. [F]avorite results in a highlighted F and the rest of the text is +# displayed normaly. +# Some of the options can be in two states, like favorites, so there you can set +# the hint-alt option to something like Un[F]avorite. # -# Example: -# status-favorite="[F]avorite","Un[F]avorite",'f','F' -# status-delete="[D]elete",'d','D' -# -# status-favorite and status-delete differs because favorite can be in two -# states, so you will have to add two key hints. -# Most keys will only have on key hint. Look at the default value for reference. -# -# Key hints must be in some of the following formats. Remember the quotation -# marks. -# "" = empty +# Examples: # "[D]elete" = Delete with a highlighted D # "Un[F]ollow" = UnFollow with a highlighted F # "[Enter]" = Enter where everything is highlighted # "Yan[K]" = YanK with a highlighted K # -# After the hint (or hints) you must set the keys. You can do this in two ways, -# with single quotation marks or double ones. -# -# The single ones are for single chars like 'a', 'b', 'c' and double marks are -# for special keys like "Enter". Remember that they are case sensitive. +# The keys option lets you define what key that should be pressed. This is +# limited to on character only and they are case sensetive. +# Example: +# keys=["j","J"] # -# To find the names of special keys you have to go to the following site and -# look for "var KeyNames = map[Key]string{" +# You can also set special-keys and they're for keys like Escape and Enter. To +# find the names of special keys you have to go to the following site and look +# for "var KeyNames = map[Key]string{" # # https://github.com/gdamore/tcell/blob/master/key.go +[input.global-down] # Keys for moving down -# default="",'j','J',"Down" -global-down="",'j','J',"Down" -# Keys for moving up -# default="",'k','K',"Up" -global-up="",'k','K',"Up" +# default=["j", "J"] +keys=["j","J"] + +# default=["Down"] +special-keys=["Down"] + +[input.global-up] +# Keys for moving down +# default=["k", "K"] +keys=["k","K"] + +# default=["Up"] +special-keys=["Up"] + +[input.global-enter] # To select items -# default="","Enter" -global-enter="","Enter" +# default=["Enter"] +special-keys=["Enter"] + +[input.global-back] # To go back -# default="[Esc]","Esc" -global-back="[Esc]","Esc" -# To go back and exit Tut -# default="[Q]uit",'q','Q' -global-exit="[Q]uit",'q','Q' +# default="[Esc]" +hint="[Esc]" +# default=["Esc"] +special-keys=["Esc"] + +[input.global-exit] +# To go back or exit + +# default="[Q]uit" +hint="[Q]uit" + +# default=["q", "Q"] +keys=["q","Q"] + +[input.main-home] # Move to the top -# default="",'g',"Home" -main-home="",'g',"Home" +# default=["g"] +keys=["g"] + +# default=["Home"] +special-keys=["Home"] + +[input.main-end] # Move to the bottom -# default="",'G',"End" -main-end="",'G',"End" +# default=["G"] +keys=["G"] + +# default=["End"] +special-keys=["End"] + +[input.main-prev-feed] # Go to previous feed -# default="",'h','H',"Left" -main-prev-feed="",'h','H',"Left" +# default=["h", "H"] +keys=["h","H"] + +# default=["Left"] +special-keys=["Left"] + +[input.main-next-feed] # Go to next feed -# default="",'l','L',"Right" -main-next-feed="",'l','L',"Right" -# Focus on the previous feed window -# default="","Backtab" -main-prev-window="","Backtab" +# default=["l", "L"] +keys=["l","L"] + +# default=["Right"] +special-keys=["Right"] + +[input.main-prev-pane] +# Focus on the previous feed pane + +# default=["Backtab"] +special-keys=["Backtab"] + +[input.main-next-pane] +# Focus on the next feed pane + +# default=["Tab"] +special-keys=["Tab"] -# Focus on the next feed window -# default="","Tab" -main-next-window="","Tab" +[input.main-next-account] +# Focus on the next account -# Focus on the notification list -# default="[N]otifications",'n','N' -main-notification-focus="[N]otifications",'n','N' +# default=["Ctrl-N"] +special-keys=["Ctrl-N"] +[input.main-prev-account] +# Focus on the previous account + +# default=["Ctrl-P"] +special-keys=["Ctrl-P"] + +[input.main-compose] # Compose a new toot -# default="",'c','C' -main-compose="",'c','C' +# default=["c", "C"] +keys=["c","C"] + +[input.status-avatar] # Open avatar -# default="[A]vatar",'a','A' -status-avatar="[A]vatar",'a','A' +# default="[A]vatar" +hint="[A]vatar" + +# default=["a", "A"] +keys=["a","A"] + +[input.status-boost] # Boost a toot -# default="[B]oost","Un[B]oost",'b','B' -status-boost="[B]oost","Un[B]oost",'b','B' +# default="[B]oost" +hint="[B]oost" + +# default=["b", "B"] +keys=["b","B"] + +[input.status-edit] # Edit a toot -# default="[E]dit",'e','E' -status-edit="[E]dit",'e','E' +# default="[E]dit" +hint="[E]dit" + +# default=["e", "E"] +keys=["e","E"] + +[input.status-delete] # Delete a toot -# default="[D]elete",'d','D' -status-delete="[D]elete",'d','D' +# default="[D]elete" +hint="[D]elete" + +# default=["d", "D"] +keys=["d","D"] + +[input.status-favorite] # Favorite a toot -# default="[F]avorite","Un[F]avorite",'f','F' -status-favorite="[F]avorite","Un[F]avorite",'f','F' +# default="[F]avorite" +hint="[F]avorite" + +# default=["f", "F"] +keys=["f","F"] + +[input.status-media] # Open toots media files -# default="[M]edia",'m','M' -status-media="[M]edia",'m','M' +# default="[M]edia" +hint="[M]edia" + +# default=["m", "M"] +keys=["m","M"] + +[input.status-links] # Open links -# default="[O]pen",'o','O' -status-links="[O]pen",'o','O' +# default="[O]pen" +hint="[O]pen" + +# default=["o", "O"] +keys=["o","O"] + +[input.status-poll] # Open poll -# default="[P]oll",'p','P' -status-poll="[P]oll",'p','P' +# default="[P]oll" +hint="[P]oll" + +# default=["p", "P"] +keys=["p","P"] + +[input.status-reply] # Reply to toot -# default="[R]eply",'r','R' -status-reply="[R]eply",'r','R' +# default="[R]eply" +hint="[R]eply" + +# default=["r", "R"] +keys=["r","R"] + +[input.status-bookmark] # Save/bookmark a toot -# default="[S]ave","Un[S]ave",'s','S' -status-bookmark="[S]ave","Un[S]ave",'s','S' +# default="[S]ave" +hint="[S]ave" + +# default="Un[S]ave" +hint-alt="Un[S]ave" + +# default=["s", "S"] +keys=["s","S"] + +[input.status-thread] # View thread -# default="[T]hread",'t','T' -status-thread="[T]hread",'t','T' +# default="[T]hread" +hint="[T]hread" + +# default=["t", "T"] +keys=["t","T"] + +[input.status-user] # Open user profile -# default="[U]ser",'u','U' -status-user="[U]ser",'u','U' +# default="[U]ser" +hint="[U]ser" + +# default=["u", "U"] +keys=["u","U"] + +[input.status-view-focus] # Open the view mode -# default="[V]iew",'v','V' -status-view-focus="[V]iew",'v','V' +# default="[V]iew" +hint="[V]iew" + +# default=["v", "V"] +keys=["v","V"] + +[input.status-yank] # Yank the url of the toot -# default="[Y]ank",'y','Y' -status-yank="[Y]ank",'y','Y' +# default="[Y]ank" +hint="[Y]ank" + +# default=["y", "Y"] +keys=["y","Y"] + +[input.status-toggle-cw] # Show the content in a content warning -# default="Press [Z] to toggle cw",'z','Z' -status-toggle-cw="Press [Z] to toggle cw",'z','Z' +# default="Press [Z] to toggle cw" +hint="Press [Z] to toggle cw" + +# default=["z", "Z"] +keys=["z","Z"] + +[input.status-show-filtered] # Show the content of a filtered toot -# default="Press [Z] to view filtered toot",'z','Z' -status-show-filtered="Press [Z] to view filtered toot",'z','Z' +# default="Press [Z] to view filtered toot" +hint="Press [Z] to view filtered toot" + +# default=["z", "Z"] +keys=["z","Z"] + +[input.user-avatar] # View avatar -# default="[A]vatar",'a','A' -user-avatar="[A]vatar",'a','A' +# default="[A]vatar" +hint="[A]vatar" + +# default=["a", "A"] +keys=["a","A"] + +[input.user-block] # Block the user -# default="[B]lock","Un[B]lock",'b','B' -user-block="[B]lock","Un[B]lock",'b','B' +# default="[B]lock" +hint="[B]lock" + +# default="Un[B]lock" +hint-alt="Un[B]lock" + +# default=["b", "B"] +keys=["b","B"] + +[input.user-follow] # Follow user -# default="[F]ollow","Un[F]ollow",'f','F' -user-follow="[F]ollow","Un[F]ollow",'f','F' +# default="[F]ollow" +hint="[F]ollow" + +# default="Un[F]ollow" +hint-alt="Un[F]ollow" + +# default=["f", "F"] +keys=["f","F"] + +[input.user-follow-request-decide] # Follow user -# default="Follow [R]equest","Follow [R]equest",'r','R' -user-follow-request-decide="Follow [R]equest","Follow [R]equest",'r','R' +# default="Follow [R]equest" +hint="Follow [R]equest" + +# default="Follow [R]equest" +hint-alt="Follow [R]equest" + +# default=["r", "R"] +keys=["r","R"] + +[input.user-mute] # Mute user -# default="[M]ute","Un[M]ute",'m','M' -user-mute="[M]ute","Un[M]ute",'m','M' +# default="[M]ute" +hint="[M]ute" + +# default="Un[M]ute" +hint-alt="Un[M]ute" + +# default=["m", "M"] +keys=["m","M"] + +[input.user-links] # Open links -# default="[O]pen",'o','O' -user-links="[O]pen",'o','O' +# default="[O]pen" +hint="[O]pen" + +# default=["o", "O"] +keys=["o","O"] + +[input.user-user] # View user profile -# default="[U]ser",'u','U' -user-user="[U]ser",'u','U' +# default="[U]ser" +hint="[U]ser" + +# default=["u", "U"] +keys=["u","U"] + +[input.user-view-focus] # Open view mode -# default="[V]iew",'v','V' -user-view-focus="[V]iew",'v','V' +# default="[V]iew" +hint="[V]iew" + +# default=["v", "V"] +keys=["v","V"] + +[input.user-yank] # Yank the user URL -# default="[Y]ank",'y','Y' -user-yank="[Y]ank",'y','Y' +# default="[Y]ank" +hint="[Y]ank" + +# default=["y", "Y"] +keys=["y","Y"] + +[input.list-open-feed] # Open list -# default="[O]pen",'o','O' -list-open-feed="[O]pen",'o','O' +# default="[O]pen" +hint="[O]pen" + +# default=["o", "O"] +keys=["o","O"] + +[input.list-user-list] # List all users in a list -# default="[U]sers",'u','U' -list-user-list="[U]sers",'u','U' +# default="[U]sers" +hint="[U]sers" + +# default=["u", "U"] +keys=["u","U"] + +[input.list-user-add] # Add user to list -# default="[A]dd",'a','A' -list-user-add="[A]dd",'a','A' +# default="[A]dd" +hint="[A]dd" + +# default=["a", "A"] +keys=["a","A"] + +[input.list-user-delete] # Delete user from list -# default="[D]elete",'d','D' -list-user-delete="[D]elete",'d','D' +# default="[D]elete" +hint="[D]elete" + +# default=["d", "D"] +keys=["d","D"] + +[input.link-open] # Open URL -# default="[O]pen",'o','O' -link-open="[O]pen",'o','O' +# default="[O]pen" +hint="[O]pen" + +# default=["o", "O"] +keys=["o","O"] + +[input.link-yank] # Yank the URL -# default="[Y]ank",'y','Y' -link-yank="[Y]ank",'y','Y' +# default="[Y]ank" +hint="[Y]ank" + +# default=["y", "Y"] +keys=["y","Y"] + +[input.tag-open-feed] # Open tag feed -# default="[O]pen",'o','O' -tag-open-feed="[O]pen",'o','O' +# default="[O]pen" +hint="[O]pen" + +# default=["o", "O"] +keys=["o","O"] + +[input.tag-follow] # Toggle follow on tag -# default="[F]ollow","Un[F]ollow",'f','F' -tag-follow="[F]ollow","Un[F]ollow",'f','F' +# default="[F]ollow" +hint="[F]ollow" + +# default="Un[F]ollow" +hint-alt="Un[F]ollow" + +# default=["f", "F"] +keys=["f","F"] + +[input.compose-edit-cw] # Edit content warning text on new toot -# default="[C]W text",'c','C' -compose-edit-cw="[C]W text",'c','C' +# default="[C]W text" +hint="[C]W text" + +# default=["c", "C"] +keys=["c","C"] + +[input.compose-edit-text] # Edit the text on new toot -# default="[E]dit text",'e','E' -compose-edit-text="[E]dit text",'e','E' +# default="[E]dit text" +hint="[E]dit text" + +# default=["e", "E"] +keys=["e","E"] + +[input.compose-include-quote] # Include a quote when replying -# default="[I]nclude quote",'i','I' -compose-include-quote="[I]nclude quote",'i','I' +# default="[I]nclude quote" +hint="[I]nclude quote" + +# default=["i", "I"] +keys=["i","I"] + +[input.compose-media-focus] # Focus on adding media to toot -# default="[M]edia",'m','M' -compose-media-focus="[M]edia",'m','M' +# default="[M]edia" +hint="[M]edia" + +# default=["m", "M"] +keys=["m","M"] + +[input.compose-post] # Post the new toot -# default="[P]ost",'p','P' -compose-post="[P]ost",'p','P' +# default="[P]ost" +hint="[P]ost" + +# default=["p", "P"] +keys=["p","P"] + +[input.compose-toggle-content-warning] # Toggle content warning on toot -# default="[T]oggle CW",'t','T' -compose-toggle-content-warning="[T]oggle CW",'t','T' +# default="[T]oggle CW" +hint="[T]oggle CW" + +# default=["t", "T"] +keys=["t","T"] + +[input.compose-visibility] # Edit the visibility on new toot -# default="[V]isibility",'v','V' -compose-visibility="[V]isibility",'v','V' +# default="[V]isibility" +hint="[V]isibility" + +# default=["v", "V"] +keys=["v","V"] + +[input.compose-language] # Edit the language of a toot -# default="[L]ang",'l','L' -compose-language="[L]ang",'l','L' +# default="[L]ang" +hint="[L]ang" + +# default=["l", "L"] +keys=["l","L"] + +[input.compose-poll] # Switch to creating a poll -# default="P[O]ll",'o','O' -compose-poll="P[O]ll",'o','O' +# default="P[O]ll" +hint="P[O]ll" + +# default=["o", "O"] +keys=["o","O"] + +[input.media-delete] # Delete media file -# default="[D]elete",'d','D' -media-delete="[D]elete",'d','D' +# default="[D]elete" +hint="[D]elete" + +# default=["d", "D"] +keys=["d","D"] + +[input.media-edit-desc] # Edit the description on media file -# default="[E]dit desc",'e','E' -media-edit-desc="[E]dit desc",'e','E' +# default="[E]dit desc" +hint="[E]dit desc" + +# default=["e", "E"] +keys=["e","E"] + +[input.media-add] # Add a new media file -# default="[A]dd",'a','A' -media-add="[A]dd",'a','A' +# default="[A]dd" +hint="[A]dd" + +# default=["a", "A"] +keys=["a","A"] + +[input.vote-vote] # Vote on poll -# default="[V]ote",'v','V' -vote-vote="[V]ote",'v','V' +# default="[V]ote" +hint="[V]ote" + +# default=["v", "V"] +keys=["v","V"] + +[input.vote-select] # Select item to vote on -# default="[Enter] to select",' ', "Enter" -vote-select="[Enter] to select",' ', "Enter" +# default="[Enter] to select" +hint="[Enter] to select" + +# default=["Enter"] +special-keys=["Enter"] + +[input.poll-add] # Add a new poll option -# default="[A]dd",'a','A' -poll-add="[A]dd",'a','A' +# default="[A]dd" +hint="[A]dd" + +# default=["a", "A"] +keys=["a","A"] + +[input.poll-edit] # Edit a poll option -# default="[E]dit",'e','E' -poll-edit="[E]dit",'e','E' +# default="[E]dit" +hint="[E]dit" + +# default=["e", "E"] +keys=["e","E"] + +[input.poll-delete] # Delete a poll option -# default="[D]elete",'d','D' -poll-delete="[D]elete",'d','D' +# default="[D]elete" +hint="[D]elete" + +# default=["d", "D"] +keys=["d","D"] + +[input.poll-multi-toggle] # Toggle voting on multiple options -# default="Toggle [M]ultiple",'m','M' -poll-multi-toggle="Toggle [M]ultiple",'m','M' +# default="Toggle [M]ultiple" +hint="Toggle [M]ultiple" + +# default=["m", "M"] +keys=["m","M"] + +[input.poll-expiration] # Change the expiration of poll -# default="E[X]pires",'x','X' -poll-expiration="E[X]pires",'x','X' +# default="E[X]pires" +hint="E[X]pires" + +# default=["x", "X"] +keys=["x","X"] + +[input.preference-name] # Change display name -# default="[N]ame",'n','N' -preference-name="[N]ame",'n','N' +# default="[N]ame" +hint="[N]ame" + +# default=["n", "N"] +keys=["n","N"] + +[input.preference-visibility] # Change default visibility of toots -# default="[V]isibility",'v','V' -preference-visibility="[V]isibility",'v','V' +# default="[V]isibility" +hint="[V]isibility" + +# default=["v", "V"] +keys=["v","V"] + +[input.preference-bio] # Change bio in profile -# default="[B]io",'b','B' -preference-bio="[B]io",'b','B' +# default="[B]io" +hint="[B]io" + +# default=["b", "B"] +keys=["b","B"] + +[input.preference-save] # Save your preferences -# default="[S]ave",'s','S' -preference-save="[S]ave",'s','S' +# default="[S]ave" +hint="[S]ave" + +# default=["s", "S"] +keys=["s","S"] + +[input.preference-fields] # Edit profile fields -# default="[F]ields",'f','F' -preference-fields="[F]ields",'f','F' +# default="[F]ields" +hint="[F]ields" + +# default=["f", "F"] +keys=["f","F"] + +[input.preference-fields-add] # Add new field -# default="[A]dd",'a','A' -preference-fields-add="[A]dd",'a','A' +# default="[A]dd" +hint="[A]dd" + +# default=["a", "A"] +keys=["a","A"] + +[input.preference-fields-edit] # Edit current field -# default="[E]dit",'e','E' -preference-fields-edit="[E]dit",'e','E' +# default="[E]dit" +hint="[E]dit" + +# default=["e", "E"] +keys=["e","E"] + +[input.preference-fields-delete] # Delete current field -# default="[D]elete",'d','D' -preference-fields-delete="[D]elete",'d','D' + +# default="[D]elete" +hint="[D]elete" + +# default=["d", "D"] +keys=["d","D"] + +[input.editor-exit] +# Exit the editor + +# default="[Esc] when done" +hint="[Esc] when done" + +# default=["Esc"] +special-keys=["Esc"] ` diff --git a/config/help.tmpl b/config/help.tmpl index 1d31c46..2bba250 100644 --- a/config/help.tmpl +++ b/config/help.tmpl @@ -50,8 +50,11 @@ Here's a list of supported commands. {{ Color .Style.TextSpecial2 }}{{ Flags "b" }}:clear-notifications{{ Flags "-" }}{{ Color .Style.Text }} Remove all of your notifications -{{ Color .Style.TextSpecial2 }}{{ Flags "b" }}:close-window{{ Flags "-" }}{{ Color .Style.Text }} - Closes the current window, including all the timelines in said window +{{ Color .Style.TextSpecial2 }}{{ Flags "b" }}:clear-temp{{ Flags "-" }}{{ Color .Style.Text }} + Remove all of your media files that have been downloaded. Only needed if you have set delete-temp-files to false under [media[] in your config. + +{{ Color .Style.TextSpecial2 }}{{ Flags "b" }}:close-pane{{ Flags "-" }}{{ Color .Style.Text }} + Closes the current pane, including all the timelines in said pane {{ Color .Style.TextSpecial2 }}{{ Flags "b" }}:compose{{ Flags "-" }}{{ Color .Style.Text }} Compose a new toot @@ -90,12 +93,15 @@ Here's a list of supported commands. Place the list in choosen placement {{ Color .Style.TextSpecial2 }}{{ Flags "b" }}:list-split{{ Flags "-" }}{{ Color .Style.Text }} row|column - Split the timelines in window by row or column + Split the timelines by row or column + +{{ Color .Style.TextSpecial2 }}{{ Flags "b" }}:login{{ Flags "-" }}{{ Color .Style.Text }} + Login to one more account -{{ Color .Style.TextSpecial2 }}{{ Flags "b" }}:move-window{{ Flags "-" }}{{ Color .Style.Text }} left|right|up|down|home|end - Moves the window in choosen direction +{{ Color .Style.TextSpecial2 }}{{ Flags "b" }}:move-pane{{ Flags "-" }}{{ Color .Style.Text }} left|right|up|down|home|end + Moves the pane in choosen direction -{{ Color .Style.TextSpecial2 }}{{ Flags "b" }}:mv{{ Flags "-" }}{{ Color .Style.Text }} l|r|u|d|h|e +{{ Color .Style.TextSpecial2 }}{{ Flags "b" }}:mp{{ Flags "-" }}{{ Color .Style.Text }} l|r|u|d|h|e Shorter form of former command {{ Color .Style.TextSpecial2 }}{{ Flags "b" }}:muting{{ Flags "-" }}{{ Color .Style.Text }} @@ -104,14 +110,20 @@ Here's a list of supported commands. {{ Color .Style.TextSpecial2 }}{{ Flags "b" }}:newer{{ Flags "-" }}{{ Color .Style.Text }} Force load newer toots in current timeline +{{ Color .Style.TextSpecial2 }}{{ Flags "b" }}:next-acct{{ Flags "-" }}{{ Color .Style.Text }} + Go to the next account if you're logged in to multiple + {{ Color .Style.TextSpecial2 }}{{ Flags "b" }}:preferences{{ Flags "-" }}{{ Color .Style.Text }} Update your profile and some other settings +{{ Color .Style.TextSpecial2 }}{{ Flags "b" }}:prev-acct{{ Flags "-" }}{{ Color .Style.Text }} + Go to the prev account if you're logged in to multiple + {{ Color .Style.TextSpecial2 }}{{ Flags "b" }}:profile{{ Flags "-" }}{{ Color .Style.Text }} Go to your profile {{ Color .Style.TextSpecial2 }}{{ Flags "b" }}:proportions{{ Flags "-" }}{{ Color .Style.Text }} [int] [int] - Sets the proportions of the windows and the content. The first integer is your windows and the other for content, e.g. :proportions 1 3 + Sets the proportions of the panes and the content. The first integer is your panes and the other for content, e.g. :proportions 1 3 {{ Color .Style.TextSpecial2 }}{{ Flags "b" }}:refetch{{ Flags "-" }}{{ Color .Style.Text }} Refetches the current item that you're viewing. Can be used to update poll results. @@ -134,15 +146,15 @@ Here's a list of supported commands. {{ Color .Style.TextSpecial2 }}{{ Flags "b" }}:user{{ Flags "-" }}{{ Color .Style.Text }} Search for users named , e.g. :user rasmus. To narrow a search include the instance like this :user rasmus@mastodon.acc.sunet.se -{{ Color .Style.TextSpecial2 }}{{ Flags "b" }}:window{{ Flags "-" }}{{ Color .Style.Text }} - Switch window by index (zero indexed) e.g. :window 0 for the left/top window +{{ Color .Style.TextSpecial2 }}{{ Flags "b" }}:pane{{ Flags "-" }}{{ Color .Style.Text }} + Switch pane by index (zero indexed) e.g. :pane 0 for the left/top pane {{ Color .Style.Text }}{{ Flags "b" }}Configuration{{ Flags "-" }} tut searches for a config file in the following locations on Linux: - 1. $XDG_CONFIG_HOME/tut/config.ini - 2. $HOME/.config/tut/config.ini + 1. $XDG_CONFIG_HOME/tut/config.toml + 2. $HOME/.config/tut/config.toml If you don't run Linux it will use the path of the Go function {{ Flags "b" }}os.UserConfigDir(){{ Flags "-" }}. But if you move the tut folder to {{ Flags "b" }}XDG_CONFIG_HOME/tut/{{ Flags "-" }} and have set the environment variable {{ Flags "b" }}XDG_CONFIG_HOME{{ Flags "-" }} diff --git a/config/keys.go b/config/keys.go index 860e6d2..4095b72 100644 --- a/config/keys.go +++ b/config/keys.go @@ -32,10 +32,6 @@ func ColorKey(c *Config, pre, key, end string) string { return text } -func ColorMark(color tcell.Color) string { - return fmt.Sprintf("[#%06x]", color.Hex()) -} - func TextFlags(s string) string { return fmt.Sprintf("[::%s]", s) } @@ -44,3 +40,7 @@ func SublteText(c *Config, text string) string { subtle := ColorMark(c.Style.Subtle) return fmt.Sprintf("%s%s", subtle, text) } + +func ColorMark(color tcell.Color) string { + return fmt.Sprintf("[#%06x]", color.Hex()) +} diff --git a/config/load.go b/config/load.go index 3bb1882..81213a2 100644 --- a/config/load.go +++ b/config/load.go @@ -11,9 +11,9 @@ func Load(cnfPath string, cnfDir string) *Config { fmt.Printf("Couldn't create or access the configuration dir. Error: %v\n", err) os.Exit(1) } - path, exists, err := checkConfig("config.ini", cnfPath, cnfDir) + path, exists, err := checkConfig("config.toml", cnfPath, cnfDir) if err != nil { - fmt.Printf("Couldn't access config.ini. Error: %v\n", err) + fmt.Printf("Couldn't access config.toml. Error: %v\n", err) os.Exit(1) } if !exists { diff --git a/config/themes/default.ini b/config/themes/default.ini deleted file mode 100644 index 668f6f5..0000000 --- a/config/themes/default.ini +++ /dev/null @@ -1,28 +0,0 @@ -background=#272822 -text=#f8f8f2 -subtle=#808080 -warning-text=#f92672 -text-special-one=#ae81ff -text-special-two=#a6e22e -top-bar-background=#f92672 -top-bar-text=#f8f8f2 -status-bar-background=#f92672 -status-bar-text=#f8f8f2 -status-bar-view-background=#ae81ff -status-bar-view-text=#f8f8f2 -command-text=#f8f8f2 -list-selected-background=#f92672 -list-selected-text=#f8f8f2 -list-selected-inactive-background=#ae81ff -list-selected-inactive-text=#f8f8f2 -controls-text=#f8f8f2 -controls-highlight=#a6e22e -autocomplete-background=#272822 -autocomplete-text=#f8f8f2 -autocomplete-selected-background=#ae81ff -autocomplete-selected-text=#f8f8f2 -button-color-one=#f92672 -button-color-two=#272822 -timeline-name-background=#272822 -timeline-name-text=#808080 - diff --git a/config/themes/default.toml b/config/themes/default.toml new file mode 100644 index 0000000..9fa332f --- /dev/null +++ b/config/themes/default.toml @@ -0,0 +1,28 @@ +background="#272822" +text="#f8f8f2" +subtle="#808080" +warning-text="#f92672" +text-special-one="#ae81ff" +text-special-two="#a6e22e" +top-bar-background="#f92672" +top-bar-text="#f8f8f2" +status-bar-background="#f92672" +status-bar-text="#f8f8f2" +status-bar-view-background="#ae81ff" +status-bar-view-text="#f8f8f2" +command-text="#f8f8f2" +list-selected-background="#f92672" +list-selected-text="#f8f8f2" +list-selected-inactive-background="#ae81ff" +list-selected-inactive-text="#f8f8f2" +controls-text="#f8f8f2" +controls-highlight="#a6e22e" +autocomplete-background="#272822" +autocomplete-text="#f8f8f2" +autocomplete-selected-background="#ae81ff" +autocomplete-selected-text="#f8f8f2" +button-color-one="#f92672" +button-color-two="#272822" +timeline-name-background="#272822" +timeline-name-text="#808080" + diff --git a/config/themes/gruvbox-light.toml b/config/themes/gruvbox-light.toml new file mode 100644 index 0000000..884dc08 --- /dev/null +++ b/config/themes/gruvbox-light.toml @@ -0,0 +1,27 @@ +background="#fbf1c7" +text="#654735" +subtle="#b47109" +warning-text="#c14a4a" +text-special-one="#45707a" +text-special-two="#945e80" +top-bar-background="#654735" +top-bar-text="#fbf1c7" +status-bar-background="#654735" +status-bar-text="#fbf1c7" +status-bar-view-background="#4c7a5d" +status-bar-view-text="#fbf1c7" +list-selected-background="#654735" +list-selected-text="#fbf1c7" +command-text="#654735" +list-selected-inactive-background="#945e80" +list-selected-inactive-text="#fbf1c7" +controls-text="#654735" +controls-highlight="#945e80" +autocomplete-background="#fbf1c7" +autocomplete-text="#654735" +autocomplete-selected-background="#4c7a5d" +autocomplete-selected-text="#fbf1c7" +button-color-one="#654735" +button-color-two="#fbf1c7" +timeline-name-background="#fbf1c7" +timeline-name-text="#654735" diff --git a/config/themes/nord.ini b/config/themes/nord.ini deleted file mode 100644 index 60d73d2..0000000 --- a/config/themes/nord.ini +++ /dev/null @@ -1,14 +0,0 @@ -background=#2E3440 -text=#ECEFF4 -subtle=#4C566A -warning-text=#BF616A -text-special-one=#8FBCBB -text-special-two=#81A1C1 -top-bar-background=#5E81AC -top-bar-text=#ECEFF4 -status-bar-background=#5E81AC -status-bar-text=#ECEFF4 -status-bar-view-background=#8FBCBB -status-bar-view-text=#f8f8f2 -list-selected-background=#5E81AC -list-selected-text=#ECEFF4 diff --git a/config/themes/nord.toml b/config/themes/nord.toml new file mode 100644 index 0000000..8bc87cf --- /dev/null +++ b/config/themes/nord.toml @@ -0,0 +1,14 @@ +background="#2E3440" +text="#ECEFF4" +subtle="#4C566A" +warning-text="#BF616A" +text-special-one="#8FBCBB" +text-special-two="#81A1C1" +top-bar-background="#5E81AC" +top-bar-text="#ECEFF4" +status-bar-background="#5E81AC" +status-bar-text="#ECEFF4" +status-bar-view-background="#8FBCBB" +status-bar-view-text="#f8f8f2" +list-selected-background="#5E81AC" +list-selected-text="#ECEFF4" diff --git a/config/themes/papercolor-light.ini b/config/themes/papercolor-light.ini deleted file mode 100644 index 93a8fc1..0000000 --- a/config/themes/papercolor-light.ini +++ /dev/null @@ -1,22 +0,0 @@ -; This is an adaption of NLKNguyen (https://github.com/NLKNguyen)'s -; PaperColor Light theme for tut (https://github.com/RasmusLindroth/tut). -; -; @author: stoerdebegga -; @source: https://codeberg.org/stoerdebegga/papercolor-light-contrib -; @source99: https://github.com/stoerdebegga/papercolor-light-contrib -; -background=#EEEEEE -text=#4D4D4C -subtle=#4271AE -warning-text=#D7005F -text-special-one=#969694 -text-special-two=#D7005F -top-bar-background=#4271AE -top-bar-text=#EEEEEE -status-bar-background=#4271AE -status-bar-text=#EEEEEE -status-bar-view-background=#718C00 -status-bar-view-text=#f5f5f5 -command-text=#4D4D4C -list-selected-background=#D7005F -list-selected-text=#f5f5f5 diff --git a/config/themes/papercolor-light.toml b/config/themes/papercolor-light.toml new file mode 100644 index 0000000..5acefd2 --- /dev/null +++ b/config/themes/papercolor-light.toml @@ -0,0 +1,22 @@ +# This is an adaption of NLKNguyen (https://github.com/NLKNguyen)'s +# PaperColor Light theme for tut (https://github.com/RasmusLindroth/tut). +# +# @author: stoerdebegga +# @source: https://codeberg.org/stoerdebegga/papercolor-light-contrib +# @source99: https://github.com/stoerdebegga/papercolor-light-contrib +# +background="#EEEEEE" +text="#4D4D4C" +subtle="#4271AE" +warning-text="#D7005F" +text-special-one="#969694" +text-special-two="#D7005F" +top-bar-background="#4271AE" +top-bar-text="#EEEEEE" +status-bar-background="#4271AE" +status-bar-text="#EEEEEE" +status-bar-view-background="#718C00" +status-bar-view-text="#f5f5f5" +command-text="#4D4D4C" +list-selected-background="#D7005F" +list-selected-text="#f5f5f5" diff --git a/config/themes/snow-white.toml b/config/themes/snow-white.toml new file mode 100644 index 0000000..4f23fcf --- /dev/null +++ b/config/themes/snow-white.toml @@ -0,0 +1,27 @@ +background="#ffffff" +text="#000000" +subtle="#000000" +warning-text="#d7005f" +text-special-one="#000000" +text-special-two="#d7005f" +top-bar-background="#4271ae" +top-bar-text="#ffffff" +status-bar-background="#f0f0f0" +status-bar-text="#000000" +status-bar-view-background="#505050" +status-bar-view-text="#ffffff" +list-selected-background="#4271ae" +list-selected-text="#ffffff" +command-text="#000000" +list-selected-inactive-background="#6d6d6d" +list-selected-inactive-text="#ffffff" +controls-text="#000000" +controls-highlight="#f92672" +autocomplete-background="#fafafa" +autocomplete-text="#000000" +autocomplete-selected-background="#4271ae" +autocomplete-selected-text="#ffffff" +button-color-one="#4271ae" +button-color-two="#fafafa" +timeline-name-background="#f0f0f0" +timeline-name-text="#000000" diff --git a/config/toml.go b/config/toml.go new file mode 100644 index 0000000..6270978 --- /dev/null +++ b/config/toml.go @@ -0,0 +1,258 @@ +package config + +type ConfigTOML struct { + General GeneralTOML `toml:"general"` + Style StyleTOML `toml:"style"` + Media MediaTOML `toml:"media"` + OpenPattern OpenPatternTOML `toml:"open-pattern"` + OpenCustom OpenCustomTOML `toml:"open-custom"` + NotificationConfig NotificationsTOML `toml:"desktop-notification"` + Input InputTOML `toml:"input"` +} + +type GeneralTOML struct { + Editor *string `toml:"editor"` + Confirmation *bool `toml:"confirmation"` + MouseSupport *bool `toml:"mouse-support"` + DateFormat *string `toml:"date-format"` + DateTodayFormat *string `toml:"date-today-format"` + DateRelative *int `toml:"date-relative"` + MaxWidth *int `toml:"max-width"` + QuoteReply *bool `toml:"quote-reply"` + ShortHints *bool `toml:"short-hints"` + ShowFilterPhrase *bool `toml:"show-filter-phrase"` + ListPlacement *string `toml:"list-placement"` + ListSplit *string `toml:"list-split"` + ListProportion *int `toml:"list-proportion"` + ContentProportion *int `toml:"content-proportion"` + TerminalTitle *int `toml:"terminal-title"` + ShowIcons *bool `toml:"show-icons"` + ShowHelp *bool `toml:"show-help"` + RedrawUI *bool `toml:"redraw-ui"` + LeaderKey *string `toml:"leader-key"` + LeaderTimeout *int64 `toml:"leader-timeout"` + Timelines *[]TimelineTOML `toml:"timelines"` + LeaderActions *[]LeaderActionTOML `toml:"leader-actions"` + StickToTop *bool `toml:"stick-to-top"` + NotificationsToHide *[]string `toml:"notifications-to-hide"` + ShowBoostedUser *bool `toml:"show-boosted-user"` + DynamicTimelineName *bool `toml:"dynamic-timeline-name"` + CommandsInNewPane *bool `toml:"commands-in-new-pane"` +} + +type TimelineTOML struct { + Name *string `toml:"name"` + Type *string `toml:"type"` + Data *string `toml:"data"` + Keys *[]string `toml:"keys"` + SpecialKeys *[]string `toml:"special-keys"` + Shortcut *string `toml:"shortcut"` + HideBoosts *bool `toml:"hide-boosts"` + HideReplies *bool `toml:"hide-replies"` + + Closed *bool `toml:"closed"` + OnCreationClosed *string `toml:"on-creation-closed"` + OnFocus *string `toml:"on-focus"` +} + +type LeaderActionTOML struct { + Type *string `toml:"type"` + Data *string `toml:"data"` + Shortcut *string `toml:"shortcut"` +} + +type StyleTOML struct { + Theme *string `toml:"theme"` + + XrdbPrefix *string `toml:"xrdb-prefix"` + + Background *string `toml:"background"` + Text *string `toml:"text"` + + Subtle *string `toml:"subtle"` + WarningText *string `toml:"warning-text"` + + TextSpecial1 *string `toml:"text-special-one"` + TextSpecial2 *string `toml:"text-special-two"` + + TopBarBackground *string `toml:"top-bar-background"` + TopBarText *string `toml:"top-bar-text"` + + StatusBarBackground *string `toml:"status-bar-background"` + StatusBarText *string `toml:"status-bar-text"` + + StatusBarViewBackground *string `toml:"status-bar-view-background"` + StatusBarViewText *string `toml:"status-bar-view-text"` + + ListSelectedBackground *string `toml:"list-selected-background"` + ListSelectedText *string `toml:"list-selected-text"` + + ListSelectedInactiveBackground *string `toml:"list-selected-inactive-background"` + ListSelectedInactiveText *string `toml:"list-selected-inactive-text"` + + ControlsText *string `toml:"controls-text"` + ControlsHighlight *string `toml:"controls-highlight"` + + AutocompleteBackground *string `toml:"autocomplete-background"` + AutocompleteText *string `toml:"autocomplete-text"` + + AutocompleteSelectedBackground *string `toml:"autocomplete-selected-background"` + AutocompleteSelectedText *string `toml:"autocomplete-selected-text"` + + ButtonColorOne *string `toml:"button-color-one"` + ButtonColorTwo *string `toml:"button-color-two"` + + TimelineNameBackground *string `toml:"timeline-name-background"` + TimelineNameText *string `toml:"timeline-name-text"` + + IconColor *string `toml:"icon-color"` + + CommandText *string `toml:"command-text"` +} + +type ViewerTOML struct { + Program *string `toml:"program"` + Args *string `toml:"args"` + Terminal *bool `toml:"terminal"` + Single *bool `toml:"single"` + Reverse *bool `toml:"reverse"` +} + +type MediaTOML struct { + DeleteTmpFiles *bool `toml:"delete-temp-files"` + Image *ViewerTOML `toml:"image"` + Video *ViewerTOML `toml:"video"` + Audio *ViewerTOML `toml:"audio"` + Link *ViewerTOML `toml:"link"` +} + +type PatternTOML struct { + Matching *string `toml:"matching"` + Program *string `toml:"program"` + Args *string `toml:"args"` + Terminal *bool `toml:"terminal"` +} + +type OpenPatternTOML struct { + Patterns *[]PatternTOML `toml:"patterns"` +} + +type CustomTOML struct { + Program *string `toml:"program"` + Args *string `toml:"args"` + Terminal *bool `toml:"terminal"` + Hint *string `toml:"hint"` + Keys *[]string `toml:"keys"` + SpecialKeys *[]string `toml:"special-keys"` +} + +type OpenCustomTOML struct { + Programs *[]CustomTOML `toml:"programs"` +} + +type NotificationsTOML struct { + Followers *bool `toml:"followers"` + Favorite *bool `toml:"favorite"` + Mention *bool `toml:"mention"` + Update *bool `toml:"update"` + Boost *bool `toml:"boost"` + Poll *bool `toml:"poll"` + Posts *bool `toml:"posts"` +} + +type KeyHintTOML struct { + Hint *string `toml:"hint"` + HintAlt *string `toml:"hint-alt"` + Keys *[]string `toml:"keys"` + SpecialKeys *[]string `toml:"special-keys"` +} + +type InputTOML struct { + GlobalDown *KeyHintTOML `toml:"global-down"` + GlobalUp *KeyHintTOML `toml:"global-up"` + GlobalEnter *KeyHintTOML `toml:"global-enter"` + GlobalBack *KeyHintTOML `toml:"global-back"` + GlobalExit *KeyHintTOML `toml:"global-exit"` + + MainHome *KeyHintTOML `toml:"main-home"` + MainEnd *KeyHintTOML `toml:"main-end"` + MainPrevFeed *KeyHintTOML `toml:"main-prev-feed"` + MainNextFeed *KeyHintTOML `toml:"main-next-feed"` + MainPrevPane *KeyHintTOML `toml:"main-prev-pane"` + MainNextPane *KeyHintTOML `toml:"main-next-pane"` + MainCompose *KeyHintTOML `toml:"main-compose"` + MainNextAccount *KeyHintTOML `toml:"main-next-account"` + MainPrevAccount *KeyHintTOML `toml:"main-prev-account"` + + StatusAvatar *KeyHintTOML `toml:"status-avatar"` + StatusBoost *KeyHintTOML `toml:"status-boost"` + StatusDelete *KeyHintTOML `toml:"status-delete"` + StatusEdit *KeyHintTOML `toml:"status-edit"` + StatusFavorite *KeyHintTOML `toml:"status-favorite"` + StatusMedia *KeyHintTOML `toml:"status-media"` + StatusLinks *KeyHintTOML `toml:"status-links"` + StatusPoll *KeyHintTOML `toml:"status-poll"` + StatusReply *KeyHintTOML `toml:"status-reply"` + StatusBookmark *KeyHintTOML `toml:"status-bookmark"` + StatusThread *KeyHintTOML `toml:"status-thread"` + StatusUser *KeyHintTOML `toml:"status-user"` + StatusViewFocus *KeyHintTOML `toml:"status-view-focus"` + StatusYank *KeyHintTOML `toml:"status-yank"` + StatusToggleCW *KeyHintTOML `toml:"status-toggle-cw"` + StatusShowFiltered *KeyHintTOML `toml:"status-show-filtered"` + + UserAvatar *KeyHintTOML `toml:"user-avatar"` + UserBlock *KeyHintTOML `toml:"user-block"` + UserFollow *KeyHintTOML `toml:"user-follow"` + UserFollowRequestDecide *KeyHintTOML `toml:"user-follow-request-decide"` + UserMute *KeyHintTOML `toml:"user-mute"` + UserLinks *KeyHintTOML `toml:"user-links"` + UserUser *KeyHintTOML `toml:"user-user"` + UserViewFocus *KeyHintTOML `toml:"user-view-focus"` + UserYank *KeyHintTOML `toml:"user-yank"` + + ListOpenFeed *KeyHintTOML `toml:"list-open-feed"` + ListUserList *KeyHintTOML `toml:"list-user-list"` + ListUserAdd *KeyHintTOML `toml:"list-user-add"` + ListUserDelete *KeyHintTOML `toml:"list-user-delete"` + + TagOpenFeed *KeyHintTOML `toml:"tag-open-feed"` + TagFollow *KeyHintTOML `toml:"tag-follow"` + + LinkOpen *KeyHintTOML `toml:"link-open"` + LinkYank *KeyHintTOML `toml:"link-yank"` + + ComposeEditCW *KeyHintTOML `toml:"compose-edit-cw"` + ComposeEditText *KeyHintTOML `toml:"compose-edit-text"` + ComposeIncludeQuote *KeyHintTOML `toml:"compose-include-quote"` + ComposeMediaFocus *KeyHintTOML `toml:"compose-media-focus"` + ComposePost *KeyHintTOML `toml:"compose-post"` + ComposeToggleContentWarning *KeyHintTOML `toml:"compose-toggle-content-warning"` + ComposeVisibility *KeyHintTOML `toml:"compose-visibility"` + ComposeLanguage *KeyHintTOML `toml:"compose-language"` + ComposePoll *KeyHintTOML `toml:"compose-poll"` + + MediaDelete *KeyHintTOML `toml:"media-delete"` + MediaEditDesc *KeyHintTOML `toml:"media-edit-desc"` + MediaAdd *KeyHintTOML `toml:"media-add"` + + VoteVote *KeyHintTOML `toml:"vote-vote"` + VoteSelect *KeyHintTOML `toml:"vote-select"` + + PollAdd *KeyHintTOML `toml:"poll-add"` + PollEdit *KeyHintTOML `toml:"poll-edit"` + PollDelete *KeyHintTOML `toml:"poll-delete"` + PollMultiToggle *KeyHintTOML `toml:"poll-multi-toggle"` + PollExpiration *KeyHintTOML `toml:"poll-expiration"` + + PreferenceName *KeyHintTOML `toml:"preference-name"` + PreferenceVisibility *KeyHintTOML `toml:"preference-visibility"` + PreferenceBio *KeyHintTOML `toml:"preference-bio"` + PreferenceSave *KeyHintTOML `toml:"preference-save"` + PreferenceFields *KeyHintTOML `toml:"preference-fields"` + PreferenceFieldsAdd *KeyHintTOML `toml:"preference-fields-add"` + PreferenceFieldsEdit *KeyHintTOML `toml:"preference-fields-edit"` + PreferenceFieldsDelete *KeyHintTOML `toml:"preference-fields-delete"` + + EditorExit *KeyHintTOML `toml:"editor-exit"` +} diff --git a/config/toml_default.go b/config/toml_default.go new file mode 100644 index 0000000..bdaa5fc --- /dev/null +++ b/config/toml_default.go @@ -0,0 +1,446 @@ +package config + +var tvar = true +var fvar = false + +var bt = &tvar +var bf = &fvar + +func sp(s string) *string { + return &s +} +func ip(i int) *int { + return &i +} + +func ip64(i int64) *int64 { + return &i +} + +var ConfigDefault = ConfigTOML{ + General: GeneralTOML{ + Editor: sp("TUT_USE_INTERNAL"), + Confirmation: bt, + MouseSupport: bf, + DateFormat: sp("2006-01-02 15:04"), + DateTodayFormat: sp("15:04"), + DateRelative: ip(-1), + QuoteReply: bf, + MaxWidth: ip(0), + ShortHints: bf, + ShowFilterPhrase: bt, + ShowIcons: bt, + ShowHelp: bt, + RedrawUI: bt, + StickToTop: bf, + ShowBoostedUser: bf, + DynamicTimelineName: bt, + CommandsInNewPane: bt, + ListPlacement: sp("left"), + ListSplit: sp("row"), + ListProportion: ip(1), + ContentProportion: ip(2), + TerminalTitle: ip(0), + LeaderKey: sp(""), + LeaderTimeout: ip64(1000), + NotificationsToHide: &[]string{}, + Timelines: &[]TimelineTOML{ + { + Name: sp("Home"), + Type: sp("home"), + HideBoosts: bf, + HideReplies: bf, + }, + { + Name: sp("Notifications"), + Type: sp("notifications"), + Keys: &[]string{"n", "N"}, + }, + }, + }, + Style: StyleTOML{ + Theme: sp("none"), + XrdbPrefix: sp("guess"), + Background: sp("#272822"), + Text: sp("#f8f8f2"), + Subtle: sp("#808080"), + WarningText: sp("#f92672"), + TextSpecial1: sp("#ae81ff"), + TextSpecial2: sp("#a6e22e"), + TopBarBackground: sp("#f92672"), + TopBarText: sp("#f8f8f2"), + StatusBarBackground: sp("#f92672"), + StatusBarText: sp("#f8f8f2"), + StatusBarViewBackground: sp("#ae81ff"), + StatusBarViewText: sp("#f8f8f2"), + CommandText: sp("#f8f8f2"), + ListSelectedBackground: sp("#f92672"), + ListSelectedText: sp("#f8f8f2"), + ListSelectedInactiveBackground: sp("#ae81ff"), + ListSelectedInactiveText: sp("#f8f8f2"), + ControlsText: sp("#f8f8f2"), + ControlsHighlight: sp("#a6e22e"), + AutocompleteBackground: sp("#272822"), + AutocompleteText: sp("#f8f8f2"), + AutocompleteSelectedBackground: sp("#ae81ff"), + AutocompleteSelectedText: sp("#f8f8f2"), + ButtonColorOne: sp("#f92672"), + ButtonColorTwo: sp("#272822"), + TimelineNameBackground: sp("#272822"), + TimelineNameText: sp("#808080"), + }, + Media: MediaTOML{ + DeleteTmpFiles: bt, + Image: &ViewerTOML{ + Program: sp("TUT_OS_DEFAULT"), + Args: sp(""), + Terminal: bf, + Single: bt, + Reverse: bf, + }, + Video: &ViewerTOML{ + Program: sp("TUT_OS_DEFAULT"), + Args: sp(""), + Terminal: bf, + Single: bt, + Reverse: bf, + }, + Audio: &ViewerTOML{ + Program: sp("TUT_OS_DEFAULT"), + Args: sp(""), + Terminal: bf, + Single: bt, + Reverse: bf, + }, + Link: &ViewerTOML{ + Program: sp("TUT_OS_DEFAULT"), + Args: sp(""), + Terminal: bf, + Single: bt, + Reverse: bf, + }, + }, + NotificationConfig: NotificationsTOML{ + Followers: bf, + Favorite: bf, + Mention: bf, + Update: bf, + Boost: bf, + Poll: bf, + Posts: bf, + }, + Input: InputTOML{ + GlobalDown: &KeyHintTOML{ + Keys: &[]string{"j", "J"}, + SpecialKeys: &[]string{"Down"}, + }, + GlobalUp: &KeyHintTOML{ + Keys: &[]string{"k", "K"}, + SpecialKeys: &[]string{"Up"}, + }, + GlobalEnter: &KeyHintTOML{ + SpecialKeys: &[]string{"Enter"}, + }, + GlobalBack: &KeyHintTOML{ + Hint: sp("[Esc]"), + SpecialKeys: &[]string{"Esc"}, + }, + GlobalExit: &KeyHintTOML{ + Hint: sp("[Q]uit"), + Keys: &[]string{"q", "Q"}, + }, + MainHome: &KeyHintTOML{ + Hint: sp(""), + Keys: &[]string{"g"}, + SpecialKeys: &[]string{"Home"}, + }, + MainEnd: &KeyHintTOML{ + Hint: sp(""), + Keys: &[]string{"G"}, + SpecialKeys: &[]string{"End"}, + }, + MainPrevFeed: &KeyHintTOML{ + Hint: sp(""), + Keys: &[]string{"h", "H"}, + SpecialKeys: &[]string{"Left"}, + }, + MainNextFeed: &KeyHintTOML{ + Hint: sp(""), + Keys: &[]string{"l", "L"}, + SpecialKeys: &[]string{"Right"}, + }, + MainPrevPane: &KeyHintTOML{ + Hint: sp(""), + SpecialKeys: &[]string{"Backtab"}, + }, + MainNextPane: &KeyHintTOML{ + Hint: sp(""), + SpecialKeys: &[]string{"Tab"}, + }, + MainCompose: &KeyHintTOML{ + Hint: sp(""), + Keys: &[]string{"c", "C"}, + }, + MainNextAccount: &KeyHintTOML{ + Hint: sp(""), + SpecialKeys: &[]string{"Ctrl-N"}, + }, + MainPrevAccount: &KeyHintTOML{ + Hint: sp(""), + SpecialKeys: &[]string{"Ctrl-P"}, + }, + StatusAvatar: &KeyHintTOML{ + Hint: sp("[A]vatar"), + Keys: &[]string{"a", "A"}, + }, + StatusBoost: &KeyHintTOML{ + Hint: sp("[B]oost"), + HintAlt: sp("Un[B]oost"), + Keys: &[]string{"b", "B"}, + }, + StatusEdit: &KeyHintTOML{ + Hint: sp("[E]dit"), + Keys: &[]string{"e", "E"}, + }, + StatusDelete: &KeyHintTOML{ + Hint: sp("[D]elete"), + Keys: &[]string{"d", "D"}, + }, + StatusFavorite: &KeyHintTOML{ + Hint: sp("[F]avorite"), + HintAlt: sp("Un[F]avorite"), + Keys: &[]string{"f", "F"}, + }, + StatusMedia: &KeyHintTOML{ + Hint: sp("[M]edia"), + Keys: &[]string{"m", "M"}, + }, + StatusLinks: &KeyHintTOML{ + Hint: sp("[O]pen"), + Keys: &[]string{"o", "O"}, + }, + StatusPoll: &KeyHintTOML{ + Hint: sp("[P]oll"), + Keys: &[]string{"p", "P"}, + }, + StatusReply: &KeyHintTOML{ + Hint: sp("[R]eply"), + Keys: &[]string{"r", "R"}, + }, + StatusBookmark: &KeyHintTOML{ + Hint: sp("[S]ave"), + HintAlt: sp("Un[S]ave"), + Keys: &[]string{"s", "S"}, + }, + StatusThread: &KeyHintTOML{ + Hint: sp("[T]hread"), + Keys: &[]string{"t", "T"}, + }, + StatusUser: &KeyHintTOML{ + Hint: sp("[U]ser"), + Keys: &[]string{"u", "U"}, + }, + StatusViewFocus: &KeyHintTOML{ + Hint: sp("[V]iew"), + Keys: &[]string{"v", "V"}, + }, + StatusYank: &KeyHintTOML{ + Hint: sp("[Y]ank"), + Keys: &[]string{"y", "Y"}, + }, + StatusToggleCW: &KeyHintTOML{ + Hint: sp("Press [Z] to toggle cw"), + Keys: &[]string{"z", "Z"}, + }, + StatusShowFiltered: &KeyHintTOML{ + Hint: sp("Press [Z] to view filtered toot"), + Keys: &[]string{"z", "Z"}, + }, + UserAvatar: &KeyHintTOML{ + Hint: sp("[A]vatar"), + Keys: &[]string{"a", "A"}, + }, + UserBlock: &KeyHintTOML{ + Hint: sp("[B]lock"), + HintAlt: sp("Un[B]lock"), + Keys: &[]string{"b", "B"}, + }, + UserFollow: &KeyHintTOML{ + Hint: sp("[F]ollow"), + HintAlt: sp("Un[F]ollow"), + Keys: &[]string{"f", "F"}, + }, + UserFollowRequestDecide: &KeyHintTOML{ + Hint: sp("Follow [R]equest"), + HintAlt: sp("Follow [R]equest"), + Keys: &[]string{"r", "R"}, + }, + UserMute: &KeyHintTOML{ + Hint: sp("[M]ute"), + HintAlt: sp("Un[M]ute"), + Keys: &[]string{"m", "M"}, + }, + UserLinks: &KeyHintTOML{ + Hint: sp("[O]pen"), + Keys: &[]string{"o", "O"}, + }, + UserUser: &KeyHintTOML{ + Hint: sp("[U]ser"), + Keys: &[]string{"u", "U"}, + }, + UserViewFocus: &KeyHintTOML{ + Hint: sp("[V]iew"), + Keys: &[]string{"v", "V"}, + }, + UserYank: &KeyHintTOML{ + Hint: sp("[Y]ank"), + Keys: &[]string{"y", "Y"}, + }, + ListOpenFeed: &KeyHintTOML{ + Hint: sp("[O]pen"), + Keys: &[]string{"o", "O"}, + }, + ListUserList: &KeyHintTOML{ + Hint: sp("[U]sers"), + Keys: &[]string{"u", "U"}, + }, + ListUserAdd: &KeyHintTOML{ + Hint: sp("[A]dd"), + Keys: &[]string{"a", "A"}, + }, + ListUserDelete: &KeyHintTOML{ + Hint: sp("[D]elete"), + Keys: &[]string{"d", "D"}, + }, + LinkOpen: &KeyHintTOML{ + Hint: sp("[O]pen"), + Keys: &[]string{"o", "O"}, + }, + LinkYank: &KeyHintTOML{ + Hint: sp("[Y]ank"), + Keys: &[]string{"y", "Y"}, + }, + TagOpenFeed: &KeyHintTOML{ + Hint: sp("[O]pen"), + Keys: &[]string{"o", "O"}, + }, + TagFollow: &KeyHintTOML{ + Hint: sp("[F]ollow"), + HintAlt: sp("Un[F]ollow"), + Keys: &[]string{"f", "F"}, + }, + ComposeEditCW: &KeyHintTOML{ + Hint: sp("[C]W text"), + Keys: &[]string{"c", "C"}, + }, + ComposeEditText: &KeyHintTOML{ + Hint: sp("[E]dit text"), + Keys: &[]string{"e", "E"}, + }, + ComposeIncludeQuote: &KeyHintTOML{ + Hint: sp("[I]nclude quote"), + Keys: &[]string{"i", "I"}, + }, + ComposeMediaFocus: &KeyHintTOML{ + Hint: sp("[M]edia"), + Keys: &[]string{"m", "M"}, + }, + ComposePost: &KeyHintTOML{ + Hint: sp("[P]ost"), + Keys: &[]string{"p", "P"}, + }, + ComposeToggleContentWarning: &KeyHintTOML{ + Hint: sp("[T]oggle CW"), + Keys: &[]string{"t", "T"}, + }, + ComposeVisibility: &KeyHintTOML{ + Hint: sp("[V]isibility"), + Keys: &[]string{"v", "V"}, + }, + ComposeLanguage: &KeyHintTOML{ + Hint: sp("[L]ang"), + Keys: &[]string{"l", "L"}, + }, + ComposePoll: &KeyHintTOML{ + Hint: sp("P[O]ll"), + Keys: &[]string{"o", "O"}, + }, + MediaDelete: &KeyHintTOML{ + Hint: sp("[D]elete"), + Keys: &[]string{"d", "D"}, + }, + MediaEditDesc: &KeyHintTOML{ + Hint: sp("[E]dit desc"), + Keys: &[]string{"e", "E"}, + }, + MediaAdd: &KeyHintTOML{ + Hint: sp("[A]dd"), + Keys: &[]string{"a", "A"}, + }, + VoteVote: &KeyHintTOML{ + Hint: sp("[V]ote"), + Keys: &[]string{"v", "V"}, + }, + VoteSelect: &KeyHintTOML{ + Hint: sp("[Enter] to select"), + Keys: &[]string{" "}, + SpecialKeys: &[]string{"Enter"}, + }, + PollAdd: &KeyHintTOML{ + Hint: sp("[A]dd"), + Keys: &[]string{"a", "A"}, + }, + PollEdit: &KeyHintTOML{ + Hint: sp("[E]dit"), + Keys: &[]string{"e", "E"}, + }, + PollDelete: &KeyHintTOML{ + Hint: sp("[D]elete"), + Keys: &[]string{"d", "D"}, + }, + PollMultiToggle: &KeyHintTOML{ + Hint: sp("Toggle [M]ultiple"), + Keys: &[]string{"m", "M"}, + }, + PollExpiration: &KeyHintTOML{ + Hint: sp("E[X]pires"), + Keys: &[]string{"x", "X"}, + }, + PreferenceName: &KeyHintTOML{ + Hint: sp("[N]ame"), + Keys: &[]string{"n", "N"}, + }, + PreferenceVisibility: &KeyHintTOML{ + Hint: sp("[V]isibility"), + Keys: &[]string{"v", "V"}, + }, + PreferenceBio: &KeyHintTOML{ + Hint: sp("[B]io"), + Keys: &[]string{"b", "B"}, + }, + PreferenceSave: &KeyHintTOML{ + Hint: sp("[S]ave"), + Keys: &[]string{"s", "S"}, + }, + PreferenceFields: &KeyHintTOML{ + Hint: sp("[F]ields"), + Keys: &[]string{"f", "F"}, + }, + PreferenceFieldsAdd: &KeyHintTOML{ + Hint: sp("[A]dd"), + Keys: &[]string{"a", "A"}, + }, + PreferenceFieldsEdit: &KeyHintTOML{ + Hint: sp("[E]dit"), + Keys: &[]string{"e", "E"}, + }, + PreferenceFieldsDelete: &KeyHintTOML{ + Hint: sp("[D]elete"), + Keys: &[]string{"d", "D"}, + }, + EditorExit: &KeyHintTOML{ + Hint: sp("[Esc] when done"), + SpecialKeys: &[]string{"Esc"}, + }, + }, +} diff --git a/config/toot.tmpl b/config/toot.tmpl index dacb537..a719d5b 100644 --- a/config/toot.tmpl +++ b/config/toot.tmpl @@ -71,3 +71,4 @@ {{- Color .Style.TextSpecial1 }} {{ .Toot.Boosts }} {{- Color .Style.Subtle }} Favorites {{- Color .Style.TextSpecial1 }} {{ .Toot.Favorites }} +{{- Color .Style.TextSpecial2 }} {{ .Toot.Lang }} diff --git a/docs/man/tut.1 b/docs/man/tut.1 index a28fdd1..0cda17b 100644 --- a/docs/man/tut.1 +++ b/docs/man/tut.1 @@ -14,7 +14,7 @@ . ftr VB CB . ftr VBI CBI .\} -.TH "tut" "1" "2023-01-01" "tut 1.0.34" "" +.TH "tut" "1" "2023-01-23" "tut 2.0.0" "" .hy .SH NAME .PP @@ -39,13 +39,17 @@ Show the version number Add one more user to tut .TP \f[B]-c\f[R], \f[B]--config\f[R] -Load config.ini from \f[I]\f[R] +Load config.toml from \f[I]\f[R] .TP \f[B]-d\f[R], \f[B]--config-dir\f[R] Load all config from \f[I]\f[R] .TP \f[B]-u\f[R], \f[B]--user\f[R] Login directly to user named \f[I]\f[R]. +If you want to login to multiple accounts seperate them with a space and +use quotation marks. +E.g. +-u \[lq]acc_one acc_two\[rq]. If two users are named the same, use full name like \f[I]tut\[at]fosstodon.org\f[R] .SH COMMANDS @@ -55,15 +59,15 @@ Runs the TUI .TP \f[B]example-config\f[R] Generates the default configuration file in the current directory and -names it ./config.example.ini +names it ./config.example.toml .SH CONFIGURATION .PP Tut is configurable, so you can change things like the colors, the default timeline, what image viewer to use and some more. Check out tut(5) or the configuration file to see all the options. .PP -You find it in \f[I]$XDG_CONFIG_HOME/tut/config.ini\f[R] on Linux which -usually equals to \f[I]\[ti]/.config/tut/config.ini\f[R]. +You find it in \f[I]$XDG_CONFIG_HOME/tut/config.toml\f[R] on Linux which +usually equals to \f[I]\[ti]/.config/tut/config.toml\f[R]. If you don\[cq]t run Linux it will use the path of the Go funcdtion os.UserConfigDir(). But if you move the tut folder to \f[I]XDG_CONFIG_HOME/tut/\f[R] and diff --git a/docs/man/tut.1.md b/docs/man/tut.1.md index 57d823c..aad0f54 100644 --- a/docs/man/tut.1.md +++ b/docs/man/tut.1.md @@ -1,6 +1,6 @@ -% tut(1) tut 1.0.34 +% tut(1) tut 2.0.0 % Rasmus Lindroth -% 2023-01-01 +% 2023-01-23 # NAME tut - a Mastodon TUI @@ -24,13 +24,14 @@ To see keys and commands you can use inside of tut check tut(7). : Add one more user to tut **-c**, **\--config** \ -: Load config.ini from *\* +: Load config.toml from *\* **-d**, **\--config-dir** \ : Load all config from *\* **-u**, **\--user** \ : Login directly to user named *\*. +: If you want to login to multiple accounts seperate them with a space and use quotation marks. E.g. -u "acc_one acc_two". : If two users are named the same, use full name like *tut@fosstodon.org* # COMMANDS @@ -39,12 +40,12 @@ To see keys and commands you can use inside of tut check tut(7). : Runs the TUI **example-config** -: Generates the default configuration file in the current directory and names it ./config.example.ini +: Generates the default configuration file in the current directory and names it ./config.example.toml # CONFIGURATION Tut is configurable, so you can change things like the colors, the default timeline, what image viewer to use and some more. Check out tut(5) or the configuration file to see all the options. -You find it in *$XDG_CONFIG_HOME/tut/config.ini* on Linux which usually equals to *~/.config/tut/config.ini*. +You find it in *$XDG_CONFIG_HOME/tut/config.toml* on Linux which usually equals to *~/.config/tut/config.toml*. If you don't run Linux it will use the path of the Go funcdtion os.UserConfigDir(). But if you move the tut folder to *XDG_CONFIG_HOME/tut/* and have set the environment variable *XDG_CONFIG_HOME* it will look there instead of the standard place. diff --git a/docs/man/tut.5 b/docs/man/tut.5 index 9206a7f..5d6edcd 100644 --- a/docs/man/tut.5 +++ b/docs/man/tut.5 @@ -14,7 +14,7 @@ . ftr VB CB . ftr VBI CBI .\} -.TH "tut" "5" "2023-01-01" "tut 1.0.34" "" +.TH "tut" "5" "2023-01-23" "tut 2.0.0" "" .hy .SH NAME .PP @@ -23,8 +23,8 @@ tut - configuration for tut(1) .PP The configuration format for tut. .PP -You find it in \f[I]$XDG_CONFIG_HOME/tut/config.ini\f[R] on Linux which -usually equals to \f[I]\[ti]/.config/tut/config.ini\f[R]. +You find it in \f[I]$XDG_CONFIG_HOME/tut/config.toml\f[R] on Linux which +usually equals to \f[I]\[ti]/.config/tut/config.toml\f[R]. If you don\[cq]t run Linux it will use the path of the Go funcdtion os.UserConfigDir(). But if you move the tut folder to \f[I]XDG_CONFIG_HOME/tut/\f[R] and @@ -40,103 +40,47 @@ The last line under each options shows the default value. .SH GENERAL .PP This section is [general] in your configuration file -.SS confirmation -.PP -Shows a confirmation view before actions such as favorite, delete toot, -boost etc. -.PD 0 -.P -.PD -\f[B]confirmation\f[R]=\f[I]true\f[R] -.SS mouse-support -.PP -Enable support for using the mouse in tut to select items. -.PD 0 -.P -.PD -\f[B]mouse-support\f[R]=\f[I]false\f[R] -.SS timelines -.PP -Timelines adds windows of feeds. -You can customize the number of feeds, what they should show and the key -to activate them. -.PP -Available timelines: home, direct, local, federated, special, bookmarks, -saved, favorited, notifications, lists, mentions, tag -.PP -The one named special are the home timeline with only boosts and/or -replies. -.PP -Tag is special as you need to add the tag after, see the example below. -.PP -The syntax is: -.PD 0 -.P -.PD -timelines=feed,name,[keys\&...],[showBoosts],[showReplies] -.PP -Tha values in brackets are optional. -You can see the syntax for keys under the input section. -.PP -showBoosts and showReplies must be formated as bools. -So either true or false. -They always defaults to true. -.PP -Some examples: -.PP -home timeline with the name Home -.PD 0 -.P -.PD -timelines=home,Home -.PP -local timeline with the name Local and it gets focus when you press 2. -It will also hide boosts in the timeline, but show toots that are -replies. -.PD 0 -.P -.PD -timelines=local,Local,\[aq]2\[aq],false,true +.SS editor .PP -notification timeline with the name [N]otifications and it gets focus -when you press n or N +What editor to use. +TUT_USE_INTERNAL will use the editor that comes with tut. +If you want you can set this to $EDITOR to use your environment variable +or vim if you want to specify the program directly. .PD 0 .P .PD -timelines=notifications,[N]otifications,\[aq]n\[aq],\[aq]N\[aq] +\f[B]editor\f[R]=\f[I]\[lq]TUT_USE_INTERNAL\[rq]\f[R] +.SS confirmation .PP -tag timeline for #linux with the name Linux and it gets focus when you -press +You need to press yes in a confirmation dialog before favoriting, +boosting, etc. .PD 0 .P .PD -timelines=tag linux,Linux,\[dq]F2\[dq] +\f[B]confirmation\f[R]=\f[I]true\f[R] +.SS mouse-support .PP -If you don\[aq]t set any timelines it will default to this: +Enable mouse support in tut. .PD 0 .P .PD -timelines=home -.PD 0 -.P -.PD -timelines=notifications,[N]otifications,\[aq]n\[aq],\[aq]N\[aq] +\f[B]mouse-support\f[R]=\f[I]false\f[R] .SS date-format .PP The date format to be used. -See https://godoc.org/time#Time.Format +See https://pkg.go.dev/time#pkg-constants .PD 0 .P .PD -\f[B]date-format\f[R]=\f[I]2006-01-02 15:04\f[R] -.SS date-today-format +\f[B]date-format\f[R]=\f[I]\[lq]2006-01-02 15:04\[rq]\f[R] +.SS date-tody-format .PP Format for dates the same day. See date-format for more info. .PD 0 .P .PD -\f[B]date-today-format\f[R]=\f[I]15:04\f[R] +\f[B]date-tody-format\f[R]=\f[I]\[lq]15:04\[rq]\f[R] .SS date-relative .PP This displays relative dates instead for statuses that are one day or @@ -156,8 +100,8 @@ The value is an integear .PD 1 - \[if] = number of days to use relative dates .PP -Example: date-relative=28 will display a relative date for toots that -are between 1-28 days old. +Value: 28 will display a relative date for toots that are between 1-28 +days old. Otherwhise it will use the short or long format. .PD 0 .P @@ -165,84 +109,66 @@ Otherwhise it will use the short or long format. \f[B]date-relative\f[R]=\f[I]-1\f[R] .SS max-width .PP -The max width of text before it wraps when displaying toots. -.PD 0 -.P -.PD -0 = no restriction. +The max with of text before it wraps when displaying a toot. .PD 0 .P .PD \f[B]max-width\f[R]=\f[I]0\f[R] .SS list-placement .PP -Where do you want the list of toots to be placed? -.PD 0 -.P -.PD -Valid values: left, right, top, bottom. -.PD 0 -.P -.PD -\f[B]list-placement\f[R]=\f[I]left\f[R] +The placement of your panes. +.PP +valid: left, right, top, bottom +.PP +\f[B]list-placement\f[R]=\f[I]\[lq]left\[rq]\f[R] .SS list-split .PP -If you have notification-feed set to true you can display it under the -main list of toots (row) or place it to the right of the main list of -toots (column). -.PD 0 -.P -.PD -\f[B]list-split\f[R]=\f[I]row\f[R] +How should panes be split? +.PP +valid: row, column +.PP +\f[B]list-split\f[R]=\f[I]\[lq]row\[rq]\f[R] .SS list-proportion .PP -You can change the proportions of the list view in relation to the -content view list-proportion=1 and content-proportoin=3 will result in -the content taking up 3 times more space. -.PD 0 -.P -.PD -Must be n > 0 +The proportion of panes vs.\ content. +1 on this and 3 on content below results in content taking up 3 times +more space. .PD 0 .P .PD \f[B]list-proportion\f[R]=\f[I]1\f[R] .SS content-proportion .PP -See list-proportion +See previous. .PD 0 .P .PD \f[B]content-proportion\f[R]=\f[I]2\f[R] .SS notifications-to-hide .PP -Hide notifications of this type. -If you have multiple you separate them with a comma. -Valid types: mention, status, boost, follow, follow_request, favorite, -poll, edit. -.PD 0 -.P -.PD -\f[B]notifications-to-hide\f[R]= +Hide notifications of this type in your notification timelines. +.PP +valid: mention, status, boost, follow, follow_request, favorite, poll, +edit +.PP +\f[B]notifications-to-hide\f[R]=\f[I][]\f[R] .SS quote-reply .PP -If you always want to quote original message when replying. +Always include a quote of the message you\[aq]re replying to. .PD 0 .P .PD \f[B]quote-reply\f[R]=\f[I]false\f[R] .SS show-icons .PP -If you want to show icons in the list of toots. +If you want to show icons in timelines. .PD 0 .P .PD \f[B]show-icons\f[R]=\f[I]true\f[R] .SS short-hints .PP -If you\[aq]ve learnt all the shortcut keys you can remove the help text -and only show the key in tui. -So it gets less cluttered. +If you only want to you the letter of keys instead of the full hint. .PD 0 .P .PD @@ -256,15 +182,14 @@ If you want to display the filter that filtered a toot. \f[B]show-filter-phrase\f[R]=\f[I]true\f[R] .SS show-help .PP -If you want to show a message in the cmdbar on how to access the help -text. +Display a message in the commandbar on how to access the help text. .PD 0 .P .PD \f[B]show-help\f[R]=\f[I]true\f[R] .SS stick-to-top .PP -If you always want tut to jump to the newest post. +Always jump to the newest post. May ruin your reading experience. .PD 0 .P @@ -272,12 +197,27 @@ May ruin your reading experience. \f[B]stick-to-top\f[R]=\f[I]false\f[R] .SS show-boosted-user .PP -If you want to display the username of the person being boosted instead -of the person that boosted. +Display the username of the person being boosted insted of the person +that boosted. .PD 0 .P .PD \f[B]show-boosted-user\f[R]=\f[I]false\f[R] +.SS commands-in-new-pane +.PP +Open a new pane when you run a command like :timeline home. +.PD 0 +.P +.PD +\f[B]commands-in-new-pane\f[R]=\f[I]true\f[R] +.SS dynamic-timeline-name +.PP +Set a default name for the timeline if the name is empty. +So if you run :tag linux the title of the pane will be set to #linux +.PD 0 +.P +.PD +\f[B]dynamic-timeline-name\f[R]=\f[I]true\f[R] .SS terminal-title .PP 0 = No terminal title @@ -288,19 +228,21 @@ of the person that boosted. .PD 0 .P .PD -2 = Only show terminal title, and no top bar in tut. +2 = Only show terminal title, and no top bar in tut .PD 0 .P .PD +3 = No terminal title and no top bar in tut. +.PP +valid: 0, 1, 2, 4 +.PP \f[B]terminal-title\f[R]=\f[I]0\f[R] .SS redraw-ui .PP -If you don\[aq]t want the whole UI to update, and only the text content -you can set this option to true. +If you don\[aq]t want the whole UI to update, and only update the text +content you can disable this. This will lead to some artifacts being left on the screen when emojis are present. -But it will keep the UI from flashing on every single toot in some -terminals. .PD 0 .P .PD @@ -308,13 +250,12 @@ terminals. .SS leader-key .PP The leader is used as a shortcut to run commands as you can do in Vim. -By default this is disabled and you enable it by setting a leader-key. -It can only consist of one char and I like to use comma as leader key. -So to set it you write leader-key=, +By default this is disabled and you enable it by setting a key here. +It can only consist of one char, so set it to something like a comma. .PD 0 .P .PD -\f[B]leader-key\f[R]= +\f[B]leader-key\f[R]=\f[I]\[lq]\[lq]\f[R] .SS leader-timeout .PP Number of milliseconds before the leader command resets. @@ -324,391 +265,559 @@ the input after X milliseconds. .P .PD \f[B]leader-timeout\f[R]=\f[I]1000\f[R] -.SS leader-action +.SH GENERAL.TIMELINES .PP -You set actions for the leader-key with one or more leader-action. -It consists of two parts first the action then the shortcut. -And they\[aq]re separated by a comma. +This section is [[general.timelines]] in your configuration file. +You can have multiple of them. .PP -Available commands: blocking, bookmarks, boosts, clear-notifications, -close-window, compose, direct, edit, favorited, favorites, federated, -followers, following, history, home, list-placement, list-split, lists, -local, mentions, move-window-left, move-window-right, move-window-up, -move-window-down, move-window-home, move-window-end, muting, newer, -notifications, preferences, profile, proportions, refetch, saved, -special-all, special-boosts, special-replies, stick-to-top, switch, tag, -tags, window -.PP -The ones named special-* are the home timeline with only boosts and/or -replies. -All contains both, -boosts only boosts and -replies only replies. +Timelines adds panes of feeds. +You can customize the number of feeds, what they should show and the key +to activate them. .PP -The shortcuts are up to you, but keep them quite short and make sure -they don\[aq]t collide. -If you have one shortcut that is \[dq]f\[dq] and an other one that is -\[dq]fav\[dq], the one with \[dq]f\[dq] will always run and -\[dq]fav\[dq] will never run. +Example: .PP -Some special leaders: +[[general.timelines]] .PD 0 .P .PD -tag is special as you need to add the tag after, e.g.\ tag linux +name=\[dq]home\[dq] .PD 0 .P .PD -window is special as it\[aq]s a shortcut for switching between the -timelines you\[aq]ve set under general and they are zero indexed. -window 0 = your first timeline, window 1 = your second and so on. +type=\[dq]home\[dq] .PD 0 .P .PD -list-placement as it takes the argument top, right, bottom or left +hide-boosts=false .PD 0 .P .PD -list-split as it takes the argument column or row +hide-replies=false +.PP +[[general.timelines]] .PD 0 .P .PD -proportions takes the arguments [int] [int], where the first integer is -the list and the other content, e.g.\ proportions 1 3. -See list-proportion above for more information. +name=\[dq]Notifications\[dq] .PD 0 .P .PD -switch let\[aq]s you go to a timeline if it already exists, if it -doesn\[aq]t it will open the timeline in a new window. -The syntax is almost the same as in timelines= and is displayed under -the examples. -.PP -Some examples: +type=\[dq]notifications\[dq] .PD 0 .P .PD -leader-action=local,lo +keys=[\[dq]n\[dq], \[dq]N\[dq]] .PD 0 .P .PD -leader-action=lists,li +closed=true .PD 0 .P .PD -leader-action=federated,fed +on-creation-closed=\[dq]new-pane\[dq] .PD 0 .P .PD -leader-action=direct,d +on-focus=\[dq]focus-self\[dq] +.SS name +.PP +The name to display above the timeline .PD 0 .P .PD -leader-action=history,h +\f[B]name\f[R]=\f[I]\[lq]\[lq]\f[R] +.SS type +.PP +The type of the timeline +.PP +valid: home, direct, local, federated, bookmarks, saved, favorited, +notifications, lists, mentions, tag +.PP +\f[B]type\f[R]=\f[I]\[lq]\[lq]\f[R] +.SS data +.PP +Used for the tag type, so here you set the tag. +If you have multiple you seperate them with a space. .PD 0 .P .PD -leader-action=tag linux,tl +\f[B]data\f[R]=\f[I]\[lq]\[lq]\f[R] +.SS keys +.PP +A list of keys to give this timeline focus. +See under the input section to learn more about keys. .PD 0 .P .PD -leader-action=window 0,h +\f[B]keys\f[R]=\f[I][]\f[R] +.SS special-keys +.PP +A list of special-keys to give this timeline focus. +See under the input section to learn more about special-keys. .PD 0 .P .PD -leader-action=list-placement bottom,b +\f[B]special-keys\f[R]=\f[I][]\f[R] +.SS shortcut +.PP +A shortcut to give this timeline focus with your leader-key + this +shortcut. .PD 0 .P .PD -leader-action=list-split column,c +\f[B]shortcut\f[R]=\f[I]\[lq]\[lq]\f[R] +.SS hide-boosts +.PP +Hide boosts in this timeline. .PD 0 .P .PD -leader-action=proportions 1 3,3 +\f[B]hide-boosts\f[R]=\f[I]\[lq]false\[rq]\f[R] +.SS hide-replies .PP -Syntax for switch: +Hide replies in this timeline. .PD 0 .P .PD -leader-action=switch feed,shortcut,name,[showBoosts],[showReplies] +\f[B]hide-replies\f[R]=\f[I]\[lq]false\[rq]\f[R] +.SS closed +.PP +Don\[aq]t open this timeline when you start tut. +Use your keys or shortcut to open it. .PD 0 .P .PD -showBoosts can be either true or false and they are both optional. -Here are some examples: +\f[B]closed\f[R]=\f[I]\[lq]false\[rq]\f[R] +.SS on-creation-closed +.PP +Don\[aq]t open this timeline when you start tut. +Use your keys or shortcut to open it. +.PP +valid: new-pane, current-pane +.PP +\f[B]on-creation-closed\f[R]=\f[I]\[lq]new-pane\[rq]\f[R] +.SS on-focus +.PP +Don\[aq]t open this timeline when you start tut. +Use your keys or shortcut to open it. +.PP +valid: focus-pane, focus-self +.PP +\f[B]on-focus\f[R]=\f[I]\[lq]focus-pane\[rq]\f[R] +.SH GENERAL.LEADER-ACTIONS +.PP +This section is [[general.leader-actions]] in your configuration file. +You can have multiple of them. +.PP +You set actions leader-key with one or more leader-actions. +.PP +The shortcuts are up to you, but keep them quite short and make sure +they don\[aq]t collide. +If you have one shortcut that is \[dq]f\[dq] and an other one that is +\[dq]fav\[dq], the one with \[dq]f\[dq] will always run and +\[dq]fav\[dq] will never run. .PP -leader-action=switch home,h,false,true +Some special actions that requires data to be set: .PD 0 .P .PD -leader-action=switch tag tut,tt -.SH MEDIA -.PP -This section is [media] in your configuration file -.SS image-viewer -.PP -Your image viewer. +pane is special as it\[aq]s a shortcut for switching between the panes +you\[aq]ve set under general and they are zero indexed. +pane 0 = your first timeline, pane 1 = your second and so on. .PD 0 .P .PD -\f[B]image-viewer\f[R]=\f[I]xdg-open\f[R] -.SS image-terminal -.PP -Open the image viewer in the same terminal as toot. -Only for terminal based viewers. +list-placement as it takes the argument top, right, bottom or left .PD 0 .P .PD -\f[B]image-terminal\f[R]=\f[I]false\f[R] -.SS image-single -.PP -If images should open one by one e.g.\ \[dq]imv image.png\[dq] multiple -times. -If set to false all images will open at the same time like this \[dq]imv -image1.png image2.png image3.png\[dq]. -Not all image viewers support this, so try it first. +list-split as it takes the argument column or row .PD 0 .P .PD -\f[B]image-single\f[R]=\f[I]true\f[R] -.SS image-reverse +proportions takes the arguments [int] [int], where the first integer is +the list and the other content, e.g.\ proportions 1 3. +See list-proportion above for more information. +.PP +Example: .PP -If you want to open the images in reverse order. -In some image viewers this will display the images in the -\[dq]right\[dq] order. +[[general.leader-actions]] .PD 0 .P .PD -\f[B]image-reverse\f[R]=\f[I]false\f[R] -.SS video-viewer -.PP -Your video viewer. +type=\[dq]close-pane\[dq] .PD 0 .P .PD -\f[B]video-viewer\f[R]=\f[I]xdg-open\f[R] -.SS video-terminal +shortcut=\[dq]q\[dq] .PP -Open the video viewer in the same terminal as toot. -Only for terminal based viewers. +[[general.leader-actions]] .PD 0 .P .PD -\f[B]video-terminal\f[R]=\f[I]false\f[R] -.SS video-single -.PP -If videos should open one by one. -See image-single. +type=\[dq]list-split\[dq] .PD 0 .P .PD -\f[B]video-single\f[R]=\f[I]true\f[R] -.SS video-reverse -.PP -If you want your videos in reverse order. -In some video apps this will play the files in the \[dq]right\[dq] -order. +data=\[dq]row\[dq] .PD 0 .P .PD -\f[B]video-reverse\f[R]=\f[I]false\f[R] -.SS audio-viewer +shortcut=\[dq]r\[dq] .PP -Your audio viewer. +[[general.leader-actions]] .PD 0 .P .PD -\f[B]audio-viewer\f[R]=\f[I]xdg-open\f[R] -.SS audio-terminal -.PP -Open the audio viewer in the same terminal as toot. -Only for terminal based viewers. +type=\[dq]list-split\[dq] .PD 0 .P .PD -\f[B]audio-terminal\f[R]=\f[I]false\f[R] -.SS audio-single -.PP -If audio should open one by one. -See image-single. +data=\[dq]column\[dq] .PD 0 .P .PD -\f[B]audio-single\f[R]=\f[I]true\f[R] -.SS audio-reverse +shortcut=\[dq]c\[dq] +.SS type .PP -If you want to play the audio files in reverse order. -In some audio apps this will play the files in the \[dq]right\[dq] -order. +The action you want to run. +.PP +valid: blocking, boosts, clear-notifications, close-pane, compose, edit, +favorited, favorites, followers, following, history, list-placement, +list-split, lists, move-pane-left, move-pane-right, move-pane-up, +move-pane-down, move-pane-home, move-pane-end, muting, newer, pane, +preferences, profile, proportions, refetch, stick-to-top, tags +.PP +\f[B]type\f[R]=\f[I]\[lq]\[lq]\f[R] +.SS data +.PP +Data to pass to the action. .PD 0 .P .PD -\f[B]audio-reverse\f[R]=\f[I]false\f[R] -.SS link-viewer +\f[B]data\f[R]=\f[I]\[lq]\[lq]\f[R] +.SS shortcut .PP -Your web browser. +A shortcut to run this action with your leader-key + this shortcut. .PD 0 .P .PD -\f[B]link-viewer\f[R]=\f[I]xdg-open\f[R] -.SS link-terminal +\f[B]shortcut\f[R]=\f[I]\[lq]\[lq]\f[R] +.SH MEDIA .PP -Open the browser in the same terminal as toot. -Only for terminal based browsers. +This section is [media] in your configuration file +.SS delete-temp-files +.PP +Media files will be removed directly after they\[aq]ve been opened. +Some programs doesn\[aq]t like this, so if your media doesn\[aq]t open, +try set this to false. +Tut will remove all files once you close the program. .PD 0 .P .PD -\f[B]link-terminal\f[R]=\f[I]false\f[R] -.SH OPEN-CUSTOM -.PP -This section is [open-custom] in your configuration file +\f[B]delete-temp-files\f[R]=\f[I]true\f[R] +.SH MEDIA.IMAGE .PP -This sections allows you to set up to five custom programs to open URLs -with. -If the url points to an image, you can set c1-name to img and c1-use to -imv. -If the program runs in a terminal and you want to run it in the same -terminal as tut. -Set cX-terminal to true. -The name will show up in the UI, so keep it short so all five fits. +This section is [media.image] in your configuration file +.SS program .PP -c1-name=name +The program to open images. +TUT_OS_DEFAULT equals xdg-open on Linux, open on MacOS and start on +Windows. .PD 0 .P .PD -c1-use=program +\f[B]program\f[R]=\f[I]\[lq]TUT_OS_DEFAULT\[rq]\f[R] +.SS args +.PP +Arguments to pass to the program. .PD 0 .P .PD -c1-terminal=false +\f[B]args\f[R]=\f[I]\[lq]\[lq]\f[R] +.SS terminal .PP -c2-name=name +If the program runs in the terminal set this to true. .PD 0 .P .PD -c2-use=program +\f[B]terminal\f[R]=\f[I]false\f[R] +.SS single +.PP +If the program should be called multiple times when there is multiple +files. +If set to false all files will be passed as an argument, but not all +programs support this. .PD 0 .P .PD -c2-terminal=false +\f[B]single\f[R]=\f[I]true\f[R] +.SS reverse .PP -c3-name=name +If the files should be passed in reverse order. +This will make some programs display the files in the correct order. .PD 0 .P .PD -c3-use=program +\f[B]reverse\f[R]=\f[I]false\f[R] +.SH MEDIA.VIDEO +.PP +This section is [media.video] in your configuration file +.SS program +.PP +The program to open videos. +TUT_OS_DEFAULT equals xdg-open on Linux, open on MacOS and start on +Windows. .PD 0 .P .PD -c3-terminal=false +\f[B]program\f[R]=\f[I]\[lq]TUT_OS_DEFAULT\[rq]\f[R] +.SS args .PP -c4-name=name +Arguments to pass to the program. .PD 0 .P .PD -c4-use=program +\f[B]args\f[R]=\f[I]\[lq]\[lq]\f[R] +.SS terminal +.PP +If the program runs in the terminal set this to true. .PD 0 .P .PD -c4-terminal=false +\f[B]terminal\f[R]=\f[I]false\f[R] +.SS single .PP -c5-name=name +If the program should be called multiple times when there is multiple +files. +If set to false all files will be passed as an argument, but not all +programs support this. .PD 0 .P .PD -c5-use=program +\f[B]single\f[R]=\f[I]true\f[R] +.SS reverse +.PP +If the files should be passed in reverse order. +This will make some programs display the files in the correct order. .PD 0 .P .PD -c5-terminal=false -.SH OPEN-PATTERN +\f[B]reverse\f[R]=\f[I]false\f[R] +.SH MEDIA.AUDIO .PP -This section is [open-pattern] in your configuration file +This section is [media.audio] in your configuration file +.SS program .PP -Here you can set your own glob patterns for opening matching URLs in the -program you want them to open up in. -You could for example open Youtube videos in your video player instead -of your default browser. +The program to open audio. +TUT_OS_DEFAULT equals xdg-open on Linux, open on MacOS and start on +Windows. +.PD 0 +.P +.PD +\f[B]program\f[R]=\f[I]\[lq]TUT_OS_DEFAULT\[rq]\f[R] +.SS args .PP -You must name the keys foo-pattern, foo-use and foo-terminal, where use -is the program that will open up the URL. -To see the syntax for glob pattern you can follow this URL -https://github.com/gobwas/glob#syntax. -foo-terminal is if the program runs in the terminal and should open in -the same terminal as tut itself. +Arguments to pass to the program. +.PD 0 +.P +.PD +\f[B]args\f[R]=\f[I]\[lq]\[lq]\f[R] +.SS terminal +.PP +If the program runs in the terminal set this to true. +.PD 0 +.P +.PD +\f[B]terminal\f[R]=\f[I]false\f[R] +.SS single .PP -Example for youtube.com and youtu.be to open up in mpv instead of the -browser. +If the program should be called multiple times when there is multiple +files. +If set to false all files will be passed as an argument, but not all +programs support this. +.PD 0 +.P +.PD +\f[B]single\f[R]=\f[I]true\f[R] +.SS reverse .PP -y1-pattern=*youtube.com/watch* +If the files should be passed in reverse order. +This will make some programs display the files in the correct order. .PD 0 .P .PD -y1-use=mpv +\f[B]reverse\f[R]=\f[I]false\f[R] +.SH MEDIA.LINK +.PP +This section is [media.link] in your configuration file +.SS program +.PP +The program to open links. +TUT_OS_DEFAULT equals xdg-open on Linux, open on MacOS and start on +Windows. .PD 0 .P .PD -y1-terminal=false +\f[B]program\f[R]=\f[I]\[lq]TUT_OS_DEFAULT\[rq]\f[R] +.SS args .PP -y2-pattern=*youtu.be/* +Arguments to pass to the program. .PD 0 .P .PD -y2-use=mpv +\f[B]args\f[R]=\f[I]\[lq]\[lq]\f[R] +.SS terminal +.PP +If the program runs in the terminal set this to true. .PD 0 .P .PD -y2-terminal=false +\f[B]terminal\f[R]=\f[I]false\f[R] .SH DESKTOP-NOTIFICATION .PP This section is [desktop-notification] in your configuration file .SS followers .PP -Notification when someone follows you. +Enable notifications when someone follows you. .PD 0 .P .PD \f[B]followers\f[R]=\f[I]false\f[R] .SS favorite .PP -Notification when someone favorites one of your toots. +Enable notifications when one of your toots gets favorited. .PD 0 .P .PD \f[B]favorite\f[R]=\f[I]false\f[R] .SS mention .PP -Notification when someone mentions you. +Enable notifications when someone mentions you. .PD 0 .P .PD \f[B]mention\f[R]=\f[I]false\f[R] .SS update .PP -Notification when someone edits their toot. +Enable notifications when a post you have interacted with gets edited. .PD 0 .P .PD \f[B]update\f[R]=\f[I]false\f[R] .SS boost .PP -Notification when someone boosts one of your toots. +Enable notifications when one of your toots gets boosted. .PD 0 .P .PD \f[B]boost\f[R]=\f[I]false\f[R] .SS poll .PP -Notification of poll results. +Enable notifications when a poll ends. .PD 0 .P .PD \f[B]poll\f[R]=\f[I]false\f[R] .SS posts .PP -Notification when there is new posts in current timeline. +Enable notifications for new posts. .PD 0 .P .PD \f[B]posts\f[R]=\f[I]false\f[R] +.SH OPEN-CUSTOM +.PP +This section is [open-custom] in your configuration file +.SH OPEN-CUSTOM.PROGRAMS +.PP +This section is [[open-custom.programs]] in your configuration file. +You can have multiple of them. +.SS program +.PP +The program to open the file with. +.PD 0 +.P +.PD +\f[B]program\f[R]=\f[I]\[lq]\[lq]\f[R] +.SS args +.PP +Arguments to pass to the program. +.PD 0 +.P +.PD +\f[B]args\f[R]=\f[I]\[lq]\[lq]\f[R] +.SS terminal +.PP +If the program runs in the terminal set this to true. +.PD 0 +.P +.PD +\f[B]terminal\f[R]=\f[I]false\f[R] +.SS hint +.PP +What should the key hint in tut be for this program. +See under the input section to learn more about hint. +.PD 0 +.P +.PD +\f[B]hint\f[R]=\f[I]\[lq]\[lq]\f[R] +.SS keys +.PP +A list of keys to to open files with this program. +See under the input section to learn more about keys. +.PD 0 +.P +.PD +\f[B]keys\f[R]=\f[I][]\f[R] +.SS special-keys +.PP +A list of special-keys to open files with this program. +See under the input section to learn more about special-keys. +.PD 0 +.P +.PD +\f[B]special-keys\f[R]=\f[I][]\f[R] +.SH OPEN-PATTERN +.PP +This section is [open-pattern] in your configuration file +.SH OPEN-PATTERN.PROGRAMS +.PP +This section is [[open-pattern.programs]] in your configuration file. +You can have multiple of them. +.SS matching +.PP +Here you can set your own glob patterns for opening matching URLs in the +program you want them to open up in. +You could for example open Youtube videos in your video player instead +of your default browser. +To see the syntax for glob pattern you can follow this URL +https://github.com/gobwas/glob#syntax. +.PD 0 +.P +.PD +\f[B]matching\f[R]=\f[I]\[lq]\[lq]\f[R] +.SS program +.PP +The program to open the file with. +.PD 0 +.P +.PD +\f[B]program\f[R]=\f[I]\[lq]\[lq]\f[R] +.SS args +.PP +Arguments to pass to the program. +.PD 0 +.P +.PD +\f[B]args\f[R]=\f[I]\[lq]\[lq]\f[R] +.SS terminal +.PP +If the program runs in the terminal set this to true. +.PD 0 +.P +.PD +\f[B]terminal\f[R]=\f[I]false\f[R] .SH STYLE .PP This section is [style] in your configuration file @@ -717,52 +826,54 @@ All styles can be represented in their HEX value like #ffffff or with their name, so in this case white. The only special value is \[dq]default\[dq] which equals to transparent, so it will be the same color as your terminal. -.PP +.PD 0 +.P +.PD You can also use xrdb colors like this xrdb:color1 The program will use colors prefixed with an * first then look for URxvt or XTerm if it can\[aq]t find any color prefixed with an asterisk. If you don\[aq]t want tut to guess the prefix you can set the prefix yourself. If the xrdb color can\[aq]t be found a preset color will be used. -You\[aq]ll have to set theme=none for this to work. -.SS xrdb-prefix -.PP -The xrdb prefix used for colors in .Xresources. -.PD 0 -.P -.PD -\f[B]xrdb-prefix\f[R]=\f[I]guess\f[R] +You\[aq]ll have to set theme=\[dq]none\[dq] for this to work. .SS theme .PP +The theme to use. You can use some themes that comes bundled with tut. Check out the themes available on the URL below. -If a theme is named \[dq]nord.ini\[dq] you just write theme=nord +If a theme is named nord.toml you just write theme=\[dq]nord\[dq]. .PP https://github.com/RasmusLindroth/tut/tree/master/config/themes .PP You can also create a theme file in your config directory -e.g.\ \[ti]/.config/tut/themes/foo.ini and then set theme=foo. +e.g.\ \[ti]/.config/tut/themes/foo.toml and then set theme=foo. .PP If you want to use your own theme but don\[aq]t want to create a new -file, set theme=none and then you can create your own theme below. +file, set theme=\[dq]none\[dq] and then you can create your own theme +below. +.PP +\f[B]theme\f[R]=\f[I]\[lq]default\[rq]\f[R] +.SS xrdb-prefix +.PP +The xrdb prefix used for colors in .Xresources. .PD 0 .P .PD -\f[B]theme\f[R]=\f[I]default\f[R] +\f[B]xrdb-prefix\f[R]=\f[I]\[lq]guess\[rq]\f[R] .SS background .PP The background color used on most elements. .PD 0 .P .PD -\f[B]background\f[R]= +\f[B]background\f[R]=\f[I]\[lq]\[lq]\f[R] .SS text .PP The text color used on most of the text. .PD 0 .P .PD -\f[B]text\f[R]= +\f[B]text\f[R]=\f[I]\[lq]\[lq]\f[R] .SS subtle .PP The color to display subtle elements or subtle text. @@ -770,147 +881,147 @@ Like lines and help text. .PD 0 .P .PD -\f[B]subtle\f[R]= +\f[B]subtle\f[R]=\f[I]\[lq]\[lq]\f[R] .SS warning-text .PP The color for errors or warnings .PD 0 .P .PD -\f[B]warning-text\f[R]= +\f[B]warning-text\f[R]=\f[I]\[lq]\[lq]\f[R] .SS text-special-one .PP This color is used to display username. .PD 0 .P .PD -\f[B]text-special-one\f[R]= +\f[B]text-special-one\f[R]=\f[I]\[lq]\[lq]\f[R] .SS text-special-two .PP This color is used to display username and key hints. .PD 0 .P .PD -\f[B]text-special-two\f[R]= +\f[B]text-special-two\f[R]=\f[I]\[lq]\[lq]\f[R] .SS top-bar-background .PP The color of the bar at the top .PD 0 .P .PD -\f[B]top-bar-background\f[R]= +\f[B]top-bar-background\f[R]=\f[I]\[lq]\[lq]\f[R] .SS top-bar-text .PP The color of the text in the bar at the top. .PD 0 .P .PD -\f[B]top-bar-text\f[R]= +\f[B]top-bar-text\f[R]=\f[I]\[lq]\[lq]\f[R] .SS status-bar-background .PP The color of the bar at the bottom .PD 0 .P .PD -\f[B]status-bar-background\f[R]= +\f[B]status-bar-background\f[R]=\f[I]\[lq]\[lq]\f[R] .SS status-bar-text .PP The color of the text in the bar at the bottom. .PD 0 .P .PD -\f[B]status-bar-text\f[R]= +\f[B]status-bar-text\f[R]=\f[I]\[lq]\[lq]\f[R] .SS status-bar-view-background .PP The color of the bar at the bottom in view mode. .PD 0 .P .PD -\f[B]status-bar-view-background\f[R]= +\f[B]status-bar-view-background\f[R]=\f[I]\[lq]\[lq]\f[R] .SS status-bar-view-text .PP The color of the text in the bar at the bottom in view mode. .PD 0 .P .PD -\f[B]status-bar-view-text\f[R]= +\f[B]status-bar-view-text\f[R]=\f[I]\[lq]\[lq]\f[R] .SS command-text .PP The color of the text in the command bar at the bottom. .PD 0 .P .PD -\f[B]command-text\f[R]= +\f[B]command-text\f[R]=\f[I]\[lq]\[lq]\f[R] .SS list-selected-background .PP Background of selected list items. .PD 0 .P .PD -\f[B]list-selected-background\f[R]= +\f[B]list-selected-background\f[R]=\f[I]\[lq]\[lq]\f[R] .SS list-selected-text .PP The text color of selected list items. .PD 0 .P .PD -\f[B]list-selected-text\f[R]= +\f[B]list-selected-text\f[R]=\f[I]\[lq]\[lq]\f[R] .SS list-selected-inactive-background .PP The background color of selected list items that are out of focus. .PD 0 .P .PD -\f[B]list-selected-inactive-background\f[R]= +\f[B]list-selected-inactive-background\f[R]=\f[I]\[lq]\[lq]\f[R] .SS list-selected-inactive-text .PP The text color of selected list items that are out of focus. .PD 0 .P .PD -\f[B]list-selected-inactive-text\f[R]= +\f[B]list-selected-inactive-text\f[R]=\f[I]\[lq]\[lq]\f[R] .SS controls-text .PP The main color of the text for key hints .PD 0 .P .PD -\f[B]controls-text\f[R]= +\f[B]controls-text\f[R]=\f[I]\[lq]\[lq]\f[R] .SS controls-highlight .PP The highlight color of for key hints .PD 0 .P .PD -\f[B]controls-highlight\f[R]= +\f[B]controls-highlight\f[R]=\f[I]\[lq]\[lq]\f[R] .SS autocomplete-background .PP The background color in dropdowns and autocompletions .PD 0 .P .PD -\f[B]autocomplete-background\f[R]= +\f[B]autocomplete-background\f[R]=\f[I]\[lq]\[lq]\f[R] .SS autocomplete-text .PP The text color in dropdowns at autocompletions .PD 0 .P .PD -\f[B]autocomplete-text\f[R]= +\f[B]autocomplete-text\f[R]=\f[I]\[lq]\[lq]\f[R] .SS autocomplete-selected-background .PP The background color for selected value in dropdowns and autocompletions .PD 0 .P .PD -\f[B]autocomplete-selected-background\f[R]= +\f[B]autocomplete-selected-background\f[R]=\f[I]\[lq]\[lq]\f[R] .SS autocomplete-selected-text .PP The text color for selected value in dropdowns and autocompletions .PD 0 .P .PD -\f[B]autocomplete-selected-text\f[R]= +\f[B]autocomplete-selected-text\f[R]=\f[I]\[lq]\[lq]\f[R] .SS button-color-one .PP The background color on selected button and the text color of unselected @@ -918,7 +1029,7 @@ buttons .PD 0 .P .PD -\f[B]button-color-one\f[R]= +\f[B]button-color-one\f[R]=\f[I]\[lq]\[lq]\f[R] .SS button-color-two .PP The text color on selected button and the background color of unselected @@ -926,603 +1037,902 @@ buttons .PD 0 .P .PD -\f[B]button-color-two\f[R]= +\f[B]button-color-two\f[R]=\f[I]\[lq]\[lq]\f[R] .SS timeline-name-background .PP The background on named timelines. .PD 0 .P .PD -\f[B]timeline-name-background\f[R]= +\f[B]timeline-name-background\f[R]=\f[I]\[lq]\[lq]\f[R] .SS timeline-name-text .PP The text color on named timelines .PD 0 .P .PD -\f[B]timeline-name-text\f[R]= +\f[B]timeline-name-text\f[R]=\f[I]\[lq]\[lq]\f[R] .SH INPUT .PP This section is [input] in your configuration file .PP -You can edit the keys for tut below. -.PP -The syntax is a bit weird, but it works. -And I\[aq]ll try to explain it as well as I can. +In this section you set the keys to be used in tut. .PP -Example: -.PD 0 -.P -.PD -status-favorite=\[dq][F]avorite\[dq],\[dq]Un[F]avorite\[dq],\[aq]f\[aq],\[aq]F\[aq] +The hint option lets you set which part of the hint that will be +highlighted in tut. +E.g. +[F]avorite results in a highlighted F and the rest of the text is +displayed normaly. .PD 0 .P .PD -status-delete=\[dq][D]elete\[dq],\[aq]d\[aq],\[aq]D\[aq] +Some of the options can be in two states, like favorites, so there you +can set the hint-alt option to something like Un[F]avorite. .PP -status-favorite and status-delete differs because favorite can be in two -states, so you will have to add two key hints. +Examples: .PD 0 .P .PD -Most keys will only have on key hint. -Look at the default value for reference. -.PP -Key hints must be in some of the following formats. -Remember the quotation marks. +\[dq][D]elete\[dq] = Delete with a highlighted D .PD 0 .P .PD -\[dq]\[dq] = empty +\[dq]Un[F]ollow\[dq] = UnFollow with a highlighted F .PD 0 .P .PD -\[dq][D]elete\[dq] = Delete with a highlighted D +\[dq][Enter]\[dq] = Enter where everything is highlighted .PD 0 .P .PD -\[dq]Un[F]ollow\[dq] = UnFollow with a highlighted F +\[dq]Yan[K]\[dq] = YanK with a highlighted K +.PP +The keys option lets you define what key that should be pressed. +This is limited to on character only and they are case sensetive. .PD 0 .P .PD -\[dq][Enter]\[dq] = Enter where everything is highlighted +Example: .PD 0 .P .PD -\[dq]Yan[K]\[dq] = YanK with a highlighted K -.PP -After the hint (or hints) you must set the keys. -You can do this in two ways, with single quotation marks or double ones. -.PP -The single ones are for single chars like \[aq]a\[aq], \[aq]b\[aq], -\[aq]c\[aq] and double marks are for special keys like \[dq]Enter\[dq]. -Remember that they are case sensitive. +keys=[\[dq]j\[dq],\[dq]J\[dq]] .PP +You can also set special-keys and they\[aq]re for keys like Escape and +Enter. To find the names of special keys you have to go to the following site and look for \[dq]var KeyNames = map[Key]string{\[dq] .PP https://github.com/gdamore/tcell/blob/master/key.go -.SS global-down +.SH INPUT.GLOBAL-DOWN +.PP +This section is [input.global-down] in your configuration file .PP Keys for moving down -.PD 0 -.P -.PD -\f[B]global-down\f[R]=\f[I]\[dq]\[dq],\[aq]j\[aq],\[aq]J\[aq],\[dq]Down\[dq]\f[R] -.SS global-up +.SS keys .PP -Keys for moving up -.PD 0 -.P -.PD -\f[B]global-up\f[R]=\f[I]\[dq]\[dq],\[aq]k\[aq],\[aq]K\[aq],\[dq]Up\[dq]\f[R] -.SS global-enter +\f[B]keys\f[R]=\f[I][\[lq]j\[rq],\[lq]J\[rq]]\f[R] +.SS special-keys +.PP +\f[B]special-keys\f[R]=\f[I][\[lq]Down\[rq]]\f[R] +.SH INPUT.GLOBAL-UP +.PP +This section is [input.global-up] in your configuration file +.PP +Keys for moving down +.SS keys +.PP +\f[B]keys\f[R]=\f[I][\[lq]k\[rq],\[lq]K\[rq]]\f[R] +.SS special-keys +.PP +\f[B]special-keys\f[R]=\f[I][\[lq]Up\[rq]]\f[R] +.SH INPUT.GLOBAL-ENTER +.PP +This section is [input.global-enter] in your configuration file .PP To select items -.PD 0 -.P -.PD -\f[B]global-enter\f[R]=\f[I]\[dq]\[dq],\[dq]Enter\[dq]\f[R] -.SS global-back +.SS special-keys +.PP +\f[B]special-keys\f[R]=\f[I][\[lq]Enter\[rq]]\f[R] +.SH INPUT.GLOBAL-BACK +.PP +This section is [input.global-back] in your configuration file .PP To go back -.PD 0 -.P -.PD -\f[B]global-back\f[R]=\f[I]\[dq][Esc]\[dq],\[dq]Esc\[dq]\f[R] -.SS global-exit +.SS hint .PP -To go back and exit Tut -.PD 0 -.P -.PD -\f[B]global-exit\f[R]=\f[I]\[dq][Q]uit\[dq],\[aq]q\[aq],\[aq]Q\[aq]\f[R] -.SS main-home +\f[B]hint\f[R]=\f[I]\[lq][Esc]\[rq]\f[R] +.SS special-keys +.PP +\f[B]special-keys\f[R]=\f[I][\[lq]Esc\[rq]]\f[R] +.SH INPUT.GLOBAL-EXIT +.PP +This section is [input.global-exit] in your configuration file +.PP +To go back or exit +.SS hint +.PP +\f[B]hint\f[R]=\f[I]\[lq][Q]uit\[rq]\f[R] +.SS keys +.PP +\f[B]keys\f[R]=\f[I][\[lq]q\[rq],\[lq]Q\[rq]]\f[R] +.SH INPUT.MAIN-HOME +.PP +This section is [input.main-home] in your configuration file .PP Move to the top -.PD 0 -.P -.PD -\f[B]main-home\f[R]=\f[I]\[dq]\[dq],\[aq]g\[aq],\[dq]Home\[dq]\f[R] -.SS main-end +.SS keys +.PP +\f[B]keys\f[R]=\f[I][\[lq]g\[rq]]\f[R] +.SS special-keys +.PP +\f[B]special-keys\f[R]=\f[I][\[lq]Home\[rq]]\f[R] +.SH INPUT.MAIN-END +.PP +This section is [input.main-end] in your configuration file .PP Move to the bottom -.PD 0 -.P -.PD -\f[B]main-end\f[R]=\f[I]\[dq]\[dq],\[aq]G\[aq],\[dq]End\[dq]\f[R] -.SS main-prev-feed +.SS keys +.PP +\f[B]keys\f[R]=\f[I][\[lq]G\[rq]]\f[R] +.SS special-keys +.PP +\f[B]special-keys\f[R]=\f[I][\[lq]End\[rq]]\f[R] +.SH INPUT.MAIN-PREV-FEED +.PP +This section is [input.main-prev-feed] in your configuration file .PP Go to previous feed -.PD 0 -.P -.PD -\f[B]main-prev-feed\f[R]=\f[I]\[dq]\[dq],\[aq]h\[aq],\[aq]H\[aq],\[dq]Left\[dq]\f[R] -.SS main-next-feed +.SS keys +.PP +\f[B]keys\f[R]=\f[I][\[lq]h\[rq],\[lq]H\[rq]]\f[R] +.SS special-keys +.PP +\f[B]special-keys\f[R]=\f[I][\[lq]Left\[rq]]\f[R] +.SH INPUT.MAIN-NEXT-FEED +.PP +This section is [input.main-next-feed] in your configuration file .PP Go to next feed -.PD 0 -.P -.PD -\f[B]main-next-feed\f[R]=\f[I]\[dq]\[dq],\[aq]l\[aq],\[aq]L\[aq],\[dq]Right\[dq]\f[R] -.SS main-prev-window +.SS keys .PP -Focus on the previous feed window -.PD 0 -.P -.PD -\f[B]main-prev-window\f[R]=\f[I]\[dq]\[dq],\[dq]Backtab\[dq]\f[R] -.SS main-next-window +\f[B]keys\f[R]=\f[I][\[lq]l\[rq],\[lq]L\[rq]]\f[R] +.SS special-keys .PP -Focus on the next feed window -.PD 0 -.P -.PD -\f[B]main-next-window\f[R]=\f[I]\[dq]\[dq],\[dq]Tab\[dq]\f[R] -.SS main-notification-focus +\f[B]special-keys\f[R]=\f[I][\[lq]Right\[rq]]\f[R] +.SH INPUT.MAIN-PREV-PANE .PP -Focus on the notification list -.PD 0 -.P -.PD -\f[B]main-notification-focus\f[R]=\f[I]\[dq][N]otifications\[dq],\[aq]n\[aq],\[aq]N\[aq]\f[R] -.SS main-compose +This section is [input.main-prev-pane] in your configuration file +.PP +Focus on the previous feed pane +.SS special-keys +.PP +\f[B]special-keys\f[R]=\f[I][\[lq]Backtab\[rq]]\f[R] +.SH INPUT.MAIN-NEXT-PANE +.PP +This section is [input.main-next-pane] in your configuration file +.PP +Focus on the next feed pane +.SS special-keys +.PP +\f[B]special-keys\f[R]=\f[I][\[lq]Tab\[rq]]\f[R] +.SH INPUT.MAIN-NEXT-ACCOUNT +.PP +This section is [input.main-next-account] in your configuration file +.PP +Focus on the next account +.SS special-keys +.PP +\f[B]special-keys\f[R]=\f[I][\[lq]Ctrl-N\[rq]]\f[R] +.SH INPUT.MAIN-PREV-ACCOUNT +.PP +This section is [input.main-prev-account] in your configuration file +.PP +Focus on the previous account +.SS special-keys +.PP +\f[B]special-keys\f[R]=\f[I][\[lq]Ctrl-P\[rq]]\f[R] +.SH INPUT.MAIN-COMPOSE +.PP +This section is [input.main-compose] in your configuration file .PP Compose a new toot -.PD 0 -.P -.PD -\f[B]main-compose\f[R]=\f[I]\[dq]\[dq],\[aq]c\[aq],\[aq]C\[aq]\f[R] -.SS status-avatar +.SS keys +.PP +\f[B]keys\f[R]=\f[I][\[lq]c\[rq],\[lq]C\[rq]]\f[R] +.SH INPUT.STATUS-AVATAR +.PP +This section is [input.status-avatar] in your configuration file .PP Open avatar -.PD 0 -.P -.PD -\f[B]status-avatar\f[R]=\f[I]\[dq][A]vatar\[dq],\[aq]a\[aq],\[aq]A\[aq]\f[R] -.SS status-boost +.SS hint +.PP +\f[B]hint\f[R]=\f[I]\[lq][A]vatar\[rq]\f[R] +.SS keys +.PP +\f[B]keys\f[R]=\f[I][\[lq]a\[rq],\[lq]A\[rq]]\f[R] +.SH INPUT.STATUS-BOOST +.PP +This section is [input.status-boost] in your configuration file .PP Boost a toot -.PD 0 -.P -.PD -\f[B]status-boost\f[R]=\f[I]\[dq][B]oost\[dq],\[dq]Un[B]oost\[dq],\[aq]b\[aq],\[aq]B\[aq]\f[R] -.SS status-edit +.SS hint +.PP +\f[B]hint\f[R]=\f[I]\[lq][B]oost\[rq]\f[R] +.SS keys +.PP +\f[B]keys\f[R]=\f[I][\[lq]b\[rq],\[lq]B\[rq]]\f[R] +.SH INPUT.STATUS-EDIT +.PP +This section is [input.status-edit] in your configuration file .PP Edit a toot -.PD 0 -.P -.PD -\f[B]status-edit\f[R]=\f[I]\[dq][E]dit\[dq],\[aq]e\[aq],\[aq]E\[aq]\f[R] -.SS status-delete +.SS hint +.PP +\f[B]hint\f[R]=\f[I]\[lq][E]dit\[rq]\f[R] +.SS keys +.PP +\f[B]keys\f[R]=\f[I][\[lq]e\[rq],\[lq]E\[rq]]\f[R] +.SH INPUT.STATUS-DELETE +.PP +This section is [input.status-delete] in your configuration file .PP Delete a toot -.PD 0 -.P -.PD -\f[B]status-delete\f[R]=\f[I]\[dq][D]elete\[dq],\[aq]d\[aq],\[aq]D\[aq]\f[R] -.SS status-favorite +.SS hint +.PP +\f[B]hint\f[R]=\f[I]\[lq][D]elete\[rq]\f[R] +.SS keys +.PP +\f[B]keys\f[R]=\f[I][\[lq]d\[rq],\[lq]D\[rq]]\f[R] +.SH INPUT.STATUS-FAVORITE +.PP +This section is [input.status-favorite] in your configuration file .PP Favorite a toot -.PD 0 -.P -.PD -\f[B]status-favorite\f[R]=\f[I]\[dq][F]avorite\[dq],\[dq]Un[F]avorite\[dq],\[aq]f\[aq],\[aq]F\[aq]\f[R] -.SS status-media +.SS hint +.PP +\f[B]hint\f[R]=\f[I]\[lq][F]avorite\[rq]\f[R] +.SS keys +.PP +\f[B]keys\f[R]=\f[I][\[lq]f\[rq],\[lq]F\[rq]]\f[R] +.SH INPUT.STATUS-MEDIA +.PP +This section is [input.status-media] in your configuration file .PP Open toots media files -.PD 0 -.P -.PD -\f[B]status-media\f[R]=\f[I]\[dq][M]edia\[dq],\[aq]m\[aq],\[aq]M\[aq]\f[R] -.SS status-links +.SS hint +.PP +\f[B]hint\f[R]=\f[I]\[lq][M]edia\[rq]\f[R] +.SS keys +.PP +\f[B]keys\f[R]=\f[I][\[lq]m\[rq],\[lq]M\[rq]]\f[R] +.SH INPUT.STATUS-LINKS +.PP +This section is [input.status-links] in your configuration file .PP Open links -.PD 0 -.P -.PD -\f[B]status-links\f[R]=\f[I]\[dq][O]pen\[dq],\[aq]o\[aq],\[aq]O\[aq]\f[R] -.SS status-poll +.SS hint +.PP +\f[B]hint\f[R]=\f[I]\[lq][O]pen\[rq]\f[R] +.SS keys +.PP +\f[B]keys\f[R]=\f[I][\[lq]o\[rq],\[lq]O\[rq]]\f[R] +.SH INPUT.STATUS-POLL +.PP +This section is [input.status-poll] in your configuration file .PP Open poll -.PD 0 -.P -.PD -\f[B]status-poll\f[R]=\f[I]\[dq][P]oll\[dq],\[aq]p\[aq],\[aq]P\[aq]\f[R] -.SS status-reply +.SS hint +.PP +\f[B]hint\f[R]=\f[I]\[lq][P]oll\[rq]\f[R] +.SS keys +.PP +\f[B]keys\f[R]=\f[I][\[lq]p\[rq],\[lq]P\[rq]]\f[R] +.SH INPUT.STATUS-REPLY +.PP +This section is [input.status-reply] in your configuration file .PP Reply to toot -.PD 0 -.P -.PD -\f[B]status-reply\f[R]=\f[I]\[dq][R]eply\[dq],\[aq]r\[aq],\[aq]R\[aq]\f[R] -.SS status-bookmark +.SS hint +.PP +\f[B]hint\f[R]=\f[I]\[lq][R]eply\[rq]\f[R] +.SS keys +.PP +\f[B]keys\f[R]=\f[I][\[lq]r\[rq],\[lq]R\[rq]]\f[R] +.SH INPUT.STATUS-BOOKMARK +.PP +This section is [input.status-bookmark] in your configuration file .PP Save/bookmark a toot -.PD 0 -.P -.PD -\f[B]status-bookmark\f[R]=\f[I]\[dq][S]ave\[dq],\[dq]Un[S]ave\[dq],\[aq]s\[aq],\[aq]S\[aq]\f[R] -.SS status-thread +.SS hint +.PP +\f[B]hint\f[R]=\f[I]\[lq][S]ave\[rq]\f[R] +.SS hint-alt +.PP +\f[B]hint-alt\f[R]=\f[I]\[lq]Un[S]ave\[rq]\f[R] +.SS keys +.PP +\f[B]keys\f[R]=\f[I][\[lq]s\[rq],\[lq]S\[rq]]\f[R] +.SH INPUT.STATUS-THREAD +.PP +This section is [input.status-thread] in your configuration file .PP View thread -.PD 0 -.P -.PD -\f[B]status-thread\f[R]=\f[I]\[dq][T]hread\[dq],\[aq]t\[aq],\[aq]T\[aq]\f[R] -.SS status-user +.SS hint +.PP +\f[B]hint\f[R]=\f[I]\[lq][T]hread\[rq]\f[R] +.SS keys +.PP +\f[B]keys\f[R]=\f[I][\[lq]t\[rq],\[lq]T\[rq]]\f[R] +.SH INPUT.STATUS-USER +.PP +This section is [input.status-user] in your configuration file .PP Open user profile -.PD 0 -.P -.PD -\f[B]status-user\f[R]=\f[I]\[dq][U]ser\[dq],\[aq]u\[aq],\[aq]U\[aq]\f[R] -.SS status-view-focus +.SS hint +.PP +\f[B]hint\f[R]=\f[I]\[lq][U]ser\[rq]\f[R] +.SS keys +.PP +\f[B]keys\f[R]=\f[I][\[lq]u\[rq],\[lq]U\[rq]]\f[R] +.SH INPUT.STATUS-VIEW-FOCUS +.PP +This section is [input.status-view-focus] in your configuration file .PP Open the view mode -.PD 0 -.P -.PD -\f[B]status-view-focus\f[R]=\f[I]\[dq][V]iew\[dq],\[aq]v\[aq],\[aq]V\[aq]\f[R] -.SS status-yank +.SS hint +.PP +\f[B]hint\f[R]=\f[I]\[lq][V]iew\[rq]\f[R] +.SS keys +.PP +\f[B]keys\f[R]=\f[I][\[lq]v\[rq],\[lq]V\[rq]]\f[R] +.SH INPUT.STATUS-YANK +.PP +This section is [input.status-yank] in your configuration file .PP Yank the url of the toot -.PD 0 -.P -.PD -\f[B]status-yank\f[R]=\f[I]\[dq][Y]ank\[dq],\[aq]y\[aq],\[aq]Y\[aq]\f[R] -.SS status-toggle-cw +.SS hint +.PP +\f[B]hint\f[R]=\f[I]\[lq][Y]ank\[rq]\f[R] +.SS keys +.PP +\f[B]keys\f[R]=\f[I][\[lq]y\[rq],\[lq]Y\[rq]]\f[R] +.SH INPUT.STATUS-TOGGLE-CW +.PP +This section is [input.status-toggle-cw] in your configuration file .PP Show the content in a content warning -.PD 0 -.P -.PD -\f[B]status-toggle-cw\f[R]=\f[I]\[dq]Press [Z] to toggle -cw\[dq],\[aq]z\[aq],\[aq]Z\[aq]\f[R] -.SS status-show-filtered +.SS hint +.PP +\f[B]hint\f[R]=\f[I]\[lq]Press [Z] to toggle cw\[rq]\f[R] +.SS keys +.PP +\f[B]keys\f[R]=\f[I][\[lq]z\[rq],\[lq]Z\[rq]]\f[R] +.SH INPUT.STATUS-SHOW-FILTERED +.PP +This section is [input.status-show-filtered] in your configuration file .PP Show the content of a filtered toot -.PD 0 -.P -.PD -\f[B]status-show-filtered\f[R]=\f[I]\[dq]Press [Z] to view filtered -toot\[dq],\[aq]z\[aq],\[aq]Z\[aq]\f[R] -.SS user-avatar +.SS hint +.PP +\f[B]hint\f[R]=\f[I]\[lq]Press [Z] to view filtered toot\[rq]\f[R] +.SS keys +.PP +\f[B]keys\f[R]=\f[I][\[lq]z\[rq],\[lq]Z\[rq]]\f[R] +.SH INPUT.USER-AVATAR +.PP +This section is [input.user-avatar] in your configuration file .PP View avatar -.PD 0 -.P -.PD -\f[B]user-avatar\f[R]=\f[I]\[dq][A]vatar\[dq],\[aq]a\[aq],\[aq]A\[aq]\f[R] -.SS user-block +.SS hint +.PP +\f[B]hint\f[R]=\f[I]\[lq][A]vatar\[rq]\f[R] +.SS keys +.PP +\f[B]keys\f[R]=\f[I][\[lq]a\[rq],\[lq]A\[rq]]\f[R] +.SH INPUT.USER-BLOCK +.PP +This section is [input.user-block] in your configuration file .PP Block the user -.PD 0 -.P -.PD -\f[B]user-block\f[R]=\f[I]\[dq][B]lock\[dq],\[dq]Un[B]lock\[dq],\[aq]b\[aq],\[aq]B\[aq]\f[R] -.SS user-follow +.SS hint +.PP +\f[B]hint\f[R]=\f[I]\[lq][B]lock\[rq]\f[R] +.SS hint-alt +.PP +\f[B]hint-alt\f[R]=\f[I]\[lq]Un[B]lock\[rq]\f[R] +.SS keys +.PP +\f[B]keys\f[R]=\f[I][\[lq]b\[rq],\[lq]B\[rq]]\f[R] +.SH INPUT.USER-FOLLOW +.PP +This section is [input.user-follow] in your configuration file .PP Follow user -.PD 0 -.P -.PD -\f[B]user-follow\f[R]=\f[I]\[dq][F]ollow\[dq],\[dq]Un[F]ollow\[dq],\[aq]f\[aq],\[aq]F\[aq]\f[R] -.SS user-follow-request-decide +.SS hint +.PP +\f[B]hint\f[R]=\f[I]\[lq][F]ollow\[rq]\f[R] +.SS hint-alt +.PP +\f[B]hint-alt\f[R]=\f[I]\[lq]Un[F]ollow\[rq]\f[R] +.SS keys +.PP +\f[B]keys\f[R]=\f[I][\[lq]f\[rq],\[lq]F\[rq]]\f[R] +.SH INPUT.USER-FOLLOW-REQUEST-DECIDE +.PP +This section is [input.user-follow-request-decide] in your configuration +file .PP Follow user -.PD 0 -.P -.PD -\f[B]user-follow-request-decide\f[R]=\f[I]\[dq]Follow -[R]equest\[dq],\[dq]Follow [R]equest\[dq],\[aq]r\[aq],\[aq]R\[aq]\f[R] -.SS user-mute +.SS hint +.PP +\f[B]hint\f[R]=\f[I]\[lq]Follow [R]equest\[rq]\f[R] +.SS hint-alt +.PP +\f[B]hint-alt\f[R]=\f[I]\[lq]Follow [R]equest\[rq]\f[R] +.SS keys +.PP +\f[B]keys\f[R]=\f[I][\[lq]r\[rq],\[lq]R\[rq]]\f[R] +.SH INPUT.USER-MUTE +.PP +This section is [input.user-mute] in your configuration file .PP Mute user -.PD 0 -.P -.PD -\f[B]user-mute\f[R]=\f[I]\[dq][M]ute\[dq],\[dq]Un[M]ute\[dq],\[aq]m\[aq],\[aq]M\[aq]\f[R] -.SS user-links +.SS hint +.PP +\f[B]hint\f[R]=\f[I]\[lq][M]ute\[rq]\f[R] +.SS hint-alt +.PP +\f[B]hint-alt\f[R]=\f[I]\[lq]Un[M]ute\[rq]\f[R] +.SS keys +.PP +\f[B]keys\f[R]=\f[I][\[lq]m\[rq],\[lq]M\[rq]]\f[R] +.SH INPUT.USER-LINKS +.PP +This section is [input.user-links] in your configuration file .PP Open links -.PD 0 -.P -.PD -\f[B]user-links\f[R]=\f[I]\[dq][O]pen\[dq],\[aq]o\[aq],\[aq]O\[aq]\f[R] -.SS user-user +.SS hint +.PP +\f[B]hint\f[R]=\f[I]\[lq][O]pen\[rq]\f[R] +.SS keys +.PP +\f[B]keys\f[R]=\f[I][\[lq]o\[rq],\[lq]O\[rq]]\f[R] +.SH INPUT.USER-USER +.PP +This section is [input.user-user] in your configuration file .PP View user profile -.PD 0 -.P -.PD -\f[B]user-user\f[R]=\f[I]\[dq][U]ser\[dq],\[aq]u\[aq],\[aq]U\[aq]\f[R] -.SS user-view-focus +.SS hint +.PP +\f[B]hint\f[R]=\f[I]\[lq][U]ser\[rq]\f[R] +.SS keys +.PP +\f[B]keys\f[R]=\f[I][\[lq]u\[rq],\[lq]U\[rq]]\f[R] +.SH INPUT.USER-VIEW-FOCUS +.PP +This section is [input.user-view-focus] in your configuration file .PP Open view mode -.PD 0 -.P -.PD -\f[B]user-view-focus\f[R]=\f[I]\[dq][V]iew\[dq],\[aq]v\[aq],\[aq]V\[aq]\f[R] -.SS user-yank +.SS hint +.PP +\f[B]hint\f[R]=\f[I]\[lq][V]iew\[rq]\f[R] +.SS keys +.PP +\f[B]keys\f[R]=\f[I][\[lq]v\[rq],\[lq]V\[rq]]\f[R] +.SH INPUT.USER-YANK +.PP +This section is [input.user-yank] in your configuration file .PP Yank the user URL -.PD 0 -.P -.PD -\f[B]user-yank\f[R]=\f[I]\[dq][Y]ank\[dq],\[aq]y\[aq],\[aq]Y\[aq]\f[R] -.SS list-open-feed +.SS hint +.PP +\f[B]hint\f[R]=\f[I]\[lq][Y]ank\[rq]\f[R] +.SS keys +.PP +\f[B]keys\f[R]=\f[I][\[lq]y\[rq],\[lq]Y\[rq]]\f[R] +.SH INPUT.LIST-OPEN-FEED +.PP +This section is [input.list-open-feed] in your configuration file .PP Open list -.PD 0 -.P -.PD -\f[B]list-open-feed\f[R]=\f[I]\[dq][O]pen\[dq],\[aq]o\[aq],\[aq]O\[aq]\f[R] -.SS list-user-list +.SS hint +.PP +\f[B]hint\f[R]=\f[I]\[lq][O]pen\[rq]\f[R] +.SS keys +.PP +\f[B]keys\f[R]=\f[I][\[lq]o\[rq],\[lq]O\[rq]]\f[R] +.SH INPUT.LIST-USER-LIST +.PP +This section is [input.list-user-list] in your configuration file .PP List all users in a list -.PD 0 -.P -.PD -\f[B]list-user-list\f[R]=\f[I]\[dq][U]sers\[dq],\[aq]u\[aq],\[aq]U\[aq]\f[R] -.SS list-user-add +.SS hint +.PP +\f[B]hint\f[R]=\f[I]\[lq][U]sers\[rq]\f[R] +.SS keys +.PP +\f[B]keys\f[R]=\f[I][\[lq]u\[rq],\[lq]U\[rq]]\f[R] +.SH INPUT.LIST-USER-ADD +.PP +This section is [input.list-user-add] in your configuration file .PP Add user to list -.PD 0 -.P -.PD -\f[B]list-user-add\f[R]=\f[I]\[dq][A]dd\[dq],\[aq]a\[aq],\[aq]A\[aq]\f[R] -.SS list-user-delete +.SS hint +.PP +\f[B]hint\f[R]=\f[I]\[lq][A]dd\[rq]\f[R] +.SS keys +.PP +\f[B]keys\f[R]=\f[I][\[lq]a\[rq],\[lq]A\[rq]]\f[R] +.SH INPUT.LIST-USER-DELETE +.PP +This section is [input.list-user-delete] in your configuration file .PP Delete user from list -.PD 0 -.P -.PD -\f[B]list-user-delete\f[R]=\f[I]\[dq][D]elete\[dq],\[aq]d\[aq],\[aq]D\[aq]\f[R] -.SS link-open +.SS hint +.PP +\f[B]hint\f[R]=\f[I]\[lq][D]elete\[rq]\f[R] +.SS keys +.PP +\f[B]keys\f[R]=\f[I][\[lq]d\[rq],\[lq]D\[rq]]\f[R] +.SH INPUT.LINK-OPEN +.PP +This section is [input.link-open] in your configuration file .PP Open URL -.PD 0 -.P -.PD -\f[B]link-open\f[R]=\f[I]\[dq][O]pen\[dq],\[aq]o\[aq],\[aq]O\[aq]\f[R] -.SS link-yank +.SS hint +.PP +\f[B]hint\f[R]=\f[I]\[lq][O]pen\[rq]\f[R] +.SS keys +.PP +\f[B]keys\f[R]=\f[I][\[lq]o\[rq],\[lq]O\[rq]]\f[R] +.SH INPUT.LINK-YANK +.PP +This section is [input.link-yank] in your configuration file .PP Yank the URL -.PD 0 -.P -.PD -\f[B]link-yank\f[R]=\f[I]\[dq][Y]ank\[dq],\[aq]y\[aq],\[aq]Y\[aq]\f[R] -.SS tag-open-feed +.SS hint +.PP +\f[B]hint\f[R]=\f[I]\[lq][Y]ank\[rq]\f[R] +.SS keys +.PP +\f[B]keys\f[R]=\f[I][\[lq]y\[rq],\[lq]Y\[rq]]\f[R] +.SH INPUT.TAG-OPEN-FEED +.PP +This section is [input.tag-open-feed] in your configuration file .PP Open tag feed -.PD 0 -.P -.PD -\f[B]tag-open-feed\f[R]=\f[I]\[dq][O]pen\[dq],\[aq]o\[aq],\[aq]O\[aq]\f[R] -.SS tag-follow +.SS hint +.PP +\f[B]hint\f[R]=\f[I]\[lq][O]pen\[rq]\f[R] +.SS keys +.PP +\f[B]keys\f[R]=\f[I][\[lq]o\[rq],\[lq]O\[rq]]\f[R] +.SH INPUT.TAG-FOLLOW +.PP +This section is [input.tag-follow] in your configuration file .PP Toggle follow on tag -.PD 0 -.P -.PD -\f[B]tag-follow\f[R]=\f[I]\[dq][F]ollow\[dq],\[dq]Un[F]ollow\[dq],\[aq]f\[aq],\[aq]F\[aq]\f[R] -.SS compose-edit-cw +.SS hint +.PP +\f[B]hint\f[R]=\f[I]\[lq][F]ollow\[rq]\f[R] +.SS hint-alt +.PP +\f[B]hint-alt\f[R]=\f[I]\[lq]Un[F]ollow\[rq]\f[R] +.SS keys +.PP +\f[B]keys\f[R]=\f[I][\[lq]f\[rq],\[lq]F\[rq]]\f[R] +.SH INPUT.COMPOSE-EDIT-CW +.PP +This section is [input.compose-edit-cw] in your configuration file .PP Edit content warning text on new toot -.PD 0 -.P -.PD -\f[B]compose-edit-cw\f[R]=\f[I]\[dq][C]W -text\[dq],\[aq]c\[aq],\[aq]C\[aq]\f[R] -.SS compose-edit-text +.SS hint +.PP +\f[B]hint\f[R]=\f[I]\[lq][C]W text\[rq]\f[R] +.SS keys +.PP +\f[B]keys\f[R]=\f[I][\[lq]c\[rq],\[lq]C\[rq]]\f[R] +.SH INPUT.COMPOSE-EDIT-TEXT +.PP +This section is [input.compose-edit-text] in your configuration file .PP Edit the text on new toot -.PD 0 -.P -.PD -\f[B]compose-edit-text\f[R]=\f[I]\[dq][E]dit -text\[dq],\[aq]e\[aq],\[aq]E\[aq]\f[R] -.SS compose-include-quote +.SS hint +.PP +\f[B]hint\f[R]=\f[I]\[lq][E]dit text\[rq]\f[R] +.SS keys +.PP +\f[B]keys\f[R]=\f[I][\[lq]e\[rq],\[lq]E\[rq]]\f[R] +.SH INPUT.COMPOSE-INCLUDE-QUOTE +.PP +This section is [input.compose-include-quote] in your configuration file .PP Include a quote when replying -.PD 0 -.P -.PD -\f[B]compose-include-quote\f[R]=\f[I]\[dq][I]nclude -quote\[dq],\[aq]i\[aq],\[aq]I\[aq]\f[R] -.SS compose-media-focus +.SS hint +.PP +\f[B]hint\f[R]=\f[I]\[lq][I]nclude quote\[rq]\f[R] +.SS keys +.PP +\f[B]keys\f[R]=\f[I][\[lq]i\[rq],\[lq]I\[rq]]\f[R] +.SH INPUT.COMPOSE-MEDIA-FOCUS +.PP +This section is [input.compose-media-focus] in your configuration file .PP Focus on adding media to toot -.PD 0 -.P -.PD -\f[B]compose-media-focus\f[R]=\f[I]\[dq][M]edia\[dq],\[aq]m\[aq],\[aq]M\[aq]\f[R] -.SS compose-post +.SS hint +.PP +\f[B]hint\f[R]=\f[I]\[lq][M]edia\[rq]\f[R] +.SS keys +.PP +\f[B]keys\f[R]=\f[I][\[lq]m\[rq],\[lq]M\[rq]]\f[R] +.SH INPUT.COMPOSE-POST +.PP +This section is [input.compose-post] in your configuration file .PP Post the new toot -.PD 0 -.P -.PD -\f[B]compose-post\f[R]=\f[I]\[dq][P]ost\[dq],\[aq]p\[aq],\[aq]P\[aq]\f[R] -.SS compose-toggle-content-warning +.SS hint +.PP +\f[B]hint\f[R]=\f[I]\[lq][P]ost\[rq]\f[R] +.SS keys +.PP +\f[B]keys\f[R]=\f[I][\[lq]p\[rq],\[lq]P\[rq]]\f[R] +.SH INPUT.COMPOSE-TOGGLE-CONTENT-WARNING +.PP +This section is [input.compose-toggle-content-warning] in your +configuration file .PP Toggle content warning on toot -.PD 0 -.P -.PD -\f[B]compose-toggle-content-warning\f[R]=\f[I]\[dq][T]oggle -CW\[dq],\[aq]t\[aq],\[aq]T\[aq]\f[R] -.SS compose-visibility +.SS hint +.PP +\f[B]hint\f[R]=\f[I]\[lq][T]oggle CW\[rq]\f[R] +.SS keys +.PP +\f[B]keys\f[R]=\f[I][\[lq]t\[rq],\[lq]T\[rq]]\f[R] +.SH INPUT.COMPOSE-VISIBILITY +.PP +This section is [input.compose-visibility] in your configuration file .PP Edit the visibility on new toot -.PD 0 -.P -.PD -\f[B]compose-visibility\f[R]=\f[I]\[dq][V]isibility\[dq],\[aq]v\[aq],\[aq]V\[aq]\f[R] -.SS compose-language +.SS hint +.PP +\f[B]hint\f[R]=\f[I]\[lq][V]isibility\[rq]\f[R] +.SS keys +.PP +\f[B]keys\f[R]=\f[I][\[lq]v\[rq],\[lq]V\[rq]]\f[R] +.SH INPUT.COMPOSE-LANGUAGE +.PP +This section is [input.compose-language] in your configuration file .PP Edit the language of a toot -.PD 0 -.P -.PD -\f[B]compose-language\f[R]=\f[I]\[dq][L]ang\[dq],\[aq]l\[aq],\[aq]L\[aq]\f[R] -.SS compose-poll +.SS hint +.PP +\f[B]hint\f[R]=\f[I]\[lq][L]ang\[rq]\f[R] +.SS keys +.PP +\f[B]keys\f[R]=\f[I][\[lq]l\[rq],\[lq]L\[rq]]\f[R] +.SH INPUT.COMPOSE-POLL +.PP +This section is [input.compose-poll] in your configuration file .PP Switch to creating a poll -.PD 0 -.P -.PD -\f[B]compose-poll\f[R]=\f[I]\[dq]P[O]ll\[dq],\[aq]o\[aq],\[aq]O\[aq]\f[R] -.SS media-delete +.SS hint +.PP +\f[B]hint\f[R]=\f[I]\[lq]P[O]ll\[rq]\f[R] +.SS keys +.PP +\f[B]keys\f[R]=\f[I][\[lq]o\[rq],\[lq]O\[rq]]\f[R] +.SH INPUT.MEDIA-DELETE +.PP +This section is [input.media-delete] in your configuration file .PP Delete media file -.PD 0 -.P -.PD -\f[B]media-delete\f[R]=\f[I]\[dq][D]elete\[dq],\[aq]d\[aq],\[aq]D\[aq]\f[R] -.SS media-edit-desc +.SS hint +.PP +\f[B]hint\f[R]=\f[I]\[lq][D]elete\[rq]\f[R] +.SS keys +.PP +\f[B]keys\f[R]=\f[I][\[lq]d\[rq],\[lq]D\[rq]]\f[R] +.SH INPUT.MEDIA-EDIT-DESC +.PP +This section is [input.media-edit-desc] in your configuration file .PP Edit the description on media file -.PD 0 -.P -.PD -\f[B]media-edit-desc\f[R]=\f[I]\[dq][E]dit -desc\[dq],\[aq]e\[aq],\[aq]E\[aq]\f[R] -.SS media-add +.SS hint +.PP +\f[B]hint\f[R]=\f[I]\[lq][E]dit desc\[rq]\f[R] +.SS keys +.PP +\f[B]keys\f[R]=\f[I][\[lq]e\[rq],\[lq]E\[rq]]\f[R] +.SH INPUT.MEDIA-ADD +.PP +This section is [input.media-add] in your configuration file .PP Add a new media file -.PD 0 -.P -.PD -\f[B]media-add\f[R]=\f[I]\[dq][A]dd\[dq],\[aq]a\[aq],\[aq]A\[aq]\f[R] -.SS vote-vote +.SS hint +.PP +\f[B]hint\f[R]=\f[I]\[lq][A]dd\[rq]\f[R] +.SS keys +.PP +\f[B]keys\f[R]=\f[I][\[lq]a\[rq],\[lq]A\[rq]]\f[R] +.SH INPUT.VOTE-VOTE +.PP +This section is [input.vote-vote] in your configuration file .PP Vote on poll -.PD 0 -.P -.PD -\f[B]vote-vote\f[R]=\f[I]\[dq][V]ote\[dq],\[aq]v\[aq],\[aq]V\[aq]\f[R] -.SS vote-select +.SS hint +.PP +\f[B]hint\f[R]=\f[I]\[lq][V]ote\[rq]\f[R] +.SS keys +.PP +\f[B]keys\f[R]=\f[I][\[lq]v\[rq],\[lq]V\[rq]]\f[R] +.SH INPUT.VOTE-SELECT +.PP +This section is [input.vote-select] in your configuration file .PP Select item to vote on -.PD 0 -.P -.PD -\f[B]vote-select\f[R]=\f[I]\[dq][Enter] to select\[dq],\[aq] \[aq], -\[dq]Enter\[dq]\f[R] -.SS poll-add +.SS hint +.PP +\f[B]hint\f[R]=\f[I]\[lq][Enter] to select\[rq]\f[R] +.SS special-keys +.PP +\f[B]special-keys\f[R]=\f[I][\[lq]Enter\[rq]]\f[R] +.SH INPUT.POLL-ADD +.PP +This section is [input.poll-add] in your configuration file .PP Add a new poll option -.PD 0 -.P -.PD -\f[B]poll-add\f[R]=\f[I]\[dq][A]dd\[dq],\[aq]a\[aq],\[aq]A\[aq]\f[R] -.SS poll-edit +.SS hint +.PP +\f[B]hint\f[R]=\f[I]\[lq][A]dd\[rq]\f[R] +.SS keys +.PP +\f[B]keys\f[R]=\f[I][\[lq]a\[rq],\[lq]A\[rq]]\f[R] +.SH INPUT.POLL-EDIT +.PP +This section is [input.poll-edit] in your configuration file .PP Edit a poll option -.PD 0 -.P -.PD -\f[B]poll-edit\f[R]=\f[I]\[dq][E]dit\[dq],\[aq]e\[aq],\[aq]E\[aq]\f[R] -.SS poll-delete +.SS hint +.PP +\f[B]hint\f[R]=\f[I]\[lq][E]dit\[rq]\f[R] +.SS keys +.PP +\f[B]keys\f[R]=\f[I][\[lq]e\[rq],\[lq]E\[rq]]\f[R] +.SH INPUT.POLL-DELETE +.PP +This section is [input.poll-delete] in your configuration file .PP Delete a poll option -.PD 0 -.P -.PD -\f[B]poll-delete\f[R]=\f[I]\[dq][D]elete\[dq],\[aq]d\[aq],\[aq]D\[aq]\f[R] -.SS poll-multi-toggle +.SS hint +.PP +\f[B]hint\f[R]=\f[I]\[lq][D]elete\[rq]\f[R] +.SS keys +.PP +\f[B]keys\f[R]=\f[I][\[lq]d\[rq],\[lq]D\[rq]]\f[R] +.SH INPUT.POLL-MULTI-TOGGLE +.PP +This section is [input.poll-multi-toggle] in your configuration file .PP Toggle voting on multiple options -.PD 0 -.P -.PD -\f[B]poll-multi-toggle\f[R]=\f[I]\[dq]Toggle -[M]ultiple\[dq],\[aq]m\[aq],\[aq]M\[aq]\f[R] -.SS poll-expiration +.SS hint +.PP +\f[B]hint\f[R]=\f[I]\[lq]Toggle [M]ultiple\[rq]\f[R] +.SS keys +.PP +\f[B]keys\f[R]=\f[I][\[lq]m\[rq],\[lq]M\[rq]]\f[R] +.SH INPUT.POLL-EXPIRATION +.PP +This section is [input.poll-expiration] in your configuration file .PP Change the expiration of poll -.PD 0 -.P -.PD -\f[B]poll-expiration\f[R]=\f[I]\[dq]E[X]pires\[dq],\[aq]x\[aq],\[aq]X\[aq]\f[R] -.SS preference-name +.SS hint +.PP +\f[B]hint\f[R]=\f[I]\[lq]E[X]pires\[rq]\f[R] +.SS keys +.PP +\f[B]keys\f[R]=\f[I][\[lq]x\[rq],\[lq]X\[rq]]\f[R] +.SH INPUT.PREFERENCE-NAME +.PP +This section is [input.preference-name] in your configuration file .PP Change display name -.PD 0 -.P -.PD -\f[B]preference-name\f[R]=\f[I]\[dq][N]ame\[dq],\[aq]n\[aq],\[aq]N\[aq]\f[R] -.SS preference-visibility +.SS hint +.PP +\f[B]hint\f[R]=\f[I]\[lq][N]ame\[rq]\f[R] +.SS keys +.PP +\f[B]keys\f[R]=\f[I][\[lq]n\[rq],\[lq]N\[rq]]\f[R] +.SH INPUT.PREFERENCE-VISIBILITY +.PP +This section is [input.preference-visibility] in your configuration file .PP Change default visibility of toots -.PD 0 -.P -.PD -\f[B]preference-visibility\f[R]=\f[I]\[dq][V]isibility\[dq],\[aq]v\[aq],\[aq]V\[aq]\f[R] -.SS preference-bio +.SS hint +.PP +\f[B]hint\f[R]=\f[I]\[lq][V]isibility\[rq]\f[R] +.SS keys +.PP +\f[B]keys\f[R]=\f[I][\[lq]v\[rq],\[lq]V\[rq]]\f[R] +.SH INPUT.PREFERENCE-BIO +.PP +This section is [input.preference-bio] in your configuration file .PP Change bio in profile -.PD 0 -.P -.PD -\f[B]preference-bio\f[R]=\f[I]\[dq][B]io\[dq],\[aq]b\[aq],\[aq]B\[aq]\f[R] -.SS preference-save +.SS hint +.PP +\f[B]hint\f[R]=\f[I]\[lq][B]io\[rq]\f[R] +.SS keys +.PP +\f[B]keys\f[R]=\f[I][\[lq]b\[rq],\[lq]B\[rq]]\f[R] +.SH INPUT.PREFERENCE-SAVE +.PP +This section is [input.preference-save] in your configuration file .PP Save your preferences -.PD 0 -.P -.PD -\f[B]preference-save\f[R]=\f[I]\[dq][S]ave\[dq],\[aq]s\[aq],\[aq]S\[aq]\f[R] -.SS preference-fields +.SS hint +.PP +\f[B]hint\f[R]=\f[I]\[lq][S]ave\[rq]\f[R] +.SS keys +.PP +\f[B]keys\f[R]=\f[I][\[lq]s\[rq],\[lq]S\[rq]]\f[R] +.SH INPUT.PREFERENCE-FIELDS +.PP +This section is [input.preference-fields] in your configuration file .PP Edit profile fields -.PD 0 -.P -.PD -\f[B]preference-fields\f[R]=\f[I]\[dq][F]ields\[dq],\[aq]f\[aq],\[aq]F\[aq]\f[R] -.SS preference-fields-add +.SS hint +.PP +\f[B]hint\f[R]=\f[I]\[lq][F]ields\[rq]\f[R] +.SS keys +.PP +\f[B]keys\f[R]=\f[I][\[lq]f\[rq],\[lq]F\[rq]]\f[R] +.SH INPUT.PREFERENCE-FIELDS-ADD +.PP +This section is [input.preference-fields-add] in your configuration file .PP Add new field -.PD 0 -.P -.PD -\f[B]preference-fields-add\f[R]=\f[I]\[dq][A]dd\[dq],\[aq]a\[aq],\[aq]A\[aq]\f[R] -.SS preference-fields-edit +.SS hint +.PP +\f[B]hint\f[R]=\f[I]\[lq][A]dd\[rq]\f[R] +.SS keys +.PP +\f[B]keys\f[R]=\f[I][\[lq]a\[rq],\[lq]A\[rq]]\f[R] +.SH INPUT.PREFERENCE-FIELDS-EDIT +.PP +This section is [input.preference-fields-edit] in your configuration +file .PP Edit current field -.PD 0 -.P -.PD -\f[B]preference-fields-edit\f[R]=\f[I]\[dq][E]dit\[dq],\[aq]e\[aq],\[aq]E\[aq]\f[R] -.SS preference-fields-delete +.SS hint +.PP +\f[B]hint\f[R]=\f[I]\[lq][E]dit\[rq]\f[R] +.SS keys +.PP +\f[B]keys\f[R]=\f[I][\[lq]e\[rq],\[lq]E\[rq]]\f[R] +.SH INPUT.PREFERENCE-FIELDS-DELETE +.PP +This section is [input.preference-fields-delete] in your configuration +file .PP Delete current field -.PD 0 -.P -.PD -\f[B]preference-fields-delete\f[R]=\f[I]\[dq][D]elete\[dq],\[aq]d\[aq],\[aq]D\[aq]\f[R] +.SS hint +.PP +\f[B]hint\f[R]=\f[I]\[lq][D]elete\[rq]\f[R] +.SS keys +.PP +\f[B]keys\f[R]=\f[I][\[lq]d\[rq],\[lq]D\[rq]]\f[R] +.SH INPUT.EDITOR-EXIT +.PP +This section is [input.editor-exit] in your configuration file +.PP +Exit the editor +.SS hint +.PP +\f[B]hint\f[R]=\f[I]\[lq][Esc] when done\[rq]\f[R] +.SS special-keys +.PP +\f[B]special-keys\f[R]=\f[I][\[lq]Esc\[rq]]\f[R] .SH SEE ALSO .IP .nf diff --git a/docs/man/tut.5.md b/docs/man/tut.5.md index 0cfff58..2fc7fa5 100644 --- a/docs/man/tut.5.md +++ b/docs/man/tut.5.md @@ -1,6 +1,6 @@ -% tut(5) tut 1.0.34 +% tut(5) tut 2.0.0 % Rasmus Lindroth -% 2023-01-01 +% 2023-01-23 # NAME tut - configuration for tut(1) @@ -8,7 +8,7 @@ tut - configuration for tut(1) # DESCRIPTION The configuration format for tut. -You find it in *$XDG_CONFIG_HOME/tut/config.ini* on Linux which usually equals to *~/.config/tut/config.ini*. +You find it in *$XDG_CONFIG_HOME/tut/config.toml* on Linux which usually equals to *~/.config/tut/config.toml*. If you don't run Linux it will use the path of the Go funcdtion os.UserConfigDir(). But if you move the tut folder to *XDG_CONFIG_HOME/tut/* and have set the environment variable *XDG_CONFIG_HOME* it will look there instead of the standard place. @@ -21,58 +21,25 @@ Under each section there is the name of the configuration option. The last line # GENERAL This section is \[general\] in your configuration file +## editor +What editor to use. TUT_USE_INTERNAL will use the editor that comes with tut. If you want you can set this to $EDITOR to use your environment variable or vim if you want to specify the program directly. +**editor**=*"TUT_USE_INTERNAL"* + ## confirmation -Shows a confirmation view before actions such as favorite, delete toot, boost etc. +You need to press yes in a confirmation dialog before favoriting, boosting, etc. **confirmation**=*true* ## mouse-support -Enable support for using the mouse in tut to select items. +Enable mouse support in tut. **mouse-support**=*false* -## timelines -Timelines adds windows of feeds. You can customize the number of feeds, what they should show and the key to activate them. - -Available timelines: home, direct, local, federated, special, bookmarks, saved, favorited, notifications, lists, mentions, tag - -The one named special are the home timeline with only boosts and/or replies. - -Tag is special as you need to add the tag after, see the example below. - -The syntax is: -timelines=feed,[name],[keys...],[showBoosts],[showReplies] - -Tha values in brackets are optional. You can see the syntax for keys under the [input] section. - -showBoosts and showReplies must be formated as bools. So either true or false. They always defaults to true. - -Some examples: - -home timeline with the name Home -timelines=home,Home - -local timeline with the name Local and it gets focus when you press 2. It will also hide boosts in the timeline, but show toots that are replies. -timelines=local,Local,\'2\',false,true - -notification timeline with the name [N]otifications and it gets focus when you press n or N -timelines=notifications,[N]otifications,\'n\',\'N\' - -tag timeline for \#linux with the name Linux and it gets focus when you press -timelines=tag linux,Linux,\"F2\" - - -If you don\'t set any timelines it will default to this: -timelines=home -timelines=notifications,[N]otifications,\'n\',\'N\' - - - ## date-format -The date format to be used. See https://godoc.org/time\#Time.Format -**date-format**=*2006-01-02 15:04* +The date format to be used. See https://pkg.go.dev/time\#pkg-constants +**date-format**=*"2006-01-02 15:04"* -## date-today-format +## date-tody-format Format for dates the same day. See date-format for more info. -**date-today-format**=*15:04* +**date-tody-format**=*"15:04"* ## date-relative This displays relative dates instead for statuses that are one day or older the output is 1y2m1d (1 year 2 months and 1 day) @@ -81,47 +48,53 @@ The value is an integear -1 = don\'t use relative dates 0 = always use relative dates, except for dates \< 1 day 1 - ∞ = number of days to use relative dates - -Example: date-relative=28 will display a relative date for toots that are between 1-28 days old. Otherwhise it will use the short or long format. + +Value: 28 will display a relative date for toots that are between 1-28 days old. Otherwhise it will use the short or long format. **date-relative**=*-1* ## max-width -The max width of text before it wraps when displaying toots. -0 = no restriction. +The max with of text before it wraps when displaying a toot. **max-width**=*0* ## list-placement -Where do you want the list of toots to be placed? -Valid values: left, right, top, bottom. -**list-placement**=*left* +The placement of your panes. + +valid: left, right, top, bottom + +**list-placement**=*"left"* ## list-split -If you have notification-feed set to true you can display it under the main list of toots (row) or place it to the right of the main list of toots (column). -**list-split**=*row* +How should panes be split? + +valid: row, column + +**list-split**=*"row"* ## list-proportion -You can change the proportions of the list view in relation to the content view list-proportion=1 and content-proportoin=3 will result in the content taking up 3 times more space. -Must be n \> 0 +The proportion of panes vs. content. 1 on this and 3 on content below results in content taking up 3 times more space. **list-proportion**=*1* ## content-proportion -See list-proportion +See previous. **content-proportion**=*2* ## notifications-to-hide -Hide notifications of this type. If you have multiple you separate them with a comma. Valid types: mention, status, boost, follow, follow_request, favorite, poll, edit. -**notifications-to-hide**= +Hide notifications of this type in your notification timelines. + +valid: mention, status, boost, follow, follow_request, favorite, poll, edit + +**notifications-to-hide**=*[]* ## quote-reply -If you always want to quote original message when replying. +Always include a quote of the message you\'re replying to. **quote-reply**=*false* ## show-icons -If you want to show icons in the list of toots. +If you want to show icons in timelines. **show-icons**=*true* ## short-hints -If you\'ve learnt all the shortcut keys you can remove the help text and only show the key in tui. So it gets less cluttered. +If you only want to you the letter of keys instead of the full hint. **short-hints**=*false* ## show-filter-phrase @@ -129,654 +102,1315 @@ If you want to display the filter that filtered a toot. **show-filter-phrase**=*true* ## show-help -If you want to show a message in the cmdbar on how to access the help text. +Display a message in the commandbar on how to access the help text. **show-help**=*true* ## stick-to-top -If you always want tut to jump to the newest post. May ruin your reading experience. +Always jump to the newest post. May ruin your reading experience. **stick-to-top**=*false* ## show-boosted-user -If you want to display the username of the person being boosted instead of the person that boosted. +Display the username of the person being boosted insted of the person that boosted. **show-boosted-user**=*false* +## commands-in-new-pane +Open a new pane when you run a command like :timeline home. +**commands-in-new-pane**=*true* + +## dynamic-timeline-name +Set a default name for the timeline if the name is empty. So if you run :tag linux the title of the pane will be set to \#linux +**dynamic-timeline-name**=*true* + ## terminal-title 0 = No terminal title 1 = Show title in terminal and top bar -2 = Only show terminal title, and no top bar in tut. +2 = Only show terminal title, and no top bar in tut +3 = No terminal title and no top bar in tut. + +valid: 0, 1, 2, 4 + **terminal-title**=*0* ## redraw-ui -If you don\'t want the whole UI to update, and only the text content you can set this option to true. This will lead to some artifacts being left on the screen when emojis are present. But it will keep the UI from flashing on every single toot in some terminals. +If you don\'t want the whole UI to update, and only update the text content you can disable this. This will lead to some artifacts being left on the screen when emojis are present. **redraw-ui**=*true* ## leader-key -The leader is used as a shortcut to run commands as you can do in Vim. By default this is disabled and you enable it by setting a leader-key. It can only consist of one char and I like to use comma as leader key. So to set it you write leader-key=, -**leader-key**= +The leader is used as a shortcut to run commands as you can do in Vim. By default this is disabled and you enable it by setting a key here. It can only consist of one char, so set it to something like a comma. +**leader-key**=*""* ## leader-timeout Number of milliseconds before the leader command resets. So if you tap the leader-key by mistake or are to slow it empties all the input after X milliseconds. **leader-timeout**=*1000* -## leader-action -You set actions for the leader-key with one or more leader-action. It consists of two parts first the action then the shortcut. And they\'re separated by a comma. - -Available commands: blocking, bookmarks, boosts, clear-notifications, close-window, compose, direct, edit, favorited, favorites, federated, followers, following, history, home, list-placement, list-split, lists, local, mentions, move-window-left, move-window-right, move-window-up, move-window-down, move-window-home, move-window-end, muting, newer, notifications, preferences, profile, proportions, refetch, saved, special-all, special-boosts, special-replies, stick-to-top, switch, tag, tags, window +# GENERAL.TIMELINES +This section is \[\[general.timelines\]\] in your configuration file. You can have multiple of them. + +Timelines adds panes of feeds. You can customize the number of feeds, what they should show and the key to activate them. + +Example: + +\[\[general.timelines\]\] +name=\"home\" +type=\"home\" +hide-boosts=false +hide-replies=false -The ones named special-\* are the home timeline with only boosts and/or replies. All contains both, -boosts only boosts and -replies only replies. +\[\[general.timelines\]\] +name=\"Notifications\" +type=\"notifications\" +keys=\[\"n\", \"N\"\] +closed=true +on-creation-closed=\"new-pane\" +on-focus=\"focus-self\" + +## name +The name to display above the timeline +**name**=*""* + +## type +The type of the timeline + +valid: home, direct, local, federated, bookmarks, saved, favorited, notifications, lists, mentions, tag + +**type**=*""* + +## data +Used for the tag type, so here you set the tag. If you have multiple you seperate them with a space. +**data**=*""* + +## keys +A list of keys to give this timeline focus. See under the input section to learn more about keys. +**keys**=*[]* + +## special-keys +A list of special-keys to give this timeline focus. See under the input section to learn more about special-keys. +**special-keys**=*[]* + +## shortcut +A shortcut to give this timeline focus with your leader-key + this shortcut. +**shortcut**=*""* + +## hide-boosts +Hide boosts in this timeline. +**hide-boosts**=*"false"* + +## hide-replies +Hide replies in this timeline. +**hide-replies**=*"false"* + +## closed +Don\'t open this timeline when you start tut. Use your keys or shortcut to open it. +**closed**=*"false"* + +## on-creation-closed +Don\'t open this timeline when you start tut. Use your keys or shortcut to open it. + +valid: new-pane, current-pane + +**on-creation-closed**=*"new-pane"* + +## on-focus +Don\'t open this timeline when you start tut. Use your keys or shortcut to open it. + +valid: focus-pane, focus-self + +**on-focus**=*"focus-pane"* + +# GENERAL.LEADER-ACTIONS +This section is \[\[general.leader-actions\]\] in your configuration file. You can have multiple of them. + +You set actions leader-key with one or more leader-actions. The shortcuts are up to you, but keep them quite short and make sure they don\'t collide. If you have one shortcut that is \"f\" and an other one that is \"fav\", the one with \"f\" will always run and \"fav\" will never run. -Some special leaders: -tag is special as you need to add the tag after, e.g. tag linux -window is special as it\'s a shortcut for switching between the timelines you\'ve set under general and they are zero indexed. window 0 = your first timeline, window 1 = your second and so on. +Some special actions that requires data to be set: +pane is special as it\'s a shortcut for switching between the panes you\'ve set under general and they are zero indexed. pane 0 = your first timeline, pane 1 = your second and so on. list-placement as it takes the argument top, right, bottom or left list-split as it takes the argument column or row -proportions takes the arguments [int] [int], where the first integer is the list and the other content, e.g. proportions 1 3. See list-proportion above for more information. -switch let\'s you go to a timeline if it already exists, if it doesn\'t it will open the timeline in a new window. The syntax is almost the same as in timelines= and is displayed under the examples. - -Some examples: -leader-action=local,lo -leader-action=lists,li -leader-action=federated,fed -leader-action=direct,d -leader-action=history,h -leader-action=tag linux,tl -leader-action=window 0,h -leader-action=list-placement bottom,b -leader-action=list-split column,c -leader-action=proportions 1 3,3 +proportions takes the arguments \[int\] \[int\], where the first integer is the list and the other content, e.g. proportions 1 3. See list-proportion above for more information. + +Example: + +\[\[general.leader-actions\]\] +type=\"close-pane\" +shortcut=\"q\" -Syntax for switch: -leader-action=switch feed,shortcut,[name],[showBoosts],[showReplies] -showBoosts can be either true or false and they are both optional. Here are some examples: +\[\[general.leader-actions\]\] +type=\"list-split\" +data=\"row\" +shortcut=\"r\" -leader-action=switch home,h,false,true -leader-action=switch tag tut,tt +\[\[general.leader-actions\]\] +type=\"list-split\" +data=\"column\" +shortcut=\"c\" +## type +The action you want to run. + +valid: blocking, boosts, clear-notifications, close-pane, compose, edit, favorited, favorites, followers, following, history, list-placement, list-split, lists, move-pane-left, move-pane-right, move-pane-up, move-pane-down, move-pane-home, move-pane-end, muting, newer, pane, preferences, profile, proportions, refetch, stick-to-top, tags + +**type**=*""* + +## data +Data to pass to the action. +**data**=*""* + +## shortcut +A shortcut to run this action with your leader-key + this shortcut. +**shortcut**=*""* # MEDIA This section is \[media\] in your configuration file -## image-viewer -Your image viewer. -**image-viewer**=*xdg-open* +## delete-temp-files +Media files will be removed directly after they\'ve been opened. Some programs doesn\'t like this, so if your media doesn\'t open, try set this to false. Tut will remove all files once you close the program. +**delete-temp-files**=*true* -## image-terminal -Open the image viewer in the same terminal as toot. Only for terminal based viewers. -**image-terminal**=*false* +# MEDIA.IMAGE +This section is \[media.image\] in your configuration file -## image-single -If images should open one by one e.g. \"imv image.png\" multiple times. If set to false all images will open at the same time like this \"imv image1.png image2.png image3.png\". Not all image viewers support this, so try it first. -**image-single**=*true* +## program +The program to open images. TUT_OS_DEFAULT equals xdg-open on Linux, open on MacOS and start on Windows. +**program**=*"TUT_OS_DEFAULT"* -## image-reverse -If you want to open the images in reverse order. In some image viewers this will display the images in the \"right\" order. -**image-reverse**=*false* +## args +Arguments to pass to the program. +**args**=*""* -## video-viewer -Your video viewer. -**video-viewer**=*xdg-open* +## terminal +If the program runs in the terminal set this to true. +**terminal**=*false* -## video-terminal -Open the video viewer in the same terminal as toot. Only for terminal based viewers. -**video-terminal**=*false* +## single +If the program should be called multiple times when there is multiple files. If set to false all files will be passed as an argument, but not all programs support this. +**single**=*true* -## video-single -If videos should open one by one. See image-single. -**video-single**=*true* +## reverse +If the files should be passed in reverse order. This will make some programs display the files in the correct order. +**reverse**=*false* -## video-reverse -If you want your videos in reverse order. In some video apps this will play the files in the \"right\" order. -**video-reverse**=*false* +# MEDIA.VIDEO +This section is \[media.video\] in your configuration file -## audio-viewer -Your audio viewer. -**audio-viewer**=*xdg-open* +## program +The program to open videos. TUT_OS_DEFAULT equals xdg-open on Linux, open on MacOS and start on Windows. +**program**=*"TUT_OS_DEFAULT"* -## audio-terminal -Open the audio viewer in the same terminal as toot. Only for terminal based viewers. -**audio-terminal**=*false* +## args +Arguments to pass to the program. +**args**=*""* -## audio-single -If audio should open one by one. See image-single. -**audio-single**=*true* +## terminal +If the program runs in the terminal set this to true. +**terminal**=*false* -## audio-reverse -If you want to play the audio files in reverse order. In some audio apps this will play the files in the \"right\" order. -**audio-reverse**=*false* +## single +If the program should be called multiple times when there is multiple files. If set to false all files will be passed as an argument, but not all programs support this. +**single**=*true* -## link-viewer -Your web browser. -**link-viewer**=*xdg-open* +## reverse +If the files should be passed in reverse order. This will make some programs display the files in the correct order. +**reverse**=*false* -## link-terminal -Open the browser in the same terminal as toot. Only for terminal based browsers. -**link-terminal**=*false* +# MEDIA.AUDIO +This section is \[media.audio\] in your configuration file -# OPEN-CUSTOM -This section is \[open-custom\] in your configuration file +## program +The program to open audio. TUT_OS_DEFAULT equals xdg-open on Linux, open on MacOS and start on Windows. +**program**=*"TUT_OS_DEFAULT"* -This sections allows you to set up to five custom programs to open URLs with. If the url points to an image, you can set c1-name to img and c1-use to imv. If the program runs in a terminal and you want to run it in the same terminal as tut. Set cX-terminal to true. The name will show up in the UI, so keep it short so all five fits. - -c1-name=name -c1-use=program -c1-terminal=false - -c2-name=name -c2-use=program -c2-terminal=false - -c3-name=name -c3-use=program -c3-terminal=false - -c4-name=name -c4-use=program -c4-terminal=false - -c5-name=name -c5-use=program -c5-terminal=false +## args +Arguments to pass to the program. +**args**=*""* -# OPEN-PATTERN -This section is \[open-pattern\] in your configuration file +## terminal +If the program runs in the terminal set this to true. +**terminal**=*false* -Here you can set your own glob patterns for opening matching URLs in the program you want them to open up in. You could for example open Youtube videos in your video player instead of your default browser. - -You must name the keys foo-pattern, foo-use and foo-terminal, where use is the program that will open up the URL. To see the syntax for glob pattern you can follow this URL https://github.com/gobwas/glob\#syntax. foo-terminal is if the program runs in the terminal and should open in the same terminal as tut itself. - -Example for youtube.com and youtu.be to open up in mpv instead of the browser. - -y1-pattern=\*youtube.com/watch\* -y1-use=mpv -y1-terminal=false - -y2-pattern=\*youtu.be/\* -y2-use=mpv -y2-terminal=false +## single +If the program should be called multiple times when there is multiple files. If set to false all files will be passed as an argument, but not all programs support this. +**single**=*true* + +## reverse +If the files should be passed in reverse order. This will make some programs display the files in the correct order. +**reverse**=*false* + +# MEDIA.LINK +This section is \[media.link\] in your configuration file + +## program +The program to open links. TUT_OS_DEFAULT equals xdg-open on Linux, open on MacOS and start on Windows. +**program**=*"TUT_OS_DEFAULT"* + +## args +Arguments to pass to the program. +**args**=*""* + +## terminal +If the program runs in the terminal set this to true. +**terminal**=*false* # DESKTOP-NOTIFICATION This section is \[desktop-notification\] in your configuration file ## followers -Notification when someone follows you. +Enable notifications when someone follows you. **followers**=*false* ## favorite -Notification when someone favorites one of your toots. +Enable notifications when one of your toots gets favorited. **favorite**=*false* ## mention -Notification when someone mentions you. +Enable notifications when someone mentions you. **mention**=*false* ## update -Notification when someone edits their toot. +Enable notifications when a post you have interacted with gets edited. **update**=*false* ## boost -Notification when someone boosts one of your toots. +Enable notifications when one of your toots gets boosted. **boost**=*false* ## poll -Notification of poll results. +Enable notifications when a poll ends. **poll**=*false* ## posts -Notification when there is new posts in current timeline. +Enable notifications for new posts. **posts**=*false* +# OPEN-CUSTOM +This section is \[open-custom\] in your configuration file + +# OPEN-CUSTOM.PROGRAMS +This section is \[\[open-custom.programs\]\] in your configuration file. You can have multiple of them. + +## program +The program to open the file with. +**program**=*""* + +## args +Arguments to pass to the program. +**args**=*""* + +## terminal +If the program runs in the terminal set this to true. +**terminal**=*false* + +## hint +What should the key hint in tut be for this program. See under the input section to learn more about hint. +**hint**=*""* + +## keys +A list of keys to to open files with this program. See under the input section to learn more about keys. +**keys**=*[]* + +## special-keys +A list of special-keys to open files with this program. See under the input section to learn more about special-keys. +**special-keys**=*[]* + +# OPEN-PATTERN +This section is \[open-pattern\] in your configuration file + +# OPEN-PATTERN.PROGRAMS +This section is \[\[open-pattern.programs\]\] in your configuration file. You can have multiple of them. + +## matching +Here you can set your own glob patterns for opening matching URLs in the program you want them to open up in. You could for example open Youtube videos in your video player instead of your default browser. To see the syntax for glob pattern you can follow this URL https://github.com/gobwas/glob\#syntax. +**matching**=*""* + +## program +The program to open the file with. +**program**=*""* + +## args +Arguments to pass to the program. +**args**=*""* + +## terminal +If the program runs in the terminal set this to true. +**terminal**=*false* + # STYLE This section is \[style\] in your configuration file All styles can be represented in their HEX value like \#ffffff or with their name, so in this case white. The only special value is \"default\" which equals to transparent, so it will be the same color as your terminal. - -You can also use xrdb colors like this xrdb:color1 The program will use colors prefixed with an \* first then look for URxvt or XTerm if it can\'t find any color prefixed with an asterisk. If you don\'t want tut to guess the prefix you can set the prefix yourself. If the xrdb color can\'t be found a preset color will be used. You\'ll have to set theme=none for this to work. - -## xrdb-prefix -The xrdb prefix used for colors in .Xresources. -**xrdb-prefix**=*guess* +You can also use xrdb colors like this xrdb:color1 The program will use colors prefixed with an \* first then look for URxvt or XTerm if it can\'t find any color prefixed with an asterisk. If you don\'t want tut to guess the prefix you can set the prefix yourself. If the xrdb color can\'t be found a preset color will be used. You\'ll have to set theme=\"none\" for this to work. ## theme -You can use some themes that comes bundled with tut. Check out the themes available on the URL below. If a theme is named \"nord.ini\" you just write theme=nord +The theme to use. You can use some themes that comes bundled with tut. Check out the themes available on the URL below. If a theme is named nord.toml you just write theme=\"nord\". https://github.com/RasmusLindroth/tut/tree/master/config/themes -You can also create a theme file in your config directory e.g. ~/.config/tut/themes/foo.ini and then set theme=foo. +You can also create a theme file in your config directory e.g. ~/.config/tut/themes/foo.toml and then set theme=foo. -If you want to use your own theme but don\'t want to create a new file, set theme=none and then you can create your own theme below. -**theme**=*default* +If you want to use your own theme but don\'t want to create a new file, set theme=\"none\" and then you can create your own theme below. + +**theme**=*"default"* + +## xrdb-prefix +The xrdb prefix used for colors in .Xresources. +**xrdb-prefix**=*"guess"* ## background The background color used on most elements. -**background**= +**background**=*""* ## text The text color used on most of the text. -**text**= +**text**=*""* ## subtle The color to display subtle elements or subtle text. Like lines and help text. -**subtle**= +**subtle**=*""* ## warning-text The color for errors or warnings -**warning-text**= +**warning-text**=*""* ## text-special-one This color is used to display username. -**text-special-one**= +**text-special-one**=*""* ## text-special-two This color is used to display username and key hints. -**text-special-two**= +**text-special-two**=*""* ## top-bar-background The color of the bar at the top -**top-bar-background**= +**top-bar-background**=*""* ## top-bar-text The color of the text in the bar at the top. -**top-bar-text**= +**top-bar-text**=*""* ## status-bar-background The color of the bar at the bottom -**status-bar-background**= +**status-bar-background**=*""* ## status-bar-text The color of the text in the bar at the bottom. -**status-bar-text**= +**status-bar-text**=*""* ## status-bar-view-background The color of the bar at the bottom in view mode. -**status-bar-view-background**= +**status-bar-view-background**=*""* ## status-bar-view-text The color of the text in the bar at the bottom in view mode. -**status-bar-view-text**= +**status-bar-view-text**=*""* ## command-text The color of the text in the command bar at the bottom. -**command-text**= +**command-text**=*""* ## list-selected-background Background of selected list items. -**list-selected-background**= +**list-selected-background**=*""* ## list-selected-text The text color of selected list items. -**list-selected-text**= +**list-selected-text**=*""* ## list-selected-inactive-background The background color of selected list items that are out of focus. -**list-selected-inactive-background**= +**list-selected-inactive-background**=*""* ## list-selected-inactive-text The text color of selected list items that are out of focus. -**list-selected-inactive-text**= +**list-selected-inactive-text**=*""* ## controls-text The main color of the text for key hints -**controls-text**= +**controls-text**=*""* ## controls-highlight The highlight color of for key hints -**controls-highlight**= +**controls-highlight**=*""* ## autocomplete-background The background color in dropdowns and autocompletions -**autocomplete-background**= +**autocomplete-background**=*""* ## autocomplete-text The text color in dropdowns at autocompletions -**autocomplete-text**= +**autocomplete-text**=*""* ## autocomplete-selected-background The background color for selected value in dropdowns and autocompletions -**autocomplete-selected-background**= +**autocomplete-selected-background**=*""* ## autocomplete-selected-text The text color for selected value in dropdowns and autocompletions -**autocomplete-selected-text**= +**autocomplete-selected-text**=*""* ## button-color-one The background color on selected button and the text color of unselected buttons -**button-color-one**= +**button-color-one**=*""* ## button-color-two The text color on selected button and the background color of unselected buttons -**button-color-two**= +**button-color-two**=*""* ## timeline-name-background The background on named timelines. -**timeline-name-background**= +**timeline-name-background**=*""* ## timeline-name-text The text color on named timelines -**timeline-name-text**= +**timeline-name-text**=*""* # INPUT This section is \[input\] in your configuration file -You can edit the keys for tut below. +In this section you set the keys to be used in tut. + +The hint option lets you set which part of the hint that will be highlighted in tut. E.g. \[F\]avorite results in a highlighted F and the rest of the text is displayed normaly. +Some of the options can be in two states, like favorites, so there you can set the hint-alt option to something like Un\[F\]avorite. -The syntax is a bit weird, but it works. And I\'ll try to explain it as well as I can. +Examples: +\"\[D\]elete\" = Delete with a highlighted D +\"Un\[F\]ollow\" = UnFollow with a highlighted F +\"\[Enter\]\" = Enter where everything is highlighted +\"Yan\[K\]\" = YanK with a highlighted K +The keys option lets you define what key that should be pressed. This is limited to on character only and they are case sensetive. Example: -status-favorite=\"[F]avorite\",\"Un[F]avorite\",\'f\',\'F\' -status-delete=\"[D]elete\",\'d\',\'D\' - -status-favorite and status-delete differs because favorite can be in two states, so you will have to add two key hints. -Most keys will only have on key hint. Look at the default value for reference. - -Key hints must be in some of the following formats. Remember the quotation marks. -\"\" = empty -\"[D]elete\" = Delete with a highlighted D -\"Un[F]ollow\" = UnFollow with a highlighted F -\"[Enter]\" = Enter where everything is highlighted -\"Yan[K]\" = YanK with a highlighted K - -After the hint (or hints) you must set the keys. You can do this in two ways, with single quotation marks or double ones. +keys=\[\"j\",\"J\"\] -The single ones are for single chars like \'a\', \'b\', \'c\' and double marks are for special keys like \"Enter\". Remember that they are case sensitive. - -To find the names of special keys you have to go to the following site and look for \"var KeyNames = map[Key]string{\" +You can also set special-keys and they\'re for keys like Escape and Enter. To find the names of special keys you have to go to the following site and look for \"var KeyNames = map\[Key\]string{\" https://github.com/gdamore/tcell/blob/master/key.go -## global-down +# INPUT.GLOBAL-DOWN +This section is \[input.global-down\] in your configuration file + Keys for moving down -**global-down**=*\"\",\'j\',\'J\',\"Down\"* -## global-up -Keys for moving up -**global-up**=*\"\",\'k\',\'K\',\"Up\"* +## keys +**keys**=*["j","J"]* + +## special-keys +**special-keys**=*["Down"]* + +# INPUT.GLOBAL-UP +This section is \[input.global-up\] in your configuration file + +Keys for moving down + +## keys +**keys**=*["k","K"]* + +## special-keys +**special-keys**=*["Up"]* + +# INPUT.GLOBAL-ENTER +This section is \[input.global-enter\] in your configuration file -## global-enter To select items -**global-enter**=*\"\",\"Enter\"* -## global-back +## special-keys +**special-keys**=*["Enter"]* + +# INPUT.GLOBAL-BACK +This section is \[input.global-back\] in your configuration file + To go back -**global-back**=*\"[Esc]\",\"Esc\"* -## global-exit -To go back and exit Tut -**global-exit**=*\"[Q]uit\",\'q\',\'Q\'* +## hint +**hint**=*"[Esc]"* + +## special-keys +**special-keys**=*["Esc"]* + +# INPUT.GLOBAL-EXIT +This section is \[input.global-exit\] in your configuration file + +To go back or exit + +## hint +**hint**=*"[Q]uit"* + +## keys +**keys**=*["q","Q"]* + +# INPUT.MAIN-HOME +This section is \[input.main-home\] in your configuration file -## main-home Move to the top -**main-home**=*\"\",\'g\',\"Home\"* -## main-end +## keys +**keys**=*["g"]* + +## special-keys +**special-keys**=*["Home"]* + +# INPUT.MAIN-END +This section is \[input.main-end\] in your configuration file + Move to the bottom -**main-end**=*\"\",\'G\',\"End\"* -## main-prev-feed +## keys +**keys**=*["G"]* + +## special-keys +**special-keys**=*["End"]* + +# INPUT.MAIN-PREV-FEED +This section is \[input.main-prev-feed\] in your configuration file + Go to previous feed -**main-prev-feed**=*\"\",\'h\',\'H\',\"Left\"* -## main-next-feed +## keys +**keys**=*["h","H"]* + +## special-keys +**special-keys**=*["Left"]* + +# INPUT.MAIN-NEXT-FEED +This section is \[input.main-next-feed\] in your configuration file + Go to next feed -**main-next-feed**=*\"\",\'l\',\'L\',\"Right\"* -## main-prev-window -Focus on the previous feed window -**main-prev-window**=*\"\",\"Backtab\"* +## keys +**keys**=*["l","L"]* + +## special-keys +**special-keys**=*["Right"]* + +# INPUT.MAIN-PREV-PANE +This section is \[input.main-prev-pane\] in your configuration file + +Focus on the previous feed pane + +## special-keys +**special-keys**=*["Backtab"]* + +# INPUT.MAIN-NEXT-PANE +This section is \[input.main-next-pane\] in your configuration file + +Focus on the next feed pane -## main-next-window -Focus on the next feed window -**main-next-window**=*\"\",\"Tab\"* +## special-keys +**special-keys**=*["Tab"]* -## main-notification-focus -Focus on the notification list -**main-notification-focus**=*\"[N]otifications\",\'n\',\'N\'* +# INPUT.MAIN-NEXT-ACCOUNT +This section is \[input.main-next-account\] in your configuration file + +Focus on the next account + +## special-keys +**special-keys**=*["Ctrl-N"]* + +# INPUT.MAIN-PREV-ACCOUNT +This section is \[input.main-prev-account\] in your configuration file + +Focus on the previous account + +## special-keys +**special-keys**=*["Ctrl-P"]* + +# INPUT.MAIN-COMPOSE +This section is \[input.main-compose\] in your configuration file -## main-compose Compose a new toot -**main-compose**=*\"\",\'c\',\'C\'* -## status-avatar +## keys +**keys**=*["c","C"]* + +# INPUT.STATUS-AVATAR +This section is \[input.status-avatar\] in your configuration file + Open avatar -**status-avatar**=*\"[A]vatar\",\'a\',\'A\'* -## status-boost +## hint +**hint**=*"[A]vatar"* + +## keys +**keys**=*["a","A"]* + +# INPUT.STATUS-BOOST +This section is \[input.status-boost\] in your configuration file + Boost a toot -**status-boost**=*\"[B]oost\",\"Un[B]oost\",\'b\',\'B\'* -## status-edit +## hint +**hint**=*"[B]oost"* + +## keys +**keys**=*["b","B"]* + +# INPUT.STATUS-EDIT +This section is \[input.status-edit\] in your configuration file + Edit a toot -**status-edit**=*\"[E]dit\",\'e\',\'E\'* -## status-delete +## hint +**hint**=*"[E]dit"* + +## keys +**keys**=*["e","E"]* + +# INPUT.STATUS-DELETE +This section is \[input.status-delete\] in your configuration file + Delete a toot -**status-delete**=*\"[D]elete\",\'d\',\'D\'* -## status-favorite +## hint +**hint**=*"[D]elete"* + +## keys +**keys**=*["d","D"]* + +# INPUT.STATUS-FAVORITE +This section is \[input.status-favorite\] in your configuration file + Favorite a toot -**status-favorite**=*\"[F]avorite\",\"Un[F]avorite\",\'f\',\'F\'* -## status-media +## hint +**hint**=*"[F]avorite"* + +## keys +**keys**=*["f","F"]* + +# INPUT.STATUS-MEDIA +This section is \[input.status-media\] in your configuration file + Open toots media files -**status-media**=*\"[M]edia\",\'m\',\'M\'* -## status-links +## hint +**hint**=*"[M]edia"* + +## keys +**keys**=*["m","M"]* + +# INPUT.STATUS-LINKS +This section is \[input.status-links\] in your configuration file + Open links -**status-links**=*\"[O]pen\",\'o\',\'O\'* -## status-poll +## hint +**hint**=*"[O]pen"* + +## keys +**keys**=*["o","O"]* + +# INPUT.STATUS-POLL +This section is \[input.status-poll\] in your configuration file + Open poll -**status-poll**=*\"[P]oll\",\'p\',\'P\'* -## status-reply +## hint +**hint**=*"[P]oll"* + +## keys +**keys**=*["p","P"]* + +# INPUT.STATUS-REPLY +This section is \[input.status-reply\] in your configuration file + Reply to toot -**status-reply**=*\"[R]eply\",\'r\',\'R\'* -## status-bookmark +## hint +**hint**=*"[R]eply"* + +## keys +**keys**=*["r","R"]* + +# INPUT.STATUS-BOOKMARK +This section is \[input.status-bookmark\] in your configuration file + Save/bookmark a toot -**status-bookmark**=*\"[S]ave\",\"Un[S]ave\",\'s\',\'S\'* -## status-thread +## hint +**hint**=*"[S]ave"* + +## hint-alt +**hint-alt**=*"Un[S]ave"* + +## keys +**keys**=*["s","S"]* + +# INPUT.STATUS-THREAD +This section is \[input.status-thread\] in your configuration file + View thread -**status-thread**=*\"[T]hread\",\'t\',\'T\'* -## status-user +## hint +**hint**=*"[T]hread"* + +## keys +**keys**=*["t","T"]* + +# INPUT.STATUS-USER +This section is \[input.status-user\] in your configuration file + Open user profile -**status-user**=*\"[U]ser\",\'u\',\'U\'* -## status-view-focus +## hint +**hint**=*"[U]ser"* + +## keys +**keys**=*["u","U"]* + +# INPUT.STATUS-VIEW-FOCUS +This section is \[input.status-view-focus\] in your configuration file + Open the view mode -**status-view-focus**=*\"[V]iew\",\'v\',\'V\'* -## status-yank +## hint +**hint**=*"[V]iew"* + +## keys +**keys**=*["v","V"]* + +# INPUT.STATUS-YANK +This section is \[input.status-yank\] in your configuration file + Yank the url of the toot -**status-yank**=*\"[Y]ank\",\'y\',\'Y\'* -## status-toggle-cw +## hint +**hint**=*"[Y]ank"* + +## keys +**keys**=*["y","Y"]* + +# INPUT.STATUS-TOGGLE-CW +This section is \[input.status-toggle-cw\] in your configuration file + Show the content in a content warning -**status-toggle-cw**=*\"Press [Z] to toggle cw\",\'z\',\'Z\'* -## status-show-filtered +## hint +**hint**=*"Press [Z] to toggle cw"* + +## keys +**keys**=*["z","Z"]* + +# INPUT.STATUS-SHOW-FILTERED +This section is \[input.status-show-filtered\] in your configuration file + Show the content of a filtered toot -**status-show-filtered**=*\"Press [Z] to view filtered toot\",\'z\',\'Z\'* -## user-avatar +## hint +**hint**=*"Press [Z] to view filtered toot"* + +## keys +**keys**=*["z","Z"]* + +# INPUT.USER-AVATAR +This section is \[input.user-avatar\] in your configuration file + View avatar -**user-avatar**=*\"[A]vatar\",\'a\',\'A\'* -## user-block +## hint +**hint**=*"[A]vatar"* + +## keys +**keys**=*["a","A"]* + +# INPUT.USER-BLOCK +This section is \[input.user-block\] in your configuration file + Block the user -**user-block**=*\"[B]lock\",\"Un[B]lock\",\'b\',\'B\'* -## user-follow +## hint +**hint**=*"[B]lock"* + +## hint-alt +**hint-alt**=*"Un[B]lock"* + +## keys +**keys**=*["b","B"]* + +# INPUT.USER-FOLLOW +This section is \[input.user-follow\] in your configuration file + Follow user -**user-follow**=*\"[F]ollow\",\"Un[F]ollow\",\'f\',\'F\'* -## user-follow-request-decide +## hint +**hint**=*"[F]ollow"* + +## hint-alt +**hint-alt**=*"Un[F]ollow"* + +## keys +**keys**=*["f","F"]* + +# INPUT.USER-FOLLOW-REQUEST-DECIDE +This section is \[input.user-follow-request-decide\] in your configuration file + Follow user -**user-follow-request-decide**=*\"Follow [R]equest\",\"Follow [R]equest\",\'r\',\'R\'* -## user-mute +## hint +**hint**=*"Follow [R]equest"* + +## hint-alt +**hint-alt**=*"Follow [R]equest"* + +## keys +**keys**=*["r","R"]* + +# INPUT.USER-MUTE +This section is \[input.user-mute\] in your configuration file + Mute user -**user-mute**=*\"[M]ute\",\"Un[M]ute\",\'m\',\'M\'* -## user-links +## hint +**hint**=*"[M]ute"* + +## hint-alt +**hint-alt**=*"Un[M]ute"* + +## keys +**keys**=*["m","M"]* + +# INPUT.USER-LINKS +This section is \[input.user-links\] in your configuration file + Open links -**user-links**=*\"[O]pen\",\'o\',\'O\'* -## user-user +## hint +**hint**=*"[O]pen"* + +## keys +**keys**=*["o","O"]* + +# INPUT.USER-USER +This section is \[input.user-user\] in your configuration file + View user profile -**user-user**=*\"[U]ser\",\'u\',\'U\'* -## user-view-focus +## hint +**hint**=*"[U]ser"* + +## keys +**keys**=*["u","U"]* + +# INPUT.USER-VIEW-FOCUS +This section is \[input.user-view-focus\] in your configuration file + Open view mode -**user-view-focus**=*\"[V]iew\",\'v\',\'V\'* -## user-yank +## hint +**hint**=*"[V]iew"* + +## keys +**keys**=*["v","V"]* + +# INPUT.USER-YANK +This section is \[input.user-yank\] in your configuration file + Yank the user URL -**user-yank**=*\"[Y]ank\",\'y\',\'Y\'* -## list-open-feed +## hint +**hint**=*"[Y]ank"* + +## keys +**keys**=*["y","Y"]* + +# INPUT.LIST-OPEN-FEED +This section is \[input.list-open-feed\] in your configuration file + Open list -**list-open-feed**=*\"[O]pen\",\'o\',\'O\'* -## list-user-list +## hint +**hint**=*"[O]pen"* + +## keys +**keys**=*["o","O"]* + +# INPUT.LIST-USER-LIST +This section is \[input.list-user-list\] in your configuration file + List all users in a list -**list-user-list**=*\"[U]sers\",\'u\',\'U\'* -## list-user-add +## hint +**hint**=*"[U]sers"* + +## keys +**keys**=*["u","U"]* + +# INPUT.LIST-USER-ADD +This section is \[input.list-user-add\] in your configuration file + Add user to list -**list-user-add**=*\"[A]dd\",\'a\',\'A\'* -## list-user-delete +## hint +**hint**=*"[A]dd"* + +## keys +**keys**=*["a","A"]* + +# INPUT.LIST-USER-DELETE +This section is \[input.list-user-delete\] in your configuration file + Delete user from list -**list-user-delete**=*\"[D]elete\",\'d\',\'D\'* -## link-open +## hint +**hint**=*"[D]elete"* + +## keys +**keys**=*["d","D"]* + +# INPUT.LINK-OPEN +This section is \[input.link-open\] in your configuration file + Open URL -**link-open**=*\"[O]pen\",\'o\',\'O\'* -## link-yank +## hint +**hint**=*"[O]pen"* + +## keys +**keys**=*["o","O"]* + +# INPUT.LINK-YANK +This section is \[input.link-yank\] in your configuration file + Yank the URL -**link-yank**=*\"[Y]ank\",\'y\',\'Y\'* -## tag-open-feed +## hint +**hint**=*"[Y]ank"* + +## keys +**keys**=*["y","Y"]* + +# INPUT.TAG-OPEN-FEED +This section is \[input.tag-open-feed\] in your configuration file + Open tag feed -**tag-open-feed**=*\"[O]pen\",\'o\',\'O\'* -## tag-follow +## hint +**hint**=*"[O]pen"* + +## keys +**keys**=*["o","O"]* + +# INPUT.TAG-FOLLOW +This section is \[input.tag-follow\] in your configuration file + Toggle follow on tag -**tag-follow**=*\"[F]ollow\",\"Un[F]ollow\",\'f\',\'F\'* -## compose-edit-cw +## hint +**hint**=*"[F]ollow"* + +## hint-alt +**hint-alt**=*"Un[F]ollow"* + +## keys +**keys**=*["f","F"]* + +# INPUT.COMPOSE-EDIT-CW +This section is \[input.compose-edit-cw\] in your configuration file + Edit content warning text on new toot -**compose-edit-cw**=*\"[C]W text\",\'c\',\'C\'* -## compose-edit-text +## hint +**hint**=*"[C]W text"* + +## keys +**keys**=*["c","C"]* + +# INPUT.COMPOSE-EDIT-TEXT +This section is \[input.compose-edit-text\] in your configuration file + Edit the text on new toot -**compose-edit-text**=*\"[E]dit text\",\'e\',\'E\'* -## compose-include-quote +## hint +**hint**=*"[E]dit text"* + +## keys +**keys**=*["e","E"]* + +# INPUT.COMPOSE-INCLUDE-QUOTE +This section is \[input.compose-include-quote\] in your configuration file + Include a quote when replying -**compose-include-quote**=*\"[I]nclude quote\",\'i\',\'I\'* -## compose-media-focus +## hint +**hint**=*"[I]nclude quote"* + +## keys +**keys**=*["i","I"]* + +# INPUT.COMPOSE-MEDIA-FOCUS +This section is \[input.compose-media-focus\] in your configuration file + Focus on adding media to toot -**compose-media-focus**=*\"[M]edia\",\'m\',\'M\'* -## compose-post +## hint +**hint**=*"[M]edia"* + +## keys +**keys**=*["m","M"]* + +# INPUT.COMPOSE-POST +This section is \[input.compose-post\] in your configuration file + Post the new toot -**compose-post**=*\"[P]ost\",\'p\',\'P\'* -## compose-toggle-content-warning +## hint +**hint**=*"[P]ost"* + +## keys +**keys**=*["p","P"]* + +# INPUT.COMPOSE-TOGGLE-CONTENT-WARNING +This section is \[input.compose-toggle-content-warning\] in your configuration file + Toggle content warning on toot -**compose-toggle-content-warning**=*\"[T]oggle CW\",\'t\',\'T\'* -## compose-visibility +## hint +**hint**=*"[T]oggle CW"* + +## keys +**keys**=*["t","T"]* + +# INPUT.COMPOSE-VISIBILITY +This section is \[input.compose-visibility\] in your configuration file + Edit the visibility on new toot -**compose-visibility**=*\"[V]isibility\",\'v\',\'V\'* -## compose-language +## hint +**hint**=*"[V]isibility"* + +## keys +**keys**=*["v","V"]* + +# INPUT.COMPOSE-LANGUAGE +This section is \[input.compose-language\] in your configuration file + Edit the language of a toot -**compose-language**=*\"[L]ang\",\'l\',\'L\'* -## compose-poll +## hint +**hint**=*"[L]ang"* + +## keys +**keys**=*["l","L"]* + +# INPUT.COMPOSE-POLL +This section is \[input.compose-poll\] in your configuration file + Switch to creating a poll -**compose-poll**=*\"P[O]ll\",\'o\',\'O\'* -## media-delete +## hint +**hint**=*"P[O]ll"* + +## keys +**keys**=*["o","O"]* + +# INPUT.MEDIA-DELETE +This section is \[input.media-delete\] in your configuration file + Delete media file -**media-delete**=*\"[D]elete\",\'d\',\'D\'* -## media-edit-desc +## hint +**hint**=*"[D]elete"* + +## keys +**keys**=*["d","D"]* + +# INPUT.MEDIA-EDIT-DESC +This section is \[input.media-edit-desc\] in your configuration file + Edit the description on media file -**media-edit-desc**=*\"[E]dit desc\",\'e\',\'E\'* -## media-add +## hint +**hint**=*"[E]dit desc"* + +## keys +**keys**=*["e","E"]* + +# INPUT.MEDIA-ADD +This section is \[input.media-add\] in your configuration file + Add a new media file -**media-add**=*\"[A]dd\",\'a\',\'A\'* -## vote-vote +## hint +**hint**=*"[A]dd"* + +## keys +**keys**=*["a","A"]* + +# INPUT.VOTE-VOTE +This section is \[input.vote-vote\] in your configuration file + Vote on poll -**vote-vote**=*\"[V]ote\",\'v\',\'V\'* -## vote-select +## hint +**hint**=*"[V]ote"* + +## keys +**keys**=*["v","V"]* + +# INPUT.VOTE-SELECT +This section is \[input.vote-select\] in your configuration file + Select item to vote on -**vote-select**=*\"[Enter] to select\",\' \', \"Enter\"* -## poll-add +## hint +**hint**=*"[Enter] to select"* + +## special-keys +**special-keys**=*["Enter"]* + +# INPUT.POLL-ADD +This section is \[input.poll-add\] in your configuration file + Add a new poll option -**poll-add**=*\"[A]dd\",\'a\',\'A\'* -## poll-edit +## hint +**hint**=*"[A]dd"* + +## keys +**keys**=*["a","A"]* + +# INPUT.POLL-EDIT +This section is \[input.poll-edit\] in your configuration file + Edit a poll option -**poll-edit**=*\"[E]dit\",\'e\',\'E\'* -## poll-delete +## hint +**hint**=*"[E]dit"* + +## keys +**keys**=*["e","E"]* + +# INPUT.POLL-DELETE +This section is \[input.poll-delete\] in your configuration file + Delete a poll option -**poll-delete**=*\"[D]elete\",\'d\',\'D\'* -## poll-multi-toggle +## hint +**hint**=*"[D]elete"* + +## keys +**keys**=*["d","D"]* + +# INPUT.POLL-MULTI-TOGGLE +This section is \[input.poll-multi-toggle\] in your configuration file + Toggle voting on multiple options -**poll-multi-toggle**=*\"Toggle [M]ultiple\",\'m\',\'M\'* -## poll-expiration +## hint +**hint**=*"Toggle [M]ultiple"* + +## keys +**keys**=*["m","M"]* + +# INPUT.POLL-EXPIRATION +This section is \[input.poll-expiration\] in your configuration file + Change the expiration of poll -**poll-expiration**=*\"E[X]pires\",\'x\',\'X\'* -## preference-name +## hint +**hint**=*"E[X]pires"* + +## keys +**keys**=*["x","X"]* + +# INPUT.PREFERENCE-NAME +This section is \[input.preference-name\] in your configuration file + Change display name -**preference-name**=*\"[N]ame\",\'n\',\'N\'* -## preference-visibility +## hint +**hint**=*"[N]ame"* + +## keys +**keys**=*["n","N"]* + +# INPUT.PREFERENCE-VISIBILITY +This section is \[input.preference-visibility\] in your configuration file + Change default visibility of toots -**preference-visibility**=*\"[V]isibility\",\'v\',\'V\'* -## preference-bio +## hint +**hint**=*"[V]isibility"* + +## keys +**keys**=*["v","V"]* + +# INPUT.PREFERENCE-BIO +This section is \[input.preference-bio\] in your configuration file + Change bio in profile -**preference-bio**=*\"[B]io\",\'b\',\'B\'* -## preference-save +## hint +**hint**=*"[B]io"* + +## keys +**keys**=*["b","B"]* + +# INPUT.PREFERENCE-SAVE +This section is \[input.preference-save\] in your configuration file + Save your preferences -**preference-save**=*\"[S]ave\",\'s\',\'S\'* -## preference-fields +## hint +**hint**=*"[S]ave"* + +## keys +**keys**=*["s","S"]* + +# INPUT.PREFERENCE-FIELDS +This section is \[input.preference-fields\] in your configuration file + Edit profile fields -**preference-fields**=*\"[F]ields\",\'f\',\'F\'* -## preference-fields-add +## hint +**hint**=*"[F]ields"* + +## keys +**keys**=*["f","F"]* + +# INPUT.PREFERENCE-FIELDS-ADD +This section is \[input.preference-fields-add\] in your configuration file + Add new field -**preference-fields-add**=*\"[A]dd\",\'a\',\'A\'* -## preference-fields-edit +## hint +**hint**=*"[A]dd"* + +## keys +**keys**=*["a","A"]* + +# INPUT.PREFERENCE-FIELDS-EDIT +This section is \[input.preference-fields-edit\] in your configuration file + Edit current field -**preference-fields-edit**=*\"[E]dit\",\'e\',\'E\'* -## preference-fields-delete +## hint +**hint**=*"[E]dit"* + +## keys +**keys**=*["e","E"]* + +# INPUT.PREFERENCE-FIELDS-DELETE +This section is \[input.preference-fields-delete\] in your configuration file + Delete current field -**preference-fields-delete**=*\"[D]elete\",\'d\',\'D\'* + +## hint +**hint**=*"[D]elete"* + +## keys +**keys**=*["d","D"]* + +# INPUT.EDITOR-EXIT +This section is \[input.editor-exit\] in your configuration file + +Exit the editor + +## hint +**hint**=*"[Esc] when done"* + +## special-keys +**special-keys**=*["Esc"]* # SEE ALSO tut(1) - flags and commands diff --git a/docs/man/tut.7 b/docs/man/tut.7 index bf24f2c..059a29a 100644 --- a/docs/man/tut.7 +++ b/docs/man/tut.7 @@ -14,7 +14,7 @@ . ftr VB CB . ftr VBI CBI .\} -.TH "tut" "7" "2023-01-01" "tut 1.0.34" "" +.TH "tut" "7" "2023-01-23" "tut 2.0.0" "" .hy .SH NAME .PP @@ -108,8 +108,13 @@ List all your bookmarks \f[B]:clear-notifications\f[R] Remove all of your notifications .TP -\f[B]:close-window\f[R] -Closes the current window, including all the timelines in said window +\f[B]:clear-temp\f[R] +Remove all of your media files that have been downloaded. +Only needed if you have set delete-temp-files to false under [media] in +your config. +.TP +\f[B]:close-pane\f[R] +Closes the current pane, including all the timelines in said pane .TP \f[B]:compose\f[R] Compose a new toot @@ -150,12 +155,15 @@ Show a list of your lists Place the list in choosen placement .TP \f[B]:list-split\f[R] \f[I]row|column\f[R] -Split the timelines in window by row or column +Split the timelines by row or column +.TP +\f[B]:login\f[R] +Login to one more account .TP -\f[B]:move-window\f[R] \f[I]left|right|up|down|home|end\f[R] -Moves the window in choosen direction +\f[B]:move-pane\f[R] \f[I]left|right|up|down|home|end\f[R] +Moves the pane in choosen direction .TP -\f[B]:mv\f[R] \f[I]l|r|u|d|h|e\f[R] +\f[B]:mp\f[R] \f[I]l|r|u|d|h|e\f[R] Shorter form of former command .TP \f[B]:muting\f[R] @@ -164,15 +172,21 @@ Lists users that you\[aq]ve muted \f[B]:newer\f[R] Force load newer toots in current timeline .TP +\f[B]:next-acct\f[R] +Go to the next account if you\[aq]re logged in to multiple +.TP \f[B]:preferences\f[R] Update your profile and some other settings .TP +\f[B]:prev-acct\f[R] +Go to the prev account if you\[aq]re logged in to multiple +.TP \f[B]:profile\f[R] Go to your profile .TP \f[B]:proportions\f[R] \f[I][int] [int]\f[R] -Sets the proportions of the windows and the content. -The first integer is your windows and the other for content, +Sets the proportions of the panes and the content. +The first integer is your panes and the other for content, e.g.\ :proportions 1 3 .TP \f[B]:refetch\f[R] @@ -202,9 +216,8 @@ Search for users named , e.g.\ :user rasmus. To narrow a search include the instance like this :user rasmus\[at]mastodon.acc.sunet.se .TP -\f[B]:window\f[R] \f[I]\f[R] -Switch window by index (zero indexed) e.g.\ :window 0 for the left/top -window +\f[B]:pane\f[R] \f[I]\f[R] +Switch pane by index (zero indexed) e.g.\ :pane 0 for the left/top pane .SH SEE ALSO .IP .nf diff --git a/docs/man/tut.7.md b/docs/man/tut.7.md index a99cc64..96e1e22 100644 --- a/docs/man/tut.7.md +++ b/docs/man/tut.7.md @@ -1,6 +1,6 @@ -% tut(7) tut 1.0.34 +% tut(7) tut 2.0.0 % Rasmus Lindroth -% 2023-01-01 +% 2023-01-23 # NAME tut - keys and commands inside of tut(1) @@ -53,8 +53,11 @@ To change the keys look at tut(5) under the *INPUT* section. **:clear-notifications** : Remove all of your notifications -**:close-window** -: Closes the current window, including all the timelines in said window +**:clear-temp** +: Remove all of your media files that have been downloaded. Only needed if you have set delete-temp-files to false under \[media\] in your config. + +**:close-pane** +: Closes the current pane, including all the timelines in said pane **:compose** : Compose a new toot @@ -93,12 +96,15 @@ To change the keys look at tut(5) under the *INPUT* section. : Place the list in choosen placement **:list-split** *row|column* -: Split the timelines in window by row or column +: Split the timelines by row or column + +**:login** +: Login to one more account -**:move-window** *left|right|up|down|home|end* -: Moves the window in choosen direction +**:move-pane** *left|right|up|down|home|end* +: Moves the pane in choosen direction -**:mv** *l|r|u|d|h|e* +**:mp** *l|r|u|d|h|e* : Shorter form of former command **:muting** @@ -107,14 +113,20 @@ To change the keys look at tut(5) under the *INPUT* section. **:newer** : Force load newer toots in current timeline +**:next-acct** +: Go to the next account if you\'re logged in to multiple + **:preferences** : Update your profile and some other settings +**:prev-acct** +: Go to the prev account if you\'re logged in to multiple + **:profile** : Go to your profile -**:proportions** *[int] [int]* -: Sets the proportions of the windows and the content. The first integer is your windows and the other for content, e.g. :proportions 1 3 +**:proportions** *\[int\] \[int\]* +: Sets the proportions of the panes and the content. The first integer is your panes and the other for content, e.g. :proportions 1 3 **:refetch** : Refetches the current item that you\'re viewing. Can be used to update poll results. @@ -137,8 +149,8 @@ To change the keys look at tut(5) under the *INPUT* section. **:user** *\* : Search for users named \, e.g. :user rasmus. To narrow a search include the instance like this :user rasmus@mastodon.acc.sunet.se -**:window** *\* -: Switch window by index (zero indexed) e.g. :window 0 for the left/top window +**:pane** *\* +: Switch pane by index (zero indexed) e.g. :pane 0 for the left/top pane # SEE ALSO tut(1) - flags and commands diff --git a/feed/feed.go b/feed/feed.go index b83c7ad..6565b91 100644 --- a/feed/feed.go +++ b/feed/feed.go @@ -64,8 +64,8 @@ type Feed struct { streams []*api.Receiver name string close func() - showBoosts bool - showReplies bool + hideBoosts bool + hideReplies bool } func (f *Feed) Type() config.FeedType { @@ -82,10 +82,10 @@ func (f *Feed) filteredList() []api.Item { if f.Type() == config.TimelineHomeSpecial && x.Reblog == nil && x.InReplyToID == nil { continue } - if x.Reblog != nil && !f.showBoosts { + if x.Reblog != nil && f.hideBoosts { continue } - if x.InReplyToID != nil && !f.showReplies { + if x.InReplyToID != nil && f.hideReplies { continue } } @@ -836,7 +836,7 @@ func (f *Feed) startStreamNotification(rec *api.Receiver, timeline string, err e }() } -func newFeed(ac *api.AccountClient, ft config.FeedType, cnf *config.Config, showBoosts bool, showReplies bool) *Feed { +func newFeed(ac *api.AccountClient, ft config.FeedType, cnf *config.Config, hideBoosts bool, hideReplies bool) *Feed { return &Feed{ accountClient: ac, config: cnf, @@ -849,13 +849,13 @@ func newFeed(ac *api.AccountClient, ft config.FeedType, cnf *config.Config, show Update: make(chan DesktopNotificationHolder, 1), loadingNewer: &LoadingLock{}, loadingOlder: &LoadingLock{}, - showBoosts: showBoosts, - showReplies: showReplies, + hideBoosts: hideBoosts, + hideReplies: hideReplies, } } -func NewTimelineHome(ac *api.AccountClient, cnf *config.Config, showBoosts bool, showReplies bool) *Feed { - feed := newFeed(ac, config.TimelineHome, cnf, showBoosts, showReplies) +func NewTimelineHome(ac *api.AccountClient, cnf *config.Config, hideBoosts bool, hideReplies bool) *Feed { + feed := newFeed(ac, config.TimelineHome, cnf, hideBoosts, hideReplies) feed.loadNewer = func() { feed.normalNewer(feed.accountClient.GetTimeline) } feed.loadOlder = func() { feed.normalOlder(feed.accountClient.GetTimeline) } feed.startStream(feed.accountClient.NewHomeStream()) @@ -868,8 +868,8 @@ func NewTimelineHome(ac *api.AccountClient, cnf *config.Config, showBoosts bool, return feed } -func NewTimelineHomeSpecial(ac *api.AccountClient, cnf *config.Config, showBoosts bool, showReplies bool) *Feed { - feed := newFeed(ac, config.TimelineHomeSpecial, cnf, showBoosts, showReplies) +func NewTimelineHomeSpecial(ac *api.AccountClient, cnf *config.Config, hideBoosts bool, hideReplies bool) *Feed { + feed := newFeed(ac, config.TimelineHomeSpecial, cnf, hideBoosts, hideReplies) feed.loadNewer = func() { feed.normalNewer(feed.accountClient.GetTimeline) } feed.loadOlder = func() { feed.normalOlder(feed.accountClient.GetTimeline) } feed.startStream(feed.accountClient.NewHomeStream()) @@ -882,8 +882,8 @@ func NewTimelineHomeSpecial(ac *api.AccountClient, cnf *config.Config, showBoost return feed } -func NewTimelineFederated(ac *api.AccountClient, cnf *config.Config, showBoosts bool, showReplies bool) *Feed { - feed := newFeed(ac, config.TimelineFederated, cnf, showBoosts, showReplies) +func NewTimelineFederated(ac *api.AccountClient, cnf *config.Config, hideBoosts bool, hideReplies bool) *Feed { + feed := newFeed(ac, config.TimelineFederated, cnf, hideBoosts, hideReplies) feed.loadNewer = func() { feed.normalNewer(feed.accountClient.GetTimelineFederated) } feed.loadOlder = func() { feed.normalOlder(feed.accountClient.GetTimelineFederated) } feed.startStream(feed.accountClient.NewFederatedStream()) @@ -896,8 +896,8 @@ func NewTimelineFederated(ac *api.AccountClient, cnf *config.Config, showBoosts return feed } -func NewTimelineLocal(ac *api.AccountClient, cnf *config.Config, showBoosts bool, showReplies bool) *Feed { - feed := newFeed(ac, config.TimelineLocal, cnf, showBoosts, showReplies) +func NewTimelineLocal(ac *api.AccountClient, cnf *config.Config, hideBoosts bool, hideReplies bool) *Feed { + feed := newFeed(ac, config.TimelineLocal, cnf, hideBoosts, hideReplies) feed.loadNewer = func() { feed.normalNewer(feed.accountClient.GetTimelineLocal) } feed.loadOlder = func() { feed.normalOlder(feed.accountClient.GetTimelineLocal) } feed.startStream(feed.accountClient.NewLocalStream()) @@ -910,7 +910,7 @@ func NewTimelineLocal(ac *api.AccountClient, cnf *config.Config, showBoosts bool } func NewConversations(ac *api.AccountClient, cnf *config.Config) *Feed { - feed := newFeed(ac, config.Conversations, cnf, true, true) + feed := newFeed(ac, config.Conversations, cnf, false, false) feed.loadNewer = func() { feed.normalNewer(feed.accountClient.GetConversations) } feed.loadOlder = func() { feed.normalOlder(feed.accountClient.GetConversations) } feed.startStream(feed.accountClient.NewDirectStream()) @@ -923,8 +923,8 @@ func NewConversations(ac *api.AccountClient, cnf *config.Config) *Feed { return feed } -func NewNotifications(ac *api.AccountClient, cnf *config.Config, showBoosts bool, showReplies bool) *Feed { - feed := newFeed(ac, config.Notifications, cnf, showBoosts, showReplies) +func NewNotifications(ac *api.AccountClient, cnf *config.Config, hideBoosts bool, hideReplies bool) *Feed { + feed := newFeed(ac, config.Notifications, cnf, hideBoosts, hideReplies) feed.loadNewer = func() { feed.normalNewerNotification(feed.accountClient.GetNotifications, cnf.General.NotificationsToHide) } @@ -942,7 +942,7 @@ func NewNotifications(ac *api.AccountClient, cnf *config.Config, showBoosts bool } func NewNotificationsMentions(ac *api.AccountClient, cnf *config.Config) *Feed { - feed := newFeed(ac, config.Notifications, cnf, true, true) + feed := newFeed(ac, config.Notifications, cnf, false, false) hide := []config.NotificationToHide{config.HideStatus, config.HideBoost, config.HideFollow, config.HideFollowRequest, config.HideFavorite, config.HidePoll, config.HideEdited} feed.loadNewer = func() { feed.normalNewerNotification(feed.accountClient.GetNotifications, hide) @@ -961,7 +961,7 @@ func NewNotificationsMentions(ac *api.AccountClient, cnf *config.Config) *Feed { } func NewFavorites(ac *api.AccountClient, cnf *config.Config) *Feed { - feed := newFeed(ac, config.Favorited, cnf, true, true) + feed := newFeed(ac, config.Favorited, cnf, false, false) feed.loadNewer = func() { feed.linkNewer(feed.accountClient.GetFavorites) } feed.loadOlder = func() { feed.linkOlder(feed.accountClient.GetFavorites) } @@ -969,7 +969,7 @@ func NewFavorites(ac *api.AccountClient, cnf *config.Config) *Feed { } func NewBookmarks(ac *api.AccountClient, cnf *config.Config) *Feed { - feed := newFeed(ac, config.Saved, cnf, true, true) + feed := newFeed(ac, config.Saved, cnf, false, false) feed.loadNewer = func() { feed.linkNewer(feed.accountClient.GetBookmarks) } feed.loadOlder = func() { feed.linkOlder(feed.accountClient.GetBookmarks) } @@ -977,7 +977,7 @@ func NewBookmarks(ac *api.AccountClient, cnf *config.Config) *Feed { } func NewUserSearch(ac *api.AccountClient, cnf *config.Config, search string) *Feed { - feed := newFeed(ac, config.UserList, cnf, true, true) + feed := newFeed(ac, config.UserList, cnf, false, false) feed.name = search feed.loadNewer = func() { feed.singleNewerSearch(feed.accountClient.GetUsers, search) } @@ -985,7 +985,7 @@ func NewUserSearch(ac *api.AccountClient, cnf *config.Config, search string) *Fe } func NewUserProfile(ac *api.AccountClient, cnf *config.Config, user *api.User) *Feed { - feed := newFeed(ac, config.User, cnf, true, true) + feed := newFeed(ac, config.User, cnf, false, false) feed.name = user.Data.Acct feed.sticky = append(feed.sticky, api.NewUserItem(user, true)) pinned, err := ac.GetUserPinned(user.Data.ID) @@ -999,7 +999,7 @@ func NewUserProfile(ac *api.AccountClient, cnf *config.Config, user *api.User) * } func NewThread(ac *api.AccountClient, cnf *config.Config, status *mastodon.Status) *Feed { - feed := newFeed(ac, config.Thread, cnf, true, true) + feed := newFeed(ac, config.Thread, cnf, false, false) once := true feed.loadNewer = func() { if once { @@ -1012,7 +1012,7 @@ func NewThread(ac *api.AccountClient, cnf *config.Config, status *mastodon.Statu } func NewHistory(ac *api.AccountClient, cnf *config.Config, status *mastodon.Status) *Feed { - feed := newFeed(ac, config.History, cnf, true, true) + feed := newFeed(ac, config.History, cnf, false, false) once := true feed.loadNewer = func() { if once { @@ -1023,8 +1023,8 @@ func NewHistory(ac *api.AccountClient, cnf *config.Config, status *mastodon.Stat return feed } -func NewTag(ac *api.AccountClient, cnf *config.Config, search string, showBoosts bool, showReplies bool) *Feed { - feed := newFeed(ac, config.Tag, cnf, showBoosts, showReplies) +func NewTag(ac *api.AccountClient, cnf *config.Config, search string, hideBoosts bool, hideReplies bool) *Feed { + feed := newFeed(ac, config.Tag, cnf, hideBoosts, hideReplies) parts := strings.Split(search, " ") var tparts []string for _, p := range parts { @@ -1050,7 +1050,7 @@ func NewTag(ac *api.AccountClient, cnf *config.Config, search string, showBoosts } func NewTags(ac *api.AccountClient, cnf *config.Config) *Feed { - feed := newFeed(ac, config.Tags, cnf, true, true) + feed := newFeed(ac, config.Tags, cnf, false, false) once := true feed.loadNewer = func() { if once { @@ -1064,7 +1064,7 @@ func NewTags(ac *api.AccountClient, cnf *config.Config) *Feed { } func NewListList(ac *api.AccountClient, cnf *config.Config) *Feed { - feed := newFeed(ac, config.Lists, cnf, true, true) + feed := newFeed(ac, config.Lists, cnf, false, false) once := true feed.loadNewer = func() { if once { @@ -1076,8 +1076,8 @@ func NewListList(ac *api.AccountClient, cnf *config.Config) *Feed { return feed } -func NewList(ac *api.AccountClient, cnf *config.Config, list *mastodon.List, showBoosts bool, showReplies bool) *Feed { - feed := newFeed(ac, config.List, cnf, showBoosts, showReplies) +func NewList(ac *api.AccountClient, cnf *config.Config, list *mastodon.List, hideBoosts bool, hideReplies bool) *Feed { + feed := newFeed(ac, config.List, cnf, hideBoosts, hideReplies) feed.name = list.Title feed.loadNewer = func() { feed.normalNewerID(feed.accountClient.GetListStatuses, list.ID) } feed.loadOlder = func() { feed.normalOlderID(feed.accountClient.GetListStatuses, list.ID) } @@ -1092,7 +1092,7 @@ func NewList(ac *api.AccountClient, cnf *config.Config, list *mastodon.List, sho } func NewUsersInList(ac *api.AccountClient, cnf *config.Config, list *mastodon.List) *Feed { - feed := newFeed(ac, config.ListUsersIn, cnf, true, true) + feed := newFeed(ac, config.ListUsersIn, cnf, false, false) feed.name = list.Title once := true feed.loadNewer = func() { @@ -1107,7 +1107,7 @@ func NewUsersInList(ac *api.AccountClient, cnf *config.Config, list *mastodon.Li } func NewUsersAddList(ac *api.AccountClient, cnf *config.Config, list *mastodon.List) *Feed { - feed := newFeed(ac, config.ListUsersAdd, cnf, true, true) + feed := newFeed(ac, config.ListUsersAdd, cnf, false, false) feed.name = list.Title once := true feed.loadNewer = func() { @@ -1122,7 +1122,7 @@ func NewUsersAddList(ac *api.AccountClient, cnf *config.Config, list *mastodon.L } func NewFavoritesStatus(ac *api.AccountClient, cnf *config.Config, id mastodon.ID) *Feed { - feed := newFeed(ac, config.Favorites, cnf, true, true) + feed := newFeed(ac, config.Favorites, cnf, false, false) once := true feed.loadNewer = func() { if once { @@ -1135,7 +1135,7 @@ func NewFavoritesStatus(ac *api.AccountClient, cnf *config.Config, id mastodon.I } func NewBoosts(ac *api.AccountClient, cnf *config.Config, id mastodon.ID) *Feed { - feed := newFeed(ac, config.Boosts, cnf, true, true) + feed := newFeed(ac, config.Boosts, cnf, false, false) once := true feed.loadNewer = func() { if once { @@ -1148,7 +1148,7 @@ func NewBoosts(ac *api.AccountClient, cnf *config.Config, id mastodon.ID) *Feed } func NewFollowers(ac *api.AccountClient, cnf *config.Config, id mastodon.ID) *Feed { - feed := newFeed(ac, config.Followers, cnf, true, true) + feed := newFeed(ac, config.Followers, cnf, false, false) once := true feed.loadNewer = func() { if once { @@ -1162,7 +1162,7 @@ func NewFollowers(ac *api.AccountClient, cnf *config.Config, id mastodon.ID) *Fe } func NewFollowing(ac *api.AccountClient, cnf *config.Config, id mastodon.ID) *Feed { - feed := newFeed(ac, config.Following, cnf, true, true) + feed := newFeed(ac, config.Following, cnf, false, false) once := true feed.loadNewer = func() { if once { @@ -1176,7 +1176,7 @@ func NewFollowing(ac *api.AccountClient, cnf *config.Config, id mastodon.ID) *Fe } func NewBlocking(ac *api.AccountClient, cnf *config.Config) *Feed { - feed := newFeed(ac, config.Blocking, cnf, true, true) + feed := newFeed(ac, config.Blocking, cnf, false, false) once := true feed.loadNewer = func() { if once { @@ -1190,7 +1190,7 @@ func NewBlocking(ac *api.AccountClient, cnf *config.Config) *Feed { } func NewMuting(ac *api.AccountClient, cnf *config.Config) *Feed { - feed := newFeed(ac, config.Muting, cnf, true, true) + feed := newFeed(ac, config.Muting, cnf, false, false) once := true feed.loadNewer = func() { if once { @@ -1204,7 +1204,7 @@ func NewMuting(ac *api.AccountClient, cnf *config.Config) *Feed { } func NewFollowRequests(ac *api.AccountClient, cnf *config.Config) *Feed { - feed := newFeed(ac, config.FollowRequests, cnf, true, true) + feed := newFeed(ac, config.FollowRequests, cnf, false, false) once := true feed.loadNewer = func() { if once { diff --git a/go.mod b/go.mod index 3f67ba1..54b8ddd 100644 --- a/go.mod +++ b/go.mod @@ -6,18 +6,17 @@ require ( github.com/RasmusLindroth/go-mastodon v0.0.21 github.com/adrg/xdg v0.4.0 github.com/atotto/clipboard v0.1.4 - github.com/gdamore/tcell/v2 v2.5.3 + github.com/gdamore/tcell/v2 v2.5.4 github.com/gen2brain/beeep v0.0.0-20220909211152-5a9ec94374f6 github.com/gobwas/glob v0.2.3 - github.com/icza/gox v0.0.0-20221026131554-a08a8cdc726a + github.com/icza/gox v0.0.0-20230117093757-93f961aa2755 github.com/microcosm-cc/bluemonday v1.0.21 github.com/pelletier/go-toml/v2 v2.0.6 - github.com/rivo/tview v0.0.0-20221221172851-9c04916f4eaa + github.com/rivo/tview v0.0.0-20230104153304-892d1a2eb0da github.com/rivo/uniseg v0.4.3 github.com/spf13/pflag v1.0.5 - golang.org/x/exp v0.0.0-20221230185412-738e83a70c30 - golang.org/x/net v0.4.0 - gopkg.in/ini.v1 v1.67.0 + golang.org/x/exp v0.0.0-20230118134722-a68e582fa157 + golang.org/x/net v0.5.0 mvdan.cc/xurls/v2 v2.4.0 ) @@ -33,7 +32,7 @@ require ( github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af // indirect github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80 // indirect - golang.org/x/sys v0.3.0 // indirect - golang.org/x/term v0.3.0 // indirect - golang.org/x/text v0.5.0 // indirect + golang.org/x/sys v0.4.0 // indirect + golang.org/x/term v0.4.0 // indirect + golang.org/x/text v0.6.0 // indirect ) diff --git a/go.sum b/go.sum index 217e576..8a13cb8 100644 --- a/go.sum +++ b/go.sum @@ -11,8 +11,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko= github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= -github.com/gdamore/tcell/v2 v2.5.3 h1:b9XQrT6QGbgI7JvZOJXFNczOQeIYbo8BfeSMzt2sAV0= -github.com/gdamore/tcell/v2 v2.5.3/go.mod h1:wSkrPaXoiIWZqW/g7Px4xc79di6FTcpB8tvaKJ6uGBo= +github.com/gdamore/tcell/v2 v2.5.4 h1:TGU4tSjD3sCL788vFNeJnTdzpNKIw1H5dgLnJRQVv/k= +github.com/gdamore/tcell/v2 v2.5.4/go.mod h1:dZgRy5v4iMobMEcWNYBtREnDZAT9DYmfqIkrgEMxLyw= github.com/gen2brain/beeep v0.0.0-20220909211152-5a9ec94374f6 h1:jFEK/SA/7E8lg9T33+y8D4Z0I782+bbiEjmyyklRzRQ= github.com/gen2brain/beeep v0.0.0-20220909211152-5a9ec94374f6/go.mod h1:/WeFVhhxMOGypVKS0w8DUJxUBbHypnWkUVnW7p5c9Pw= github.com/go-toast/toast v0.0.0-20190211030409-01e6764cf0a4 h1:qZNfIGkIANxGv/OqtnntR4DfOY2+BgwR60cAcu/i3SE= @@ -25,14 +25,13 @@ github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/icza/gox v0.0.0-20221026131554-a08a8cdc726a h1:ctOSka++0Y+9xF7VLtZ8TOJjyXjOGYywzuhbzj3IEHw= -github.com/icza/gox v0.0.0-20221026131554-a08a8cdc726a/go.mod h1:VbcN86fRkkUMPX2ufM85Um8zFndLZswoIW1eYtpAcVk= +github.com/icza/gox v0.0.0-20230117093757-93f961aa2755 h1:CdxhyIdXDB8Ilp3ogohR8g1omzjKzDsVh5kd71M7Pvc= +github.com/icza/gox v0.0.0-20230117093757-93f961aa2755/go.mod h1:VbcN86fRkkUMPX2ufM85Um8zFndLZswoIW1eYtpAcVk= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= -github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/microcosm-cc/bluemonday v1.0.21 h1:dNH3e4PSyE4vNX+KlRGHT5KrSvjeUkoNPwEORjffHJg= @@ -44,8 +43,8 @@ github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rivo/tview v0.0.0-20221221172851-9c04916f4eaa h1:f5OVRPQnaO1dvCS5UdaSLsf+3RFG49UlQAUE/Jfstp0= -github.com/rivo/tview v0.0.0-20221221172851-9c04916f4eaa/go.mod h1:lBUy/T5kyMudFzWUH/C2moN+NlU5qF505vzOyINXuUQ= +github.com/rivo/tview v0.0.0-20230104153304-892d1a2eb0da h1:3Mh+tcC2KqetuHpWMurDeF+yOgyt4w4qtLIpwSQ3uqo= +github.com/rivo/tview v0.0.0-20230104153304-892d1a2eb0da/go.mod h1:lBUy/T5kyMudFzWUH/C2moN+NlU5qF505vzOyINXuUQ= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.3 h1:utMvzDsuh3suAEnhH0RdHmoPbU648o6CvXxTx4SBMOw= github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= @@ -64,30 +63,46 @@ github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af h1:6yITBqGTE2lEeTPG0 github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af/go.mod h1:4F09kP5F+am0jAwlQLddpoMDM+iewkxxt6nxUQ5nq5o= github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80 h1:nrZ3ySNYwJbSpD6ce9duiP+QkD3JuLCcWkdaehUS/3Y= github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80/go.mod h1:iFyPdL66DjUD96XmzVL3ZntbzcflLnznH0fr99w5VqE= -golang.org/x/exp v0.0.0-20221230185412-738e83a70c30 h1:m9O6OTJ627iFnN2JIWfdqlZCzneRO6EEBsHXI25P8ws= -golang.org/x/exp v0.0.0-20221230185412-738e83a70c30/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= -golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU= -golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/exp v0.0.0-20230118134722-a68e582fa157 h1:fiNkyhJPUvxbRPbCqY/D9qdjmPzfHcpK3P4bM4gioSY= +golang.org/x/exp v0.0.0-20230118134722-a68e582fa157/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw= +golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220318055525-2edf467146b5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= -golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.3.0 h1:qoo4akIqOcDME5bhc/NgxUdovd6BSS2uMsVjB56q1xI= -golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18= +golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.4.0 h1:O7UWfv5+A2qiuulQk30kVinPoMtoIPeVaKLEgLpVkvg= +golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM= golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k= +golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= -gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go index a17e810..fc3ca8b 100644 --- a/main.go +++ b/main.go @@ -1,6 +1,8 @@ package main import ( + "strings" + "github.com/RasmusLindroth/tut/auth" "github.com/RasmusLindroth/tut/config" "github.com/RasmusLindroth/tut/ui" @@ -8,7 +10,9 @@ import ( "github.com/rivo/tview" ) -const version = "1.0.34" +const version = "2.0.0" + +var tutViews []*ui.TutView func main() { util.SetTerminalTitle("tut") @@ -17,32 +21,35 @@ func main() { accs := auth.StartAuth(newUser) app := tview.NewApplication() - t := &ui.Tut{ - App: app, - Config: config.Load(cnfPath, cnfDir), - } - if t.Config.General.MouseSupport { + cnf := config.Load(cnfPath, cnfDir) + + if cnf.General.MouseSupport { app.EnableMouse(true) } tview.Styles = tview.Theme{ - PrimitiveBackgroundColor: t.Config.Style.Background, // background - ContrastBackgroundColor: t.Config.Style.Text, //background for button, checkbox, form, modal - MoreContrastBackgroundColor: t.Config.Style.Text, //background for dropdown - BorderColor: t.Config.Style.Background, //border - TitleColor: t.Config.Style.Text, //titles - GraphicsColor: t.Config.Style.Text, //borders - PrimaryTextColor: t.Config.Style.StatusBarViewBackground, //backround color selected - SecondaryTextColor: t.Config.Style.Text, //text - TertiaryTextColor: t.Config.Style.Text, //list secondary - InverseTextColor: t.Config.Style.Text, //label activated - ContrastSecondaryTextColor: t.Config.Style.Text, //foreground on input and prefix on dropdown + PrimitiveBackgroundColor: cnf.Style.Background, // background + ContrastBackgroundColor: cnf.Style.Text, //background for button, checkbox, form, modal + MoreContrastBackgroundColor: cnf.Style.Text, //background for dropdown + BorderColor: cnf.Style.Background, //border + TitleColor: cnf.Style.Text, //titles + GraphicsColor: cnf.Style.Text, //borders + PrimaryTextColor: cnf.Style.StatusBarViewBackground, //backround color selected + SecondaryTextColor: cnf.Style.Text, //text + TertiaryTextColor: cnf.Style.Text, //list secondary + InverseTextColor: cnf.Style.Text, //label activated + ContrastSecondaryTextColor: cnf.Style.Text, //foreground on input and prefix on dropdown } - main := ui.NewTutView(t, accs, selectedUser) - app.SetInputCapture(main.Input) - if t.Config.General.MouseSupport { - app.SetMouseCapture(main.MouseInput) + ui.SetVars(cnf, app, accs) + users := strings.Fields(selectedUser) + if len(users) > 0 { + for _, user := range strings.Fields(selectedUser) { + ui.NewTutView(user) + } + } else { + ui.NewTutView(selectedUser) } - if err := app.SetRoot(main.View, true).Run(); err != nil { + ui.DoneAdding() + if err := app.Run(); err != nil { panic(err) } } diff --git a/ui/cliview.go b/ui/cliview.go index 9fcf031..7f380b6 100644 --- a/ui/cliview.go +++ b/ui/cliview.go @@ -16,14 +16,14 @@ func CliView(version string) (newUser bool, selectedUser string, confPath string showVersion := pflag.BoolP("version", "v", false, "config path") nu := pflag.BoolP("new-user", "n", false, "add one more user to tut") user := pflag.StringP("user", "u", "", "login directly to user named ``") - cnf := pflag.StringP("config", "c", "", "load config.ini from ``") + cnf := pflag.StringP("config", "c", "", "load config.toml from ``") cnfDir := pflag.StringP("config-dir", "d", "", "load all config from ``") pflag.Parse() if len(os.Args) > 1 { switch os.Args[1] { case "example-config": - config.CreateDefaultConfig("./config.example.ini") + config.CreateDefaultConfig("./config.example.toml") os.Exit(0) } } @@ -69,19 +69,20 @@ func CliView(version string) (newUser bool, selectedUser string, confPath string fmt.Print("\tTo run the program you just have to write tut\n\n") fmt.Print("Commands:\n") - fmt.Print("\texample-config - creates the default configuration file in the current directory and names it ./config.example.ini\n\n") + fmt.Print("\texample-config - creates the default configuration file in the current directory and names it ./config.example.toml\n\n") fmt.Print("Flags:\n") fmt.Print("\t-h --help prints this message\n") fmt.Print("\t-v --version prints the version\n") fmt.Print("\t-n --new-user add one more user to tut\n") - fmt.Print("\t-c --config load config.ini from \n") + fmt.Print("\t-c --config load config.toml from \n") fmt.Print("\t-d --config-dir load all config from \n") - fmt.Print("\t-u --user login directly to user named \n") + fmt.Print("\t-u --user login directly to user named .\n") + fmt.Print("\t\tIf you want to login to multiple accounts seperate them with a space and use quotation marks. E.g. -u \"acc_one acc_two\"\n") fmt.Print("\t\tIf two users are named the same. Use full name like tut@fosstodon.org\n\n") fmt.Print("Configuration:\n") - fmt.Printf("\tThe config is located in XDG_CONFIG_HOME/tut/config.ini which usually equals to ~/.config/tut/config.ini.\n") + fmt.Printf("\tThe config is located in XDG_CONFIG_HOME/tut/config.toml which usually equals to ~/.config/tut/config.toml.\n") fmt.Printf("\tThe program will generate the file the first time you run tut. The file has comments which exmplains what each configuration option does.\n\n") fmt.Print("Contact info for issues or questions:\n") diff --git a/ui/cmdbar.go b/ui/cmdbar.go index a6c6021..6a09e9f 100644 --- a/ui/cmdbar.go +++ b/ui/cmdbar.go @@ -19,7 +19,7 @@ func NewCmdBar(tv *TutView) *CmdBar { View: NewInputField(tv.tut.Config), } c.View.SetAutocompleteFunc(c.Autocomplete) - //c.View.SetAutocompletedFunc(c.Autocompleted) + c.View.SetAutocompletedFunc(c.Autocompleted) c.View.SetDoneFunc(c.DoneFunc) return c @@ -100,28 +100,40 @@ func (c *CmdBar) DoneFunc(key tcell.Key) { case ":newer": c.tutView.LoadNewerCommand() c.Back() + case ":login": + c.tutView.LoginCommand() + c.Back() + case ":next-acct": + c.tutView.NextAcct() + c.Back() + case ":prev-acct": + c.tutView.PrevAcct() + c.Back() case ":clear-notifications": c.tutView.ClearNotificationsCommand() c.Back() - case ":close-window": - c.tutView.CloseWindowCommand() + case ":clear-temp": + c.tutView.ClearTemp() c.Back() - case ":move-window", ":mv": + case ":close-pane": + c.tutView.ClosePaneCommand() + c.Back() + case ":move-pane", ":mp": if len(parts) < 2 { break } switch parts[1] { case "left", "up", "l", "u": - c.tutView.MoveWindowLeft() + c.tutView.MovePaneLeft() c.Back() case "right", "down", "r", "d": - c.tutView.MoveWindowRight() + c.tutView.MovePaneRight() c.Back() case "home", "h": - c.tutView.MoveWindowHome() + c.tutView.MovePaneHome() c.Back() case "end", "e": - c.tutView.MoveWindowEnd() + c.tutView.MovePaneEnd() c.Back() } case ":list-placement": @@ -185,13 +197,13 @@ func (c *CmdBar) DoneFunc(key tcell.Key) { c.tutView.FederatedCommand() c.Back() case "special-all", "sa": - c.tutView.SpecialCommand(true, true) + c.tutView.SpecialCommand(false, false) c.Back() case "special-boosts", "sb": - c.tutView.SpecialCommand(true, false) + c.tutView.SpecialCommand(false, true) c.Back() case "special-replies", "sr": - c.tutView.SpecialCommand(false, true) + c.tutView.SpecialCommand(true, false) c.Back() case "direct", "d": c.tutView.DirectCommand() @@ -222,11 +234,11 @@ func (c *CmdBar) DoneFunc(key tcell.Key) { case ":tags": c.tutView.TagsCommand() c.Back() - case ":window": + case ":pane": if len(parts) < 2 { break } - c.tutView.WindowCommand(parts[1]) + c.tutView.PaneCommand(parts[1]) c.Back() case ":user": if len(parts) < 2 { @@ -237,7 +249,10 @@ func (c *CmdBar) DoneFunc(key tcell.Key) { break } c.tutView.Timeline.AddFeed( - NewUserSearchFeed(c.tutView, user), + NewUserSearchFeed(c.tutView, config.NewTimeline(config.Timeline{ + FeedType: config.UserList, + Subaction: user, + })), c.tutView.tut.Config.General.CommandsInNewPane, ) c.Back() case ":refetch": @@ -279,13 +294,13 @@ func (c *CmdBar) DoneFunc(key tcell.Key) { func (c *CmdBar) Autocomplete(curr string) []string { var entries []string - words := strings.Split(":blocking,:boosts,:bookmarks,:clear-notifications,:compose,:favorites,:favorited,:follow-tag,:followers,:following,:help,:h,:history,:move-window,:lists,:list-placement,:list-split,:muting,:newer,:preferences,:profile,:proportions,:refetch,:requests,:saved,:stick-to-top,:tag,:timeline,:tl,:unfollow-tag,:user,:window,:quit,:q", ",") + words := strings.Split(":blocking,:boosts,:bookmarks,:clear-notifications,:clear-temp,:close-pane,:compose,:favorites,:favorited,:follow-tag,:followers,:following,:help,:h,:history,:move-pane,:next-acct,:lists,:list-placement,:list-split,:login,:muting,:newer,:preferences,:prev-acct,:profile,:proportions,:refetch,:requests,:saved,:stick-to-top,:tag,:timeline,:tl,:unfollow-tag,:user,:pane,:quit,:q", ",") if curr == "" { return entries } if len(curr) > 2 && curr[:3] == ":tl" { - words = strings.Split(":tl home,:tl notifications,:tl local,:tl federated,:tl direct,:tl mentions,:tl favorited,:tl special-all,:tl special-boosts,:tl-special-replies", ",") + words = strings.Split(":tl home,:tl notifications,:tl local,:tl federated,:tl direct,:tl mentions,:tl favorited,:tl special-all,:tl special-boosts,:tl special-replies", ",") } if len(curr) > 8 && curr[:9] == ":timeline" { words = strings.Split(":timeline home,:timeline notifications,:timeline local,:timeline federated,:timeline direct,:timeline mentions,:timeline favorited,:timeline special-all,:timeline special-boosts,:timeline special-replies", ",") @@ -297,8 +312,8 @@ func (c *CmdBar) Autocomplete(curr string) []string { words = strings.Split(":list-split row,:list-split column", ",") } - if len(curr) > 11 && curr[:12] == ":move-window" { - words = strings.Split(":move-window left,:move-window right,:move-window up,:move-window down,:move-window home,:move-window end", ",") + if len(curr) > 11 && curr[:12] == ":move-pane" { + words = strings.Split(":move-pane left,:move-pane right,:move-pane up,:move-pane down,:move-pane home,:move-pane end", ",") } if len(curr) > 2 && curr[:3] == ":mv" { words = strings.Split(":mv left,:mv right,:mv up,:mv down,:mv home,:mv end", ",") @@ -315,12 +330,9 @@ func (c *CmdBar) Autocomplete(curr string) []string { return entries } -/* func (c *CmdBar) Autocompleted(text string, index, source int) bool { if source != tview.AutocompletedNavigate { c.View.SetText(text) } - - return source == tview.AutocompletedEnter || source == tview.AutocompletedClick + return false } -*/ diff --git a/ui/commands.go b/ui/commands.go index 4bb5c44..e975224 100644 --- a/ui/commands.go +++ b/ui/commands.go @@ -4,13 +4,11 @@ import ( "fmt" "os" "strconv" - "strings" "github.com/RasmusLindroth/go-mastodon" "github.com/RasmusLindroth/tut/api" "github.com/RasmusLindroth/tut/config" "github.com/RasmusLindroth/tut/util" - "golang.org/x/exp/slices" ) func (tv *TutView) ComposeCommand() { @@ -35,91 +33,124 @@ func (tv *TutView) EditCommand() { func (tv *TutView) BlockingCommand() { tv.Timeline.AddFeed( - NewBlocking(tv), + NewBlocking(tv, config.NewTimeline(config.Timeline{ + FeedType: config.Blocking, + })), tv.tut.Config.General.CommandsInNewPane, ) } func (tv *TutView) BookmarksCommand() { tv.Timeline.AddFeed( - NewBookmarksFeed(tv), - ) + NewBookmarksFeed(tv, config.NewTimeline(config.Timeline{ + FeedType: config.Saved, + })), + tv.tut.Config.General.CommandsInNewPane) } func (tv *TutView) FavoritedCommand() { tv.Timeline.AddFeed( - NewFavoritedFeed(tv), - ) + NewFavoritedFeed(tv, config.NewTimeline(config.Timeline{ + FeedType: config.Favorited, + })), + tv.tut.Config.General.CommandsInNewPane) } func (tv *TutView) MutingCommand() { tv.Timeline.AddFeed( - NewMuting(tv), - ) + NewMuting(tv, config.NewTimeline(config.Timeline{ + FeedType: config.Muting, + })), + tv.tut.Config.General.CommandsInNewPane) } func (tv *TutView) FollowRequestsCommand() { tv.Timeline.AddFeed( - NewFollowRequests(tv), - ) + NewFollowRequests(tv, config.NewTimeline(config.Timeline{ + FeedType: config.FollowRequests, + })), + tv.tut.Config.General.CommandsInNewPane) } func (tv *TutView) LocalCommand() { tv.Timeline.AddFeed( - NewLocalFeed(tv, true, true), - ) + NewLocalFeed(tv, config.NewTimeline(config.Timeline{ + FeedType: config.TimelineLocal, + })), + tv.tut.Config.General.CommandsInNewPane) } func (tv *TutView) FederatedCommand() { tv.Timeline.AddFeed( - NewFederatedFeed(tv, true, true), - ) + NewFederatedFeed(tv, config.NewTimeline(config.Timeline{ + FeedType: config.TimelineFederated, + })), + tv.tut.Config.General.CommandsInNewPane) } -func (tv *TutView) SpecialCommand(boosts, replies bool) { +func (tv *TutView) SpecialCommand(hideBoosts, hideReplies bool) { tv.Timeline.AddFeed( - NewHomeSpecialFeed(tv, boosts, replies), - ) + NewHomeSpecialFeed(tv, config.NewTimeline(config.Timeline{ + FeedType: config.TimelineHomeSpecial, + HideBoosts: hideBoosts, + HideReplies: hideReplies, + })), + tv.tut.Config.General.CommandsInNewPane) } func (tv *TutView) DirectCommand() { tv.Timeline.AddFeed( - NewConversationsFeed(tv), - ) + NewConversationsFeed(tv, config.NewTimeline(config.Timeline{ + FeedType: config.Conversations, + })), + tv.tut.Config.General.CommandsInNewPane) } func (tv *TutView) HomeCommand() { tv.Timeline.AddFeed( - NewHomeFeed(tv, true, true), - ) + NewHomeFeed(tv, config.NewTimeline(config.Timeline{ + FeedType: config.TimelineHome, + })), + tv.tut.Config.General.CommandsInNewPane) } func (tv *TutView) NotificationsCommand() { tv.Timeline.AddFeed( - NewNotificationFeed(tv, true, true), - ) + NewNotificationFeed(tv, config.NewTimeline(config.Timeline{ + FeedType: config.Notifications, + })), + tv.tut.Config.General.CommandsInNewPane) } func (tv *TutView) MentionsCommand() { tv.Timeline.AddFeed( - NewNotificatioMentionsFeed(tv, true, true), - ) + NewNotificatioMentionsFeed(tv, config.NewTimeline(config.Timeline{ + FeedType: config.Mentions, + })), + tv.tut.Config.General.CommandsInNewPane) } func (tv *TutView) ListsCommand() { tv.Timeline.AddFeed( - NewListsFeed(tv), - ) + NewListsFeed(tv, config.NewTimeline(config.Timeline{ + FeedType: config.Lists, + })), + tv.tut.Config.General.CommandsInNewPane) } func (tv *TutView) TagCommand(tag string) { tv.Timeline.AddFeed( - NewTagFeed(tv, tag, true, true), - ) + NewTagFeed(tv, config.NewTimeline(config.Timeline{ + FeedType: config.Tag, + Subaction: tag, + })), + tv.tut.Config.General.CommandsInNewPane) } func (tv *TutView) TagsCommand() { tv.Timeline.AddFeed( - NewTagsFeed(tv), - ) + NewTagsFeed(tv, config.NewTimeline(config.Timeline{ + FeedType: config.Tags, + })), + tv.tut.Config.General.CommandsInNewPane) } func (tv *TutView) TagFollowCommand(tag string) { @@ -138,7 +169,7 @@ func (tv *TutView) TagUnfollowCommand(tag string) { } } -func (tv *TutView) WindowCommand(index string) { +func (tv *TutView) PaneCommand(index string) { i, err := strconv.Atoi(index) if err != nil { tv.ShowError( @@ -146,119 +177,27 @@ func (tv *TutView) WindowCommand(index string) { ) return } - tv.FocusFeed(i) + tv.FocusFeed(i, nil) } -func (tv *TutView) MoveWindowLeft() { - tv.Timeline.MoveCurrentWindowLeft() +func (tv *TutView) MovePaneLeft() { + tv.Timeline.MoveCurrentPaneLeft() } -func (tv *TutView) MoveWindowRight() { - tv.Timeline.MoveCurrentWindowRight() +func (tv *TutView) MovePaneRight() { + tv.Timeline.MoveCurrentPaneRight() } -func (tv *TutView) MoveWindowHome() { - tv.Timeline.MoveCurrentWindowHome() +func (tv *TutView) MovePaneHome() { + tv.Timeline.MoveCurrentPaneHome() } -func (tv *TutView) MoveWindowEnd() { - tv.Timeline.MoveCurrentWindowEnd() +func (tv *TutView) MovePaneEnd() { + tv.Timeline.MoveCurrentPaneEnd() } -func (tv *TutView) SwitchCommand(s string) { - ft := config.InvalidFeed - - parts := strings.Split(s, ",") - for i, p := range parts { - parts[i] = strings.TrimSpace(p) - } - cmd := parts[0] - var subaction string - if strings.Contains(parts[0], " ") { - p := strings.Split(cmd, " ") - cmd = p[0] - subaction = strings.Join(p[1:], " ") - } - showBoosts := true - showReplies := true - name := "" - if len(parts) > 1 { - tfStr := []string{"true", "false"} - name = parts[1] - if slices.Contains(tfStr, name) { - name = "" - } - if len(parts) > 2 && slices.Contains(tfStr, parts[len(parts)-2]) && - slices.Contains(tfStr, parts[len(parts)-1]) { - showBoosts = parts[len(parts)-2] == "true" - showReplies = parts[len(parts)-1] == "true" - } else if len(parts) > 1 && slices.Contains(tfStr, parts[len(parts)-1]) { - showBoosts = parts[len(parts)-1] == "true" - } else if name == "" { - fmt.Printf("switch is invalid . Check this for errors: switch %s\n", s) - os.Exit(1) - } - } - var data string - switch cmd { - case "home": - ft = config.TimelineHome - case "direct": - ft = config.Conversations - case "local": - ft = config.TimelineLocal - case "federated": - ft = config.TimelineFederated - case "special": - ft = config.TimelineHomeSpecial - case "special-all": - ft = config.TimelineHomeSpecial - showBoosts = true - showReplies = true - case "special-boosts": - ft = config.TimelineHomeSpecial - showBoosts = true - showReplies = false - case "special-replies": - ft = config.TimelineHomeSpecial - showBoosts = false - showReplies = true - case "bookmarks", "saved": - ft = config.Saved - case "favorited": - ft = config.Favorited - case "notifications": - ft = config.Notifications - case "lists": - ft = config.Lists - case "tag": - ft = config.Tag - data = subaction - case "blocking": - ft = config.Blocking - case "muting": - ft = config.Muting - case "tags": - ft = config.Tags - case "mentions": - ft = config.Mentions - } - found := tv.Timeline.FindAndGoTo(ft, data, showBoosts, showReplies) - if found { - return - } - nf := CreateFeed(tv, ft, data, showBoosts, showReplies) - tv.Timeline.Feeds = append(tv.Timeline.Feeds, &FeedHolder{ - Feeds: []*Feed{nf}, - Name: name, - }) - tv.FocusFeed(len(tv.Timeline.Feeds) - 1) - tv.Shared.Top.SetText(tv.Timeline.GetTitle()) - tv.Timeline.update <- true -} - -func (tv *TutView) CloseWindowCommand() { - tv.Timeline.CloseCurrentWindow() +func (tv *TutView) ClosePaneCommand() { + tv.Timeline.CloseCurrentPane() } func (tv *TutView) BoostsCommand() { @@ -272,8 +211,12 @@ func (tv *TutView) BoostsCommand() { s := item.Raw().(*mastodon.Status) s = util.StatusOrReblog(s) tv.Timeline.AddFeed( - NewBoosts(tv, s.ID), - ) + NewBoosts(tv, s.ID, config.NewTimeline(config.Timeline{ + FeedType: config.Boosts, + HideBoosts: false, + HideReplies: true, + })), + tv.tut.Config.General.CommandsInNewPane) } func (tv *TutView) FavoritesCommand() { @@ -287,8 +230,10 @@ func (tv *TutView) FavoritesCommand() { s := item.Raw().(*mastodon.Status) s = util.StatusOrReblog(s) tv.Timeline.AddFeed( - NewFavoritesStatus(tv, s.ID), - ) + NewFavoritesStatus(tv, s.ID, config.NewTimeline(config.Timeline{ + FeedType: config.Favorites, + })), + tv.tut.Config.General.CommandsInNewPane) } func (tv *TutView) FollowingCommand() { @@ -301,8 +246,10 @@ func (tv *TutView) FollowingCommand() { } s := item.Raw().(*api.User) tv.Timeline.AddFeed( - NewFollowing(tv, s.Data.ID), - ) + NewFollowing(tv, s.Data.ID, config.NewTimeline(config.Timeline{ + FeedType: config.Following, + })), + tv.tut.Config.General.CommandsInNewPane) } func (tv *TutView) FollowersCommand() { @@ -315,8 +262,10 @@ func (tv *TutView) FollowersCommand() { } s := item.Raw().(*api.User) tv.Timeline.AddFeed( - NewFollowers(tv, s.Data.ID), - ) + NewFollowers(tv, s.Data.ID, config.NewTimeline(config.Timeline{ + FeedType: config.Followers, + })), + tv.tut.Config.General.CommandsInNewPane) } func (tv *TutView) HistoryCommand() { @@ -328,8 +277,10 @@ func (tv *TutView) HistoryCommand() { return } tv.Timeline.AddFeed( - NewHistoryFeed(tv, item), - ) + NewHistoryFeed(tv, item, config.NewTimeline(config.Timeline{ + FeedType: config.History, + })), + tv.tut.Config.General.CommandsInNewPane) } func (tv *TutView) ProfileCommand() { @@ -339,8 +290,10 @@ func (tv *TutView) ProfileCommand() { return } tv.Timeline.AddFeed( - NewUserFeed(tv, item), - ) + NewUserFeed(tv, item, config.NewTimeline(config.Timeline{ + FeedType: config.User, + })), + tv.tut.Config.General.CommandsInNewPane) } func (tv *TutView) PreferencesCommand() { @@ -378,6 +331,17 @@ func (tv *TutView) LoadNewerCommand() { f.LoadNewer(true) } +func (tv *TutView) LoginCommand() { + NewTutView("") +} + +func (tv *TutView) NextAcct() { + TutViews.Next() +} +func (tv *TutView) PrevAcct() { + TutViews.Prev() +} + func (tv *TutView) ClearNotificationsCommand() { err := tv.tut.Client.ClearNotifications() if err != nil { @@ -393,6 +357,17 @@ func (tv *TutView) ClearNotificationsCommand() { } } +func (tv *TutView) ClearTemp() { + if !tv.tut.Config.Media.DeleteTmpFiles { + for _, t := range TutViews.Views { + for _, f := range t.FileList { + os.Remove(f) + } + t.FileList = []string{} + } + } +} + func (tv *TutView) ToggleStickToTop() { tv.tut.Config.General.StickToTop = !tv.tut.Config.General.StickToTop } diff --git a/ui/composeview.go b/ui/composeview.go index fc92dcc..f35de89 100644 --- a/ui/composeview.go +++ b/ui/composeview.go @@ -33,17 +33,19 @@ type msgToot struct { } type ComposeView struct { - tutView *TutView - shared *Shared - View *tview.Flex - content *tview.TextView - input *MediaInput - info *tview.TextView - controls *tview.Flex - visibility *tview.DropDown - lang *tview.DropDown - media *MediaList - msg *msgToot + tutView *TutView + shared *Shared + View *tview.Flex + content *tview.TextView + textAreaMain *tview.TextArea + textAreaCW *tview.TextArea + input *MediaInput + info *tview.TextView + controls *tview.Flex + visibility *tview.DropDown + lang *tview.DropDown + media *MediaList + msg *msgToot } var visibilities = map[string]int{ @@ -61,15 +63,17 @@ var visibilitiesStr = []string{ func NewComposeView(tv *TutView) *ComposeView { cv := &ComposeView{ - tutView: tv, - shared: tv.Shared, - content: NewTextView(tv.tut.Config), - input: NewMediaInput(tv), - controls: NewControlView(tv.tut.Config), - info: NewTextView(tv.tut.Config), - visibility: NewDropDown(tv.tut.Config), - lang: NewDropDown(tv.tut.Config), - media: NewMediaList(tv), + tutView: tv, + shared: tv.Shared, + content: NewTextView(tv.tut.Config), + textAreaMain: NewTextArea(tv.tut.Config), + textAreaCW: NewTextArea(tv.tut.Config), + input: NewMediaInput(tv), + controls: NewControlView(tv.tut.Config), + info: NewTextView(tv.tut.Config), + visibility: NewDropDown(tv.tut.Config), + lang: NewDropDown(tv.tut.Config), + media: NewMediaList(tv), } cv.content.SetDynamicColors(true) cv.View = newComposeUI(cv) @@ -81,18 +85,41 @@ func newComposeUI(cv *ComposeView) *tview.Flex { if cv.tutView.tut.Config.General.TerminalTitle < 2 { r.AddItem(cv.tutView.Shared.Top.View, 1, 0, false) } - r.AddItem(tview.NewFlex().SetDirection(tview.FlexColumn). - AddItem(tview.NewFlex().SetDirection(tview.FlexRow). - AddItem(cv.content, 0, 2, false), 0, 2, false). - AddItem(tview.NewBox(), 2, 0, false). - AddItem(tview.NewFlex().SetDirection(tview.FlexRow). - AddItem(cv.visibility, 1, 0, false). - AddItem(cv.lang, 1, 0, false). - AddItem(cv.info, 5, 0, false). - AddItem(cv.media.View, 0, 1, false), 0, 1, false), 0, 1, false). - AddItem(cv.input.View, 1, 0, false). - AddItem(cv.controls, 1, 0, false). - AddItem(cv.tutView.Shared.Bottom.View, 2, 0, false) + if !cv.tutView.tut.Config.General.UseInternalEditor { + r.AddItem(tview.NewFlex().SetDirection(tview.FlexColumn). + AddItem(tview.NewFlex().SetDirection(tview.FlexRow). + AddItem(cv.content, 0, 2, false), 0, 2, false). + AddItem(tview.NewBox(), 2, 0, false). + AddItem(tview.NewFlex().SetDirection(tview.FlexRow). + AddItem(cv.visibility, 1, 0, false). + AddItem(cv.lang, 1, 0, false). + AddItem(cv.info, 5, 0, false). + AddItem(cv.media.View, 0, 1, false), 0, 1, false), 0, 1, false). + AddItem(cv.input.View, 1, 0, false). + AddItem(cv.controls, 1, 0, false). + AddItem(cv.tutView.Shared.Bottom.View, 2, 0, false) + } else { + txtOne := NewTextView(cv.tutView.tut.Config) + txtOne.SetText("Content warning text:") + txtTwo := NewTextView(cv.tutView.tut.Config) + txtTwo.SetText("Main content:") + r.AddItem(tview.NewFlex().SetDirection(tview.FlexColumn). + AddItem(tview.NewFlex().SetDirection(tview.FlexRow). + AddItem(cv.content, 3, 0, false). + AddItem(txtOne, 1, 0, false). + AddItem(cv.textAreaCW, 0, 1, false). + AddItem(txtTwo, 1, 0, false). + AddItem(cv.textAreaMain, 0, 2, false), 0, 2, false). + AddItem(tview.NewBox(), 2, 0, false). + AddItem(tview.NewFlex().SetDirection(tview.FlexRow). + AddItem(cv.visibility, 1, 0, false). + AddItem(cv.lang, 1, 0, false). + AddItem(cv.info, 5, 0, false). + AddItem(cv.media.View, 0, 1, false), 0, 1, false), 0, 1, false). + AddItem(cv.input.View, 1, 0, false). + AddItem(cv.controls, 1, 0, false). + AddItem(cv.tutView.Shared.Bottom.View, 2, 0, false) + } return r } @@ -167,6 +194,8 @@ func (cv *ComposeView) SetControls(ctrl ComposeControls) { func (cv *ComposeView) SetStatus(reply *mastodon.Status, edit *mastodon.Status) error { cv.tutView.PollView.Reset() cv.media.Reset() + cv.textAreaMain.SetText("", false) + cv.textAreaCW.SetText("", false) msg := &msgToot{} me := cv.tutView.tut.Client.Me visibility := mastodon.VisibilityPublic @@ -250,6 +279,10 @@ func (cv *ComposeView) SetStatus(reply *mastodon.Status, edit *mastodon.Status) cv.lang.SetOptions(langStrs, cv.langSelected) cv.lang.SetCurrentOption(index) + if cv.tutView.tut.Config.General.UseInternalEditor { + cv.textAreaMain.SetText(cv.msg.Text, true) + cv.textAreaCW.SetText(cv.msg.CWText, true) + } cv.UpdateContent() cv.SetControls(ComposeNormal) return nil @@ -275,27 +308,35 @@ func (cv *ComposeView) getAccs() string { } func (cv *ComposeView) EditText() { - text, err := OpenEditor(cv.tutView, cv.msg.Text) - if err != nil { - cv.tutView.ShowError( - fmt.Sprintf("Couldn't open editor. Error: %v", err), - ) - return + if !cv.tutView.tut.Config.General.UseInternalEditor { + text, err := OpenEditor(cv.tutView, cv.msg.Text) + if err != nil { + cv.tutView.ShowError( + fmt.Sprintf("Couldn't open editor. Error: %v", err), + ) + return + } + cv.msg.Text = text + cv.UpdateContent() + } else { + cv.textAreaMainFocus() } - cv.msg.Text = text - cv.UpdateContent() } func (cv *ComposeView) EditSpoiler() { - text, err := OpenEditor(cv.tutView, cv.msg.CWText) - if err != nil { - cv.tutView.ShowError( - fmt.Sprintf("Couldn't open editor. Error: %v", err), - ) - return + if !cv.tutView.tut.Config.General.UseInternalEditor { + text, err := OpenEditor(cv.tutView, cv.msg.CWText) + if err != nil { + cv.tutView.ShowError( + fmt.Sprintf("Couldn't open editor. Error: %v", err), + ) + return + } + cv.msg.CWText = text + cv.UpdateContent() + } else { + cv.textAreaCWFocus() } - cv.msg.CWText = text - cv.UpdateContent() } func (cv *ComposeView) ToggleCW() { @@ -315,26 +356,29 @@ func (cv *ComposeView) UpdateContent() { if cv.msg.Reply != nil { var acct string if cv.msg.Reply.Account.DisplayName != "" { - acct = fmt.Sprintf("%s (%s)\n", cv.msg.Reply.Account.DisplayName, cv.msg.Reply.Account.Acct) + acct = fmt.Sprintf("%s (%s)", cv.msg.Reply.Account.DisplayName, cv.msg.Reply.Account.Acct) } else { - acct = fmt.Sprintf("%s\n", cv.msg.Reply.Account.Acct) + acct = cv.msg.Reply.Account.Acct } outputHead += subtleColor + "Replying to " + tview.Escape(acct) + "\n" + normal } if cv.msg.CWText != "" && !cv.msg.Sensitive { outputHead += warningColor + "You have entered content warning text, but haven't set an content warning. Do it by pressing " + tview.Escape("[T]") + "\n\n" + normal } - if cv.msg.Sensitive && cv.msg.CWText == "" { outputHead += warningColor + "You have added an content warning, but haven't set any text above the hidden text. Do it by pressing " + tview.Escape("[C]") + "\n\n" + normal } - if cv.msg.Sensitive && cv.msg.CWText != "" { - outputHead += subtleColor + "Content warning\n\n" + normal - outputHead += tview.Escape(cv.msg.CWText) - outputHead += "\n\n" + subtleColor + "---hidden content below---\n\n" + normal + if !cv.tutView.tut.Config.General.UseInternalEditor { + if cv.msg.Sensitive && cv.msg.CWText != "" { + outputHead += subtleColor + "Content warning\n\n" + normal + outputHead += tview.Escape(cv.msg.CWText) + outputHead += "\n\n" + subtleColor + "---hidden content below---\n\n" + normal + } + output = outputHead + normal + tview.Escape(cv.msg.Text) + } else { + output = strings.TrimSpace(outputHead) } - output = outputHead + normal + tview.Escape(cv.msg.Text) cv.content.SetText(output) } @@ -354,12 +398,51 @@ func (cv *ComposeView) IncludeQuote() { for _, line := range strings.Split(tootText, "\n") { t += "> " + line + "\n" } - t += "\n" cv.msg.Text = t + if cv.tutView.tut.Config.General.UseInternalEditor { + cv.textAreaMain.SetText(cv.msg.Text, false) + } cv.msg.QuoteIncluded = true cv.UpdateContent() } +func (cv *ComposeView) textAreaInput(event *tcell.EventKey) *tcell.EventKey { + if cv.tutView.tut.Config.Input.GlobalBack.Match(event.Key(), rune(-1)) { + cv.exitTextAreaInput() + return nil + } + switch key := event.Key(); key { + case tcell.KeyCtrlQ: + return nil + case tcell.KeyCtrlC: + return tcell.NewEventKey(tcell.KeyCtrlQ, rune(0), tcell.ModNone) + } + return event +} + +func (cv *ComposeView) exitTextAreaInput() { + cv.tutView.tut.App.SetInputCapture(cv.tutView.Input) + cv.tutView.tut.App.SetFocus(cv.content) +} + +func (cv *ComposeView) textAreaMainFocus() { + cv.tutView.tut.App.SetInputCapture(cv.textAreaInput) + cv.textAreaMain.SetChangedFunc(func() { + cv.msg.Text = cv.textAreaMain.GetText() + cv.UpdateContent() + }) + cv.tutView.tut.App.SetFocus(cv.textAreaMain) +} + +func (cv *ComposeView) textAreaCWFocus() { + cv.tutView.tut.App.SetInputCapture(cv.textAreaInput) + cv.textAreaCW.SetChangedFunc(func() { + cv.msg.CWText = cv.textAreaCW.GetText() + cv.UpdateContent() + }) + cv.tutView.tut.App.SetFocus(cv.textAreaCW) +} + func (cv *ComposeView) HasMedia() bool { return len(cv.media.Files) > 0 } @@ -650,14 +733,29 @@ func (m *MediaList) EditDesc() { ) return } - desc, err := OpenEditor(m.tutView, file.Description) + var desc string + var err error + if m.tutView.tut.Config.General.UseInternalEditor { + m.tutView.EditorView.Init(file.Description, 0, true, func(input string) { + m.editDesc(input, nil) + }) + return + } else { + desc, err = OpenEditor(m.tutView, file.Description) + m.editDesc(desc, err) + } +} + +func (m *MediaList) editDesc(text string, err error) { + index := m.list.GetCurrentItem() + file := m.Files[index] if err != nil { m.tutView.ShowError( fmt.Sprintf("Couldn't edit description. Error: %v\n", err), ) return } - file.Description = desc + file.Description = text m.Files[index] = file m.Draw() } diff --git a/ui/editorview.go b/ui/editorview.go new file mode 100644 index 0000000..bc0aa09 --- /dev/null +++ b/ui/editorview.go @@ -0,0 +1,94 @@ +package ui + +import ( + "fmt" + + "github.com/gdamore/tcell/v2" + "github.com/rivo/tview" + "github.com/rivo/uniseg" +) + +type EditorView struct { + tutView *TutView + shared *Shared + View *tview.Flex + editor *tview.TextArea + controls *tview.Flex + info *tview.TextView + limit int + prevPage string + prevFocus tview.Primitive + prevInput func(event *tcell.EventKey) *tcell.EventKey + exitFunc func(string) +} + +func NewEditorView(tv *TutView) *EditorView { + e := &EditorView{ + tutView: tv, + shared: tv.Shared, + editor: NewTextArea(tv.tut.Config), + controls: NewControlView(tv.tut.Config), + info: NewTextView(tv.tut.Config), + } + item := NewControl(tv.tut.Config, tv.tut.Config.Input.EditorExit, true) + e.controls.AddItem( + NewControlButton(tv, item), item.Len, 0, false) + e.View = editorViewUI(e) + return e +} + +func editorViewUI(e *EditorView) *tview.Flex { + r := tview.NewFlex().SetDirection(tview.FlexRow) + if e.tutView.tut.Config.General.TerminalTitle < 2 { + r.AddItem(e.shared.Top.View, 1, 0, false) + } + if e.limit > 0 { + r.AddItem(e.info, 1, 0, false). + AddItem(e.editor, 0, 1, false). + AddItem(e.controls, 1, 0, false). + AddItem(e.shared.Bottom.View, 2, 0, false) + } else { + r.AddItem(e.editor, 0, 1, false). + AddItem(e.controls, 1, 0, false). + AddItem(e.shared.Bottom.View, 2, 0, false) + } + return r +} + +func (e *EditorView) Init(text string, textLimit int, setReturn bool, exit func(string)) { + e.editor.SetText(text, true) + e.limit = textLimit + *e.View = *editorViewUI(e) + e.exitFunc = exit + if setReturn { + e.prevPage, _ = e.tutView.View.GetFrontPage() + e.prevFocus = e.tutView.tut.App.GetFocus() + e.prevInput = e.tutView.tut.App.GetInputCapture() + } + e.info.SetText("") + e.editor.SetChangedFunc(e.updateInfo) + e.updateInfo() + e.tutView.View.HidePage(e.prevPage) + e.tutView.View.ShowPage("editor") + e.tutView.tut.App.SetInputCapture(e.tutView.InputEditorView) + e.tutView.tut.App.SetFocus(e.editor) +} + +func (e *EditorView) updateInfo() { + if e.limit > 0 { + content := e.editor.GetText() + charCount := uniseg.GraphemeClusterCount(content) + charsLeft := e.limit - charCount + e.info.SetText( + fmt.Sprintf("Chars left: %d", charsLeft), + ) + } +} + +func (e *EditorView) ExitTextAreaInput() { + e.tutView.View.HidePage("editor") + e.tutView.View.ShowPage(e.prevPage) + e.tutView.tut.App.SetInputCapture(e.prevInput) + e.tutView.tut.App.SetFocus(e.prevFocus) + e.exitFunc(e.editor.GetText()) +} diff --git a/ui/feed.go b/ui/feed.go index f260651..30ae6e8 100644 --- a/ui/feed.go +++ b/ui/feed.go @@ -45,12 +45,11 @@ func outFocus(l *tview.List, style config.Style) { } type Feed struct { - tutView *TutView - Data *feed.Feed - List *FeedList - Content *FeedContent - ShowBoosts bool - ShowReplies bool + tutView *TutView + Data *feed.Feed + List *FeedList + Content *FeedContent + Timeline *config.Timeline } func (f *Feed) ListInFocus() { @@ -141,113 +140,106 @@ func (f *Feed) update() { } } -func NewHomeFeed(tv *TutView, showBoosts bool, showReplies bool) *Feed { - f := feed.NewTimelineHome(tv.tut.Client, tv.tut.Config, showBoosts, showReplies) +func NewHomeFeed(tv *TutView, tl *config.Timeline) *Feed { + f := feed.NewTimelineHome(tv.tut.Client, tv.tut.Config, tl.HideBoosts, tl.HideReplies) f.LoadNewer() fd := &Feed{ - tutView: tv, - Data: f, - List: NewFeedList(tv.tut, f.StickyCount()), - Content: NewFeedContent(tv.tut), - ShowBoosts: showBoosts, - ShowReplies: showReplies, + tutView: tv, + Data: f, + List: NewFeedList(tv.tut, f.StickyCount()), + Content: NewFeedContent(tv.tut), + Timeline: tl, } go fd.update() return fd } -func NewHomeSpecialFeed(tv *TutView, showBoosts bool, showReplies bool) *Feed { - f := feed.NewTimelineHomeSpecial(tv.tut.Client, tv.tut.Config, showBoosts, showReplies) +func NewHomeSpecialFeed(tv *TutView, tl *config.Timeline) *Feed { + f := feed.NewTimelineHomeSpecial(tv.tut.Client, tv.tut.Config, tl.HideBoosts, tl.HideReplies) f.LoadNewer() fd := &Feed{ - tutView: tv, - Data: f, - List: NewFeedList(tv.tut, f.StickyCount()), - Content: NewFeedContent(tv.tut), - ShowBoosts: showBoosts, - ShowReplies: showReplies, + tutView: tv, + Data: f, + List: NewFeedList(tv.tut, f.StickyCount()), + Content: NewFeedContent(tv.tut), + Timeline: tl, } go fd.update() return fd } -func NewFederatedFeed(tv *TutView, showBoosts bool, showReplies bool) *Feed { - f := feed.NewTimelineFederated(tv.tut.Client, tv.tut.Config, showBoosts, showReplies) +func NewFederatedFeed(tv *TutView, tl *config.Timeline) *Feed { + f := feed.NewTimelineFederated(tv.tut.Client, tv.tut.Config, tl.HideBoosts, tl.HideReplies) f.LoadNewer() fd := &Feed{ - tutView: tv, - Data: f, - List: NewFeedList(tv.tut, f.StickyCount()), - Content: NewFeedContent(tv.tut), - ShowBoosts: showBoosts, - ShowReplies: showReplies, + tutView: tv, + Data: f, + List: NewFeedList(tv.tut, f.StickyCount()), + Content: NewFeedContent(tv.tut), + Timeline: tl, } go fd.update() return fd } -func NewLocalFeed(tv *TutView, showBoosts bool, showReplies bool) *Feed { - f := feed.NewTimelineLocal(tv.tut.Client, tv.tut.Config, showBoosts, showReplies) +func NewLocalFeed(tv *TutView, tl *config.Timeline) *Feed { + f := feed.NewTimelineLocal(tv.tut.Client, tv.tut.Config, tl.HideBoosts, tl.HideReplies) f.LoadNewer() fd := &Feed{ - tutView: tv, - Data: f, - List: NewFeedList(tv.tut, f.StickyCount()), - Content: NewFeedContent(tv.tut), - ShowBoosts: showBoosts, - ShowReplies: showReplies, + tutView: tv, + Data: f, + List: NewFeedList(tv.tut, f.StickyCount()), + Content: NewFeedContent(tv.tut), + Timeline: tl, } go fd.update() return fd } -func NewNotificationFeed(tv *TutView, showBoosts bool, showReplies bool) *Feed { - f := feed.NewNotifications(tv.tut.Client, tv.tut.Config, showBoosts, showReplies) +func NewNotificationFeed(tv *TutView, tl *config.Timeline) *Feed { + f := feed.NewNotifications(tv.tut.Client, tv.tut.Config, tl.HideBoosts, tl.HideReplies) f.LoadNewer() fd := &Feed{ - tutView: tv, - Data: f, - List: NewFeedList(tv.tut, f.StickyCount()), - Content: NewFeedContent(tv.tut), - ShowBoosts: showBoosts, - ShowReplies: showReplies, + tutView: tv, + Data: f, + List: NewFeedList(tv.tut, f.StickyCount()), + Content: NewFeedContent(tv.tut), + Timeline: tl, } go fd.update() return fd } -func NewNotificatioMentionsFeed(tv *TutView, showBoosts bool, showReplies bool) *Feed { +func NewNotificatioMentionsFeed(tv *TutView, tl *config.Timeline) *Feed { f := feed.NewNotificationsMentions(tv.tut.Client, tv.tut.Config) f.LoadNewer() fd := &Feed{ - tutView: tv, - Data: f, - List: NewFeedList(tv.tut, f.StickyCount()), - Content: NewFeedContent(tv.tut), - ShowBoosts: showBoosts, - ShowReplies: showReplies, + tutView: tv, + Data: f, + List: NewFeedList(tv.tut, f.StickyCount()), + Content: NewFeedContent(tv.tut), + Timeline: tl, } go fd.update() return fd } -func NewThreadFeed(tv *TutView, item api.Item) *Feed { +func NewThreadFeed(tv *TutView, item api.Item, tl *config.Timeline) *Feed { status := util.StatusOrReblog(item.Raw().(*mastodon.Status)) f := feed.NewThread(tv.tut.Client, tv.tut.Config, status) f.LoadNewer() fd := &Feed{ - tutView: tv, - Data: f, - List: NewFeedList(tv.tut, f.StickyCount()), - Content: NewFeedContent(tv.tut), - ShowBoosts: true, - ShowReplies: true, + tutView: tv, + Data: f, + List: NewFeedList(tv.tut, f.StickyCount()), + Content: NewFeedContent(tv.tut), + Timeline: tl, } for i, s := range f.List() { main, symbol := DrawListItem(tv.tut.Config, s) @@ -261,17 +253,16 @@ func NewThreadFeed(tv *TutView, item api.Item) *Feed { return fd } -func NewHistoryFeed(tv *TutView, item api.Item) *Feed { +func NewHistoryFeed(tv *TutView, item api.Item, tl *config.Timeline) *Feed { status := util.StatusOrReblog(item.Raw().(*mastodon.Status)) f := feed.NewHistory(tv.tut.Client, tv.tut.Config, status) f.LoadNewer() fd := &Feed{ - tutView: tv, - Data: f, - List: NewFeedList(tv.tut, f.StickyCount()), - Content: NewFeedContent(tv.tut), - ShowBoosts: true, - ShowReplies: true, + tutView: tv, + Data: f, + List: NewFeedList(tv.tut, f.StickyCount()), + Content: NewFeedContent(tv.tut), + Timeline: tl, } for _, s := range f.List() { main, symbol := DrawListItem(tv.tut.Config, s) @@ -283,23 +274,22 @@ func NewHistoryFeed(tv *TutView, item api.Item) *Feed { return fd } -func NewConversationsFeed(tv *TutView) *Feed { +func NewConversationsFeed(tv *TutView, tl *config.Timeline) *Feed { f := feed.NewConversations(tv.tut.Client, tv.tut.Config) f.LoadNewer() fd := &Feed{ - tutView: tv, - Data: f, - List: NewFeedList(tv.tut, f.StickyCount()), - Content: NewFeedContent(tv.tut), - ShowBoosts: true, - ShowReplies: true, + tutView: tv, + Data: f, + List: NewFeedList(tv.tut, f.StickyCount()), + Content: NewFeedContent(tv.tut), + Timeline: tl, } go fd.update() return fd } -func NewUserFeed(tv *TutView, item api.Item) *Feed { +func NewUserFeed(tv *TutView, item api.Item, tl *config.Timeline) *Feed { if item.Type() != api.UserType && item.Type() != api.ProfileType { panic("Can't open user. Wrong type.\n") } @@ -307,28 +297,26 @@ func NewUserFeed(tv *TutView, item api.Item) *Feed { f := feed.NewUserProfile(tv.tut.Client, tv.tut.Config, u) f.LoadNewer() fd := &Feed{ - tutView: tv, - Data: f, - List: NewFeedList(tv.tut, f.StickyCount()), - Content: NewFeedContent(tv.tut), - ShowBoosts: true, - ShowReplies: true, + tutView: tv, + Data: f, + List: NewFeedList(tv.tut, f.StickyCount()), + Content: NewFeedContent(tv.tut), + Timeline: tl, } go fd.update() return fd } -func NewUserSearchFeed(tv *TutView, search string) *Feed { - f := feed.NewUserSearch(tv.tut.Client, tv.tut.Config, search) +func NewUserSearchFeed(tv *TutView, tl *config.Timeline) *Feed { + f := feed.NewUserSearch(tv.tut.Client, tv.tut.Config, tl.Subaction) f.LoadNewer() fd := &Feed{ - tutView: tv, - Data: f, - List: NewFeedList(tv.tut, f.StickyCount()), - Content: NewFeedContent(tv.tut), - ShowBoosts: true, - ShowReplies: true, + tutView: tv, + Data: f, + List: NewFeedList(tv.tut, f.StickyCount()), + Content: NewFeedContent(tv.tut), + Timeline: tl, } for _, s := range f.List() { main, symbol := DrawListItem(tv.tut.Config, s) @@ -339,240 +327,225 @@ func NewUserSearchFeed(tv *TutView, search string) *Feed { return fd } -func NewTagFeed(tv *TutView, search string, showBoosts bool, showReplies bool) *Feed { - f := feed.NewTag(tv.tut.Client, tv.tut.Config, search, showBoosts, showReplies) +func NewTagFeed(tv *TutView, tl *config.Timeline) *Feed { + f := feed.NewTag(tv.tut.Client, tv.tut.Config, tl.Subaction, tl.HideBoosts, tl.HideReplies) f.LoadNewer() fd := &Feed{ - tutView: tv, - Data: f, - List: NewFeedList(tv.tut, f.StickyCount()), - Content: NewFeedContent(tv.tut), - ShowBoosts: showBoosts, - ShowReplies: showReplies, + tutView: tv, + Data: f, + List: NewFeedList(tv.tut, f.StickyCount()), + Content: NewFeedContent(tv.tut), + Timeline: tl, } go fd.update() return fd } -func NewTagsFeed(tv *TutView) *Feed { +func NewTagsFeed(tv *TutView, tl *config.Timeline) *Feed { f := feed.NewTags(tv.tut.Client, tv.tut.Config) f.LoadNewer() fd := &Feed{ - tutView: tv, - Data: f, - List: NewFeedList(tv.tut, f.StickyCount()), - Content: NewFeedContent(tv.tut), - ShowBoosts: true, - ShowReplies: true, + tutView: tv, + Data: f, + List: NewFeedList(tv.tut, f.StickyCount()), + Content: NewFeedContent(tv.tut), + Timeline: tl, } go fd.update() return fd } -func NewListsFeed(tv *TutView) *Feed { +func NewListsFeed(tv *TutView, tl *config.Timeline) *Feed { f := feed.NewListList(tv.tut.Client, tv.tut.Config) f.LoadNewer() fd := &Feed{ - tutView: tv, - Data: f, - List: NewFeedList(tv.tut, f.StickyCount()), - Content: NewFeedContent(tv.tut), - ShowBoosts: true, - ShowReplies: true, + tutView: tv, + Data: f, + List: NewFeedList(tv.tut, f.StickyCount()), + Content: NewFeedContent(tv.tut), + Timeline: tl, } go fd.update() return fd } -func NewListFeed(tv *TutView, l *mastodon.List, showBoosts bool, showReplies bool) *Feed { - f := feed.NewList(tv.tut.Client, tv.tut.Config, l, showBoosts, showReplies) +func NewListFeed(tv *TutView, l *mastodon.List, tl *config.Timeline) *Feed { + f := feed.NewList(tv.tut.Client, tv.tut.Config, l, tl.HideBoosts, tl.HideReplies) f.LoadNewer() fd := &Feed{ - tutView: tv, - Data: f, - List: NewFeedList(tv.tut, f.StickyCount()), - Content: NewFeedContent(tv.tut), - ShowBoosts: showBoosts, - ShowReplies: showReplies, + tutView: tv, + Data: f, + List: NewFeedList(tv.tut, f.StickyCount()), + Content: NewFeedContent(tv.tut), + Timeline: tl, } go fd.update() return fd } -func NewUsersInListFeed(tv *TutView, l *mastodon.List) *Feed { +func NewUsersInListFeed(tv *TutView, l *mastodon.List, tl *config.Timeline) *Feed { f := feed.NewUsersInList(tv.tut.Client, tv.tut.Config, l) f.LoadNewer() fd := &Feed{ - tutView: tv, - Data: f, - List: NewFeedList(tv.tut, f.StickyCount()), - Content: NewFeedContent(tv.tut), - ShowBoosts: true, - ShowReplies: true, + tutView: tv, + Data: f, + List: NewFeedList(tv.tut, f.StickyCount()), + Content: NewFeedContent(tv.tut), + Timeline: tl, } go fd.update() return fd } -func NewUsersAddListFeed(tv *TutView, l *mastodon.List) *Feed { +func NewUsersAddListFeed(tv *TutView, l *mastodon.List, tl *config.Timeline) *Feed { f := feed.NewUsersAddList(tv.tut.Client, tv.tut.Config, l) f.LoadNewer() fd := &Feed{ - tutView: tv, - Data: f, - List: NewFeedList(tv.tut, f.StickyCount()), - Content: NewFeedContent(tv.tut), - ShowBoosts: true, - ShowReplies: true, + tutView: tv, + Data: f, + List: NewFeedList(tv.tut, f.StickyCount()), + Content: NewFeedContent(tv.tut), + Timeline: tl, } go fd.update() return fd } -func NewFavoritedFeed(tv *TutView) *Feed { +func NewFavoritedFeed(tv *TutView, tl *config.Timeline) *Feed { f := feed.NewFavorites(tv.tut.Client, tv.tut.Config) f.LoadNewer() fd := &Feed{ - tutView: tv, - Data: f, - List: NewFeedList(tv.tut, f.StickyCount()), - Content: NewFeedContent(tv.tut), - ShowBoosts: true, - ShowReplies: true, + tutView: tv, + Data: f, + List: NewFeedList(tv.tut, f.StickyCount()), + Content: NewFeedContent(tv.tut), + Timeline: tl, } go fd.update() return fd } -func NewBookmarksFeed(tv *TutView) *Feed { +func NewBookmarksFeed(tv *TutView, tl *config.Timeline) *Feed { f := feed.NewBookmarks(tv.tut.Client, tv.tut.Config) f.LoadNewer() fd := &Feed{ - tutView: tv, - Data: f, - List: NewFeedList(tv.tut, f.StickyCount()), - Content: NewFeedContent(tv.tut), - ShowBoosts: true, - ShowReplies: true, + tutView: tv, + Data: f, + List: NewFeedList(tv.tut, f.StickyCount()), + Content: NewFeedContent(tv.tut), + Timeline: tl, } go fd.update() return fd } -func NewFavoritesStatus(tv *TutView, id mastodon.ID) *Feed { +func NewFavoritesStatus(tv *TutView, id mastodon.ID, tl *config.Timeline) *Feed { f := feed.NewFavoritesStatus(tv.tut.Client, tv.tut.Config, id) f.LoadNewer() fd := &Feed{ - tutView: tv, - Data: f, - List: NewFeedList(tv.tut, f.StickyCount()), - Content: NewFeedContent(tv.tut), - ShowBoosts: true, - ShowReplies: true, + tutView: tv, + Data: f, + List: NewFeedList(tv.tut, f.StickyCount()), + Content: NewFeedContent(tv.tut), + Timeline: tl, } go fd.update() return fd } -func NewBoosts(tv *TutView, id mastodon.ID) *Feed { +func NewBoosts(tv *TutView, id mastodon.ID, tl *config.Timeline) *Feed { f := feed.NewBoosts(tv.tut.Client, tv.tut.Config, id) f.LoadNewer() fd := &Feed{ - tutView: tv, - Data: f, - List: NewFeedList(tv.tut, f.StickyCount()), - Content: NewFeedContent(tv.tut), - ShowBoosts: true, - ShowReplies: true, + tutView: tv, + Data: f, + List: NewFeedList(tv.tut, f.StickyCount()), + Content: NewFeedContent(tv.tut), + Timeline: tl, } go fd.update() return fd } -func NewFollowers(tv *TutView, id mastodon.ID) *Feed { +func NewFollowers(tv *TutView, id mastodon.ID, tl *config.Timeline) *Feed { f := feed.NewFollowers(tv.tut.Client, tv.tut.Config, id) f.LoadNewer() fd := &Feed{ - tutView: tv, - Data: f, - List: NewFeedList(tv.tut, f.StickyCount()), - Content: NewFeedContent(tv.tut), - ShowBoosts: true, - ShowReplies: true, + tutView: tv, + Data: f, + List: NewFeedList(tv.tut, f.StickyCount()), + Content: NewFeedContent(tv.tut), + Timeline: tl, } go fd.update() return fd } -func NewFollowing(tv *TutView, id mastodon.ID) *Feed { +func NewFollowing(tv *TutView, id mastodon.ID, tl *config.Timeline) *Feed { f := feed.NewFollowing(tv.tut.Client, tv.tut.Config, id) f.LoadNewer() fd := &Feed{ - tutView: tv, - Data: f, - List: NewFeedList(tv.tut, f.StickyCount()), - Content: NewFeedContent(tv.tut), - ShowBoosts: true, - ShowReplies: true, + tutView: tv, + Data: f, + List: NewFeedList(tv.tut, f.StickyCount()), + Content: NewFeedContent(tv.tut), + Timeline: tl, } go fd.update() return fd } -func NewBlocking(tv *TutView) *Feed { +func NewBlocking(tv *TutView, tl *config.Timeline) *Feed { f := feed.NewBlocking(tv.tut.Client, tv.tut.Config) f.LoadNewer() fd := &Feed{ - tutView: tv, - Data: f, - List: NewFeedList(tv.tut, f.StickyCount()), - Content: NewFeedContent(tv.tut), - ShowBoosts: true, - ShowReplies: true, + tutView: tv, + Data: f, + List: NewFeedList(tv.tut, f.StickyCount()), + Content: NewFeedContent(tv.tut), + Timeline: tl, } go fd.update() return fd } -func NewMuting(tv *TutView) *Feed { +func NewMuting(tv *TutView, tl *config.Timeline) *Feed { f := feed.NewMuting(tv.tut.Client, tv.tut.Config) f.LoadNewer() fd := &Feed{ - tutView: tv, - Data: f, - List: NewFeedList(tv.tut, f.StickyCount()), - Content: NewFeedContent(tv.tut), - ShowBoosts: true, - ShowReplies: true, + tutView: tv, + Data: f, + List: NewFeedList(tv.tut, f.StickyCount()), + Content: NewFeedContent(tv.tut), + Timeline: tl, } go fd.update() return fd } -func NewFollowRequests(tv *TutView) *Feed { +func NewFollowRequests(tv *TutView, tl *config.Timeline) *Feed { f := feed.NewFollowRequests(tv.tut.Client, tv.tut.Config) f.LoadNewer() fd := &Feed{ - tutView: tv, - Data: f, - List: NewFeedList(tv.tut, f.StickyCount()), - Content: NewFeedContent(tv.tut), - ShowBoosts: true, - ShowReplies: true, + tutView: tv, + Data: f, + List: NewFeedList(tv.tut, f.StickyCount()), + Content: NewFeedContent(tv.tut), + Timeline: tl, } go fd.update() diff --git a/ui/input.go b/ui/input.go index db5d975..39a3fbc 100644 --- a/ui/input.go +++ b/ui/input.go @@ -2,7 +2,6 @@ package ui import ( "fmt" - "strconv" "strings" "sync" "time" @@ -30,6 +29,16 @@ func (tv *TutView) Input(event *tcell.EventKey) *tcell.EventKey { return nil } } + + if tv.tut.Config.Input.MainNextAccount.Match(event.Key(), event.Rune()) { + tv.NextAcct() + return nil + } + if tv.tut.Config.Input.MainPrevAccount.Match(event.Key(), event.Rune()) { + tv.NextAcct() + return nil + } + switch tv.PageFocus { case LoginFocus: return tv.InputLoginView(event) @@ -55,6 +64,8 @@ func (tv *TutView) Input(event *tcell.EventKey) *tcell.EventKey { return tv.InputHelp(event) case PreferenceFocus: return tv.InputPreference(event) + case EditorFocus: + return tv.InputEditorView(event) default: return event } @@ -95,6 +106,33 @@ func (tv *TutView) InputLeaderKey(event *tcell.EventKey) *tcell.EventKey { action := config.LeaderNone var subaction string content := tv.Leader.Content() + foundFeed := false + for i, fh := range tv.Timeline.Feeds { + for _, f := range fh.Feeds { + if f.Timeline.Shortcut == "" || f.Timeline.Shortcut != content { + continue + } + switch f.Timeline.OnFocus { + case config.TimelineFocusPane: + tv.FocusFeed(i, nil) + case config.TimelineFocusTimeline: + tv.FocusFeed(i, f.Timeline) + } + foundFeed = true + tv.Leader.ResetInactive() + return nil + } + } + if !foundFeed { + for _, tl := range tv.tut.Config.General.Timelines { + if tl.Shortcut == "" || tl.Shortcut != content { + continue + } + tv.matchedTimeline(tl) + tv.Leader.ResetInactive() + return nil + } + } for _, la := range tv.tut.Config.General.LeaderActions { if la.Shortcut == content { action = la.Command @@ -106,20 +144,6 @@ func (tv *TutView) InputLeaderKey(event *tcell.EventKey) *tcell.EventKey { return nil } switch action { - case config.LeaderHome: - tv.HomeCommand() - case config.LeaderDirect: - tv.DirectCommand() - case config.LeaderLocal: - tv.LocalCommand() - case config.LeaderFederated: - tv.FederatedCommand() - case config.LeaderSpecialAll: - tv.SpecialCommand(true, true) - case config.LeaderSpecialBoosts: - tv.SpecialCommand(true, false) - case config.LeaderSpecialReplies: - tv.SpecialCommand(false, true) case config.LeaderClearNotifications: tv.ClearNotificationsCommand() case config.LeaderCompose: @@ -128,8 +152,6 @@ func (tv *TutView) InputLeaderKey(event *tcell.EventKey) *tcell.EventKey { tv.EditCommand() case config.LeaderBlocking: tv.BlockingCommand() - case config.LeaderBookmarks, config.LeaderSaved: - tv.BookmarksCommand() case config.LeaderFavorited: tv.FavoritedCommand() case config.LeaderHistory: @@ -148,36 +170,26 @@ func (tv *TutView) InputLeaderKey(event *tcell.EventKey) *tcell.EventKey { tv.PreferencesCommand() case config.LeaderProfile: tv.ProfileCommand() - case config.LeaderNotifications: - tv.NotificationsCommand() - case config.LeaderMentions: - tv.MentionsCommand() case config.LeaderLoadNewer: tv.LoadNewerCommand() - case config.LeaderLists: - tv.ListsCommand() case config.LeaderStickToTop: tv.ToggleStickToTop() case config.LeaderRefetch: tv.RefetchCommand() - case config.LeaderTag: - tv.TagCommand(subaction) case config.LeaderTags: tv.TagsCommand() - case config.LeaderWindow: - tv.WindowCommand(subaction) - case config.LeaderCloseWindow: - tv.CloseWindowCommand() - case config.LeaderMoveWindowLeft: - tv.MoveWindowLeft() - case config.LeaderMoveWindowRight: - tv.MoveWindowRight() - case config.LeaderMoveWindowHome: - tv.MoveWindowHome() - case config.LeaderMoveWindowEnd: - tv.MoveWindowEnd() - case config.LeaderSwitch: - tv.SwitchCommand(subaction) + case config.LeaderPane: + tv.PaneCommand(subaction) + case config.LeaderClosePane: + tv.ClosePaneCommand() + case config.LeaderMovePaneLeft: + tv.MovePaneLeft() + case config.LeaderMovePaneRight: + tv.MovePaneRight() + case config.LeaderMovePaneHome: + tv.MovePaneHome() + case config.LeaderMovePaneEnd: + tv.MovePaneEnd() case config.LeaderListPlacement: switch subaction { case "top": @@ -219,6 +231,16 @@ func (tv *TutView) InputMainView(event *tcell.EventKey) *tcell.EventKey { } } +func (tv *TutView) matchedTimeline(tl *config.Timeline) { + nf := CreateFeed(tv, tl) + switch tl.OnCreationClosed { + case config.TimelineCreationClosedCurrentPane: + tv.Timeline.AddFeed(nf, false) + case config.TimelineCreationClosedNewPane: + tv.Timeline.AddFeed(nf, true) + } +} + func (tv *TutView) InputMainViewFeed(event *tcell.EventKey) *tcell.EventKey { if tv.tut.Config.Input.MainHome.Match(event.Key(), event.Rune()) { tv.Timeline.HomeItemFeed() @@ -244,16 +266,12 @@ func (tv *TutView) InputMainViewFeed(event *tcell.EventKey) *tcell.EventKey { tv.Timeline.PrevItemFeed() return nil } - if tv.tut.Config.Input.MainPrevWindow.Match(event.Key(), event.Rune()) { - if tv.tut.Config.General.NotificationFeed { - tv.PrevFeed() - } + if tv.tut.Config.Input.MainPrevPane.Match(event.Key(), event.Rune()) { + tv.PrevFeed() return nil } - if tv.tut.Config.Input.MainNextWindow.Match(event.Key(), event.Rune()) { - if tv.tut.Config.General.NotificationFeed { - tv.NextFeed() - } + if tv.tut.Config.Input.MainNextPane.Match(event.Key(), event.Rune()) { + tv.NextFeed() return nil } if tv.tut.Config.Input.GlobalExit.Match(event.Key(), event.Rune()) { @@ -265,19 +283,39 @@ func (tv *TutView) InputMainViewFeed(event *tcell.EventKey) *tcell.EventKey { }) return nil } else if exiting && tv.Timeline.FeedFocusIndex != 0 { - tv.FocusFeed(0) + tv.FocusFeed(0, nil) } return nil } - for i, tl := range tv.tut.Config.General.Timelines { - if tl.Key.Match(event.Key(), event.Rune()) { - tv.FocusFeed(i) + + foundFeed := false + for i, fh := range tv.Timeline.Feeds { + for _, f := range fh.Feeds { + if f.Timeline.Key.Match(event.Key(), event.Rune()) { + switch f.Timeline.OnFocus { + case config.TimelineFocusPane: + tv.FocusFeed(i, nil) + case config.TimelineFocusTimeline: + tv.FocusFeed(i, f.Timeline) + } + foundFeed = true + return nil + } + } + } + if !foundFeed { + for _, tl := range tv.tut.Config.General.Timelines { + if tl.Key.Match(event.Key(), event.Rune()) { + tv.matchedTimeline(tl) + return nil + } } } + if tv.tut.Config.Input.GlobalBack.Match(event.Key(), event.Rune()) { exiting := tv.Timeline.RemoveCurrent(false) if exiting && tv.Timeline.FeedFocusIndex != 0 { - tv.FocusFeed(0) + tv.FocusFeed(0, nil) } return nil } @@ -511,7 +549,9 @@ func (tv *TutView) InputStatus(event *tcell.EventKey, item api.Item, status *mas return nil } if tv.tut.Config.Input.StatusThread.Match(event.Key(), event.Rune()) { - tv.Timeline.AddFeed(NewThreadFeed(tv, item)) + tv.Timeline.AddFeed(NewThreadFeed(tv, item, config.NewTimeline(config.Timeline{ + FeedType: config.Thread, + })), false) return nil } if tv.tut.Config.Input.StatusUser.Match(event.Key(), event.Rune()) { @@ -523,7 +563,9 @@ func (tv *TutView) InputStatus(event *tcell.EventKey, item api.Item, status *mas if err != nil { return nil } - tv.Timeline.AddFeed(NewUserFeed(tv, user)) + tv.Timeline.AddFeed(NewUserFeed(tv, user, config.NewTimeline(config.Timeline{ + FeedType: config.User, + })), false) return nil } if tv.tut.Config.Input.StatusViewFocus.Match(event.Key(), event.Rune()) { @@ -751,7 +793,9 @@ func (tv *TutView) InputUser(event *tcell.EventKey, user *api.User, ut InputUser return nil } if tv.tut.Config.Input.UserUser.Match(event.Key(), event.Rune()) { - tv.Timeline.AddFeed(NewUserFeed(tv, api.NewUserItem(user, true))) + tv.Timeline.AddFeed(NewUserFeed(tv, api.NewUserItem(user, true), config.NewTimeline(config.Timeline{ + FeedType: config.User, + })), false) return nil } if tv.tut.Config.Input.UserViewFocus.Match(event.Key(), event.Rune()) { @@ -763,7 +807,9 @@ func (tv *TutView) InputUser(event *tcell.EventKey, user *api.User, ut InputUser return nil } if tv.tut.Config.Input.GlobalEnter.Match(event.Key(), event.Rune()) { - tv.Timeline.AddFeed(NewUserFeed(tv, api.NewUserItem(user, true))) + tv.Timeline.AddFeed(NewUserFeed(tv, api.NewUserItem(user, true), config.NewTimeline(config.Timeline{ + FeedType: config.User, + })), false) return nil } return event @@ -772,15 +818,21 @@ func (tv *TutView) InputUser(event *tcell.EventKey, user *api.User, ut InputUser func (tv *TutView) InputList(event *tcell.EventKey, list *mastodon.List) *tcell.EventKey { if tv.tut.Config.Input.ListOpenFeed.Match(event.Key(), event.Rune()) || tv.tut.Config.Input.GlobalEnter.Match(event.Key(), event.Rune()) { - tv.Timeline.AddFeed(NewListFeed(tv, list, true, true)) + tv.Timeline.AddFeed(NewListFeed(tv, list, config.NewTimeline(config.Timeline{ + FeedType: config.List, + })), false) return nil } if tv.tut.Config.Input.ListUserList.Match(event.Key(), event.Rune()) { - tv.Timeline.AddFeed(NewUsersInListFeed(tv, list)) + tv.Timeline.AddFeed(NewUsersInListFeed(tv, list, config.NewTimeline(config.Timeline{ + FeedType: config.ListUsersIn, + })), false) return nil } if tv.tut.Config.Input.ListUserAdd.Match(event.Key(), event.Rune()) { - tv.Timeline.AddFeed(NewUsersAddListFeed(tv, list)) + tv.Timeline.AddFeed(NewUsersAddListFeed(tv, list, config.NewTimeline(config.Timeline{ + FeedType: config.ListUsersAdd, + })), false) return nil } return event @@ -789,7 +841,10 @@ func (tv *TutView) InputList(event *tcell.EventKey, list *mastodon.List) *tcell. func (tv *TutView) InputTag(event *tcell.EventKey, tag *mastodon.Tag) *tcell.EventKey { if tv.tut.Config.Input.TagOpenFeed.Match(event.Key(), event.Rune()) || tv.tut.Config.Input.GlobalEnter.Match(event.Key(), event.Rune()) { - tv.Timeline.AddFeed(NewTagFeed(tv, tag.Name, true, true)) + tv.Timeline.AddFeed(NewTagFeed(tv, config.NewTimeline(config.Timeline{ + FeedType: config.Tag, + Subaction: tag.Name, + })), false) return nil } if tv.tut.Config.Input.TagFollow.Match(event.Key(), event.Rune()) { @@ -837,12 +892,9 @@ func (tv *TutView) InputLinkView(event *tcell.EventKey) *tcell.EventKey { tv.SetPage(MainFocus) return nil } - if event.Key() == tcell.KeyRune { - switch event.Rune() { - case '1', '2', '3', '4', '5': - s := string(event.Rune()) - i, _ := strconv.Atoi(s) - tv.LinkView.OpenCustom(i) + for _, oc := range tv.tut.Config.OpenCustom.OpenCustoms { + if oc.Key.Match(event.Key(), event.Rune()) { + tv.LinkView.OpenCustom(oc) return nil } } @@ -1099,6 +1151,20 @@ func (tv *TutView) InputCmdView(event *tcell.EventKey) *tcell.EventKey { return event } +func (tv *TutView) InputEditorView(event *tcell.EventKey) *tcell.EventKey { + if tv.tut.Config.Input.GlobalBack.Match(event.Key(), rune(-1)) { + tv.EditorView.ExitTextAreaInput() + return nil + } + switch key := event.Key(); key { + case tcell.KeyCtrlQ: + return nil + case tcell.KeyCtrlC: + return tcell.NewEventKey(tcell.KeyCtrlQ, rune(0), tcell.ModNone) + } + return event +} + func (tv *TutView) MouseInput(event *tcell.EventMouse, action tview.MouseAction) (*tcell.EventMouse, tview.MouseAction) { if event == nil { return nil, action @@ -1108,6 +1174,12 @@ func (tv *TutView) MouseInput(event *tcell.EventMouse, action tview.MouseAction) return event, action } + /* Switch accounts */ + x, y := event.Position() + if tv.MainView.accView.InRect(x, y) { + return event, action + } + switch tv.PageFocus { case ViewFocus, MainFocus: return tv.MouseInputMainView(event, action) @@ -1136,7 +1208,7 @@ func (tv *TutView) MouseInput(event *tcell.EventMouse, action tview.MouseAction) func (tv *TutView) feedListMouse(list *tview.List, i int, event *tcell.EventMouse, action tview.MouseAction) { tv.SetPage(MainFocus) - tv.FocusFeed(i) + tv.FocusFeed(i, nil) mh := list.MouseHandler() if mh == nil { return diff --git a/ui/item_status.go b/ui/item_status.go index b66e224..3c93905 100644 --- a/ui/item_status.go +++ b/ui/item_status.go @@ -36,6 +36,7 @@ type Toot struct { Boosts int Favorites int Edited bool + Lang string Controls string } @@ -104,8 +105,7 @@ func drawStatus(tv *TutView, item api.Item, status *mastodon.Status, main *tview status = status.Reblog } - strippedContent, _ = util.CleanHTML(status.Content) - strippedContent = tview.Escape(strippedContent) + strippedContent, _ = util.CleanHTMLStyled(status.Content) width := 0 if main != nil { @@ -121,6 +121,12 @@ func drawStatus(tv *TutView, item api.Item, status *mastodon.Status, main *tview ShowSpoiler: showSensitive, CWlabel: cwToggle.Label, } + for _, lang := range util.Languages { + if status.Language == lang.Code { + toot.Lang = lang.English + break + } + } toot.AccountDisplayName = tview.Escape(status.Account.DisplayName) toot.Account = tview.Escape(status.Account.Acct) @@ -162,8 +168,7 @@ func drawStatus(tv *TutView, item api.Item, status *mastodon.Status, main *tview } if status.Sensitive { - strippedSpoiler, _ = util.CleanHTML(status.SpoilerText) - strippedSpoiler = tview.Escape(strippedSpoiler) + strippedSpoiler, _ = util.CleanHTMLStyled(status.SpoilerText) } toot.CWText = strippedSpoiler diff --git a/ui/linkview.go b/ui/linkview.go index bcf9cd2..2a0c5e3 100644 --- a/ui/linkview.go +++ b/ui/linkview.go @@ -37,10 +37,7 @@ func linkViewUI(lv *LinkView) *tview.Flex { NewControl(lv.tutView.tut.Config, lv.tutView.tut.Config.Input.LinkYank, true), } for _, cust := range lv.tutView.tut.Config.OpenCustom.OpenCustoms { - key := config.Key{ - Hint: [][]string{{"", fmt.Sprintf("%d", cust.Index), cust.Name}}, - } - items = append(items, NewControl(lv.tutView.tut.Config, key, true)) + items = append(items, NewControl(lv.tutView.tut.Config, cust.Key, true)) } lv.controls.Clear() for i, item := range items { @@ -114,16 +111,20 @@ func (lv *LinkView) Open() { return } lv.tutView.Timeline.AddFeed( - NewUserFeed(lv.tutView, u), - ) + NewUserFeed(lv.tutView, u, config.NewTimeline(config.Timeline{ + FeedType: config.User, + }), + ), false) lv.tutView.FocusMainNoHistory() return } tIndex := index - len(mentions) - len(urls) if tIndex < len(tags) { lv.tutView.Timeline.AddFeed( - NewTagFeed(lv.tutView, tags[tIndex].Name, true, true), - ) + NewTagFeed(lv.tutView, config.NewTimeline(config.Timeline{ + FeedType: config.Tag, + Subaction: tags[tIndex].Name, + })), false) lv.tutView.FocusMainNoHistory() return } @@ -162,17 +163,10 @@ func (lv *LinkView) Yank() { copyToClipboard(url) } -func (lv *LinkView) OpenCustom(index int) { +func (lv *LinkView) OpenCustom(c config.Custom) { url := lv.getURL() if url == "" { return } - customs := lv.tutView.tut.Config.OpenCustom.OpenCustoms - for _, c := range customs { - if c.Index != index { - continue - } - openCustom(lv.tutView, c.Program, c.Args, c.Terminal, url) - return - } + openCustom(lv.tutView, c.Program, c.Args, c.Terminal, url) } diff --git a/ui/mainview.go b/ui/mainview.go index c875c5b..ef70347 100644 --- a/ui/mainview.go +++ b/ui/mainview.go @@ -1,24 +1,28 @@ package ui import ( + "fmt" + "github.com/RasmusLindroth/tut/config" "github.com/rivo/tview" ) type MainView struct { - View *tview.Flex - update chan bool + View *tview.Flex + accView *tview.Flex + update chan bool } func NewMainView(tv *TutView, update chan bool) *MainView { mv := &MainView{ - View: mainViewUI(tv), - update: update, + update: update, + accView: NewControlView(tv.tut.Config), } + mv.View = mv.mainViewUI(tv) go func() { for range mv.update { tv.tut.App.QueueUpdateDraw(func() { - *tv.MainView.View = *mainViewUI(tv) + *tv.MainView.View = *mv.mainViewUI(tv) tv.ShouldSync() }) } @@ -40,85 +44,102 @@ func feedList(mv *TutView, fh *FeedHolder) *tview.Flex { AddItem(fh.GetFeedList().Symbol, iw, 0, false) } -func mainViewUI(mv *TutView) *tview.Flex { - vl := NewVerticalLine(mv.tut.Config) - hl := NewHorizontalLine(mv.tut.Config) - lp := mv.tut.Config.General.ListProportion - cp := mv.tut.Config.General.ContentProportion +func (mv *MainView) mainViewUI(tv *TutView) *tview.Flex { + vl := NewVerticalLine(tv.tut.Config) + hl := NewHorizontalLine(tv.tut.Config) + lp := tv.tut.Config.General.ListProportion + cp := tv.tut.Config.General.ContentProportion var list *tview.Flex - if mv.tut.Config.General.ListSplit == config.ListColumn { + if tv.tut.Config.General.ListSplit == config.ListColumn { list = tview.NewFlex().SetDirection(tview.FlexColumn) } else { list = tview.NewFlex().SetDirection(tview.FlexRow) } - if mv.tut.Config.General.ListSplit == config.ListColumn { + if tv.tut.Config.General.ListSplit == config.ListColumn { feeds := tview.NewFlex() - for _, fh := range mv.Timeline.Feeds { - if mv.tut.Config.General.TimelineName && len(fh.Name) > 0 { - txt := NewTextView(mv.tut.Config) - txt.SetText(tview.Escape(fh.Name)) - txt.SetBackgroundColor(mv.tut.Config.Style.TimelineNameBackground) - txt.SetTextColor(mv.tut.Config.Style.TimelineNameText) + for _, fh := range tv.Timeline.Feeds { + fTitle := fh.GetTitle() + if len(fTitle) > 0 { + txt := NewTextView(tv.tut.Config) + txt.SetText(tview.Escape(fTitle)) + txt.SetBackgroundColor(tv.tut.Config.Style.TimelineNameBackground) + txt.SetTextColor(tv.tut.Config.Style.TimelineNameText) feeds.AddItem(tview.NewFlex().SetDirection(tview.FlexRow). AddItem(txt, 1, 0, false). - AddItem(feedList(mv, fh), 0, 1, false), 0, 1, false) + AddItem(feedList(tv, fh), 0, 1, false), 0, 1, false) } else { - feeds.AddItem(feedList(mv, fh), 0, 1, false) + feeds.AddItem(feedList(tv, fh), 0, 1, false) } } list.AddItem(tview.NewFlex().SetDirection(tview.FlexRow). AddItem(feeds, 0, 1, false), 0, 1, false) } else { feeds := tview.NewFlex().SetDirection(tview.FlexRow) - for _, fh := range mv.Timeline.Feeds { - if mv.tut.Config.General.TimelineName && len(fh.Name) > 0 { - txt := NewTextView(mv.tut.Config) - txt.SetText(tview.Escape(fh.Name)) - txt.SetBackgroundColor(mv.tut.Config.Style.TimelineNameBackground) - txt.SetTextColor(mv.tut.Config.Style.TimelineNameText) + for _, fh := range tv.Timeline.Feeds { + fTitle := fh.GetTitle() + if len(fTitle) > 0 { + txt := NewTextView(tv.tut.Config) + txt.SetText(tview.Escape(fTitle)) + txt.SetBackgroundColor(tv.tut.Config.Style.TimelineNameBackground) + txt.SetTextColor(tv.tut.Config.Style.TimelineNameText) feeds.AddItem(txt, 1, 0, false) } - feeds.AddItem(feedList(mv, fh), 0, 1, false) + feeds.AddItem(feedList(tv, fh), 0, 1, false) } list.AddItem(feeds, 0, 1, false) } - fc := mv.Timeline.GetFeedContent() + fc := tv.Timeline.GetFeedContent() content := fc.Main controls := fc.Controls + + mv.accView.Clear() + for i, t := range TutViews.Views { + acct := t.tut.Client.Me.Acct + acct = fmt.Sprintf("%s ", acct) + if i > 0 { + acct = fmt.Sprintf(" %s", acct) + } + item := NewAccButton(tv, tv.tut.Config, acct, i, i == TutViews.Current) + mv.accView.AddItem(item, len(acct), 0, false) + } + r := tview.NewFlex().SetDirection(tview.FlexRow) - if mv.tut.Config.General.TerminalTitle < 2 { - r.AddItem(mv.Shared.Top.View, 1, 0, false) + if tv.tut.Config.General.TerminalTitle < 2 { + r.AddItem(tv.Shared.Top.View, 1, 0, false) } - if mv.tut.Config.General.ListPlacement == config.ListPlacementTop { + if tv.tut.Config.General.ListPlacement == config.ListPlacementTop { r.AddItem(list, 0, lp, false). AddItem(hl, 1, 0, false). AddItem(content, 0, cp, false). AddItem(controls, 1, 0, false). - AddItem(mv.Shared.Bottom.View, 2, 0, false) - } else if mv.tut.Config.General.ListPlacement == config.ListPlacementBottom { + AddItem(tv.Shared.Bottom.View, 2, 0, false) + } else if tv.tut.Config.General.ListPlacement == config.ListPlacementBottom { r.AddItem(content, 0, cp, false). AddItem(controls, 1, 0, false). AddItem(hl, 1, 0, false). AddItem(list, 0, lp, false). - AddItem(mv.Shared.Bottom.View, 2, 0, false) - } else if mv.tut.Config.General.ListPlacement == config.ListPlacementLeft { + AddItem(tv.Shared.Bottom.View, 2, 0, false) + } else if tv.tut.Config.General.ListPlacement == config.ListPlacementLeft { r.AddItem(tview.NewFlex().SetDirection(tview.FlexColumn). AddItem(list, 0, lp, false). AddItem(vl, 1, 0, false). AddItem(tview.NewFlex().SetDirection(tview.FlexRow). AddItem(content, 0, 1, false). AddItem(controls, 1, 0, false), 0, cp, false), 0, 1, false). - AddItem(mv.Shared.Bottom.View, 2, 0, false) - } else if mv.tut.Config.General.ListPlacement == config.ListPlacementRight { + AddItem(tv.Shared.Bottom.View, 2, 0, false) + } else if tv.tut.Config.General.ListPlacement == config.ListPlacementRight { r.AddItem(tview.NewFlex().SetDirection(tview.FlexColumn). AddItem(tview.NewFlex().SetDirection(tview.FlexRow). AddItem(content, 0, 1, false). AddItem(controls, 1, 0, false), 0, cp, false). AddItem(vl, 1, 0, false). AddItem(list, 0, lp, false), 0, 1, false). - AddItem(mv.Shared.Bottom.View, 2, 0, false) + AddItem(tv.Shared.Bottom.View, 2, 0, false) + } + if len(TutViews.Views) > 1 { + r.AddItem(mv.accView, 1, 0, false) } return r } diff --git a/ui/media.go b/ui/media.go index e871308..c97813c 100644 --- a/ui/media.go +++ b/ui/media.go @@ -155,18 +155,22 @@ func openMediaType(tv *TutView, filenames []string, mediaType string) { go func() { for _, ext := range external { exec.Command(ext.Name, ext.Args...).Run() - deleteFiles(ext.Filenames) + deleteFiles(tv, ext.Filenames) } }() for _, term := range terminal { openInTerminal(tv, term.Name, term.Args...) - deleteFiles(term.Filenames) + deleteFiles(tv, term.Filenames) } } -func deleteFiles(filenames []string) { - for _, filename := range filenames { - os.Remove(filename) +func deleteFiles(tv *TutView, filenames []string) { + if tv.tut.Config.Media.DeleteTmpFiles { + for _, filename := range filenames { + os.Remove(filename) + } + } else { + tv.FileList = append(tv.FileList, filenames...) } } diff --git a/ui/open.go b/ui/open.go index 7ecfc3f..d57d1a8 100644 --- a/ui/open.go +++ b/ui/open.go @@ -52,15 +52,13 @@ func openCustom(tv *TutView, program string, args []string, terminal bool, url s } } -func OpenEditorLengthLimit(tv *TutView, s string, limit int) (string, bool, error) { +func OpenEditorLengthLimit(tv *TutView, s string, limit int) (string, error) { text, err := OpenEditor(tv, s) if err != nil { - return text, false, err + return text, err } s = strings.TrimSpace(text) - if len(s) == 0 { - return "", false, nil - } + if utf8.RuneCountInString(s) > limit { ns := "" i := 0 @@ -73,13 +71,19 @@ func OpenEditorLengthLimit(tv *TutView, s string, limit int) (string, bool, erro } s = ns } - return s, true, nil + return s, nil } func OpenEditor(tv *TutView, content string) (string, error) { - editor, exists := os.LookupEnv("EDITOR") - if !exists || editor == "" { - editor = "vi" + var editor string + var exists bool + if tv.tut.Config.General.Editor == strings.TrimSpace("$EDITOR") { + editor, exists = os.LookupEnv("EDITOR") + if !exists || editor == "" { + editor = "vi" + } + } else { + editor = strings.TrimSpace(tv.tut.Config.General.Editor) } args := []string{} parts := strings.Split(editor, " ") diff --git a/ui/pollview.go b/ui/pollview.go index ede9ce8..9825ca7 100644 --- a/ui/pollview.go +++ b/ui/pollview.go @@ -166,14 +166,24 @@ func (p *PollView) Add() { p.tutView.ShowError(fmt.Sprintf("You can only have a maximum of %d options.", p.numOptions)) return } - text, valid, err := OpenEditorLengthLimit(p.tutView, "", p.numChars) + if p.tutView.tut.Config.General.UseInternalEditor { + p.tutView.EditorView.Init("", p.numChars, true, func(input string) { + p.add(input, nil) + }) + } else { + text, err := OpenEditorLengthLimit(p.tutView, "", p.numChars) + p.add(text, err) + } +} + +func (p *PollView) add(text string, err error) { if err != nil { p.tutView.ShowError( fmt.Sprintf("Couldn't open editor. Error: %v", err), ) return } - if !valid { + if len(text) == 0 { return } p.list.AddItem(text, "", 0, nil) @@ -187,14 +197,24 @@ func (p *PollView) Edit() { return } text, _ := p.list.GetItemText(p.list.GetCurrentItem()) - text, valid, err := OpenEditorLengthLimit(p.tutView, text, p.numChars) + if p.tutView.tut.Config.General.UseInternalEditor { + p.tutView.EditorView.Init(text, p.numChars, true, func(input string) { + p.edit(input, nil) + }) + } else { + text, err := OpenEditorLengthLimit(p.tutView, text, p.numChars) + p.edit(text, err) + } +} + +func (p *PollView) edit(text string, err error) { if err != nil { p.tutView.ShowError( fmt.Sprintf("Couldn't open editor. Error: %v", err), ) return } - if !valid { + if len(text) == 0 { return } p.list.SetItemText(p.list.GetCurrentItem(), text, "") diff --git a/ui/preferenceview.go b/ui/preferenceview.go index 25b6339..747334a 100644 --- a/ui/preferenceview.go +++ b/ui/preferenceview.go @@ -183,25 +183,45 @@ func (p *PreferenceView) AddField() { p.tutView.ShowError("You can have a maximum of four fields.") return } - name, valid, err := OpenEditorLengthLimit(p.tutView, "name", 255) + if p.tutView.tut.Config.General.UseInternalEditor { + p.tutView.EditorView.Init("name", 255, true, func(input string) { + p.addFieldOne(input, nil) + }) + } else { + name, err := OpenEditorLengthLimit(p.tutView, "name", 255) + p.addFieldOne(name, err) + } +} + +func (p *PreferenceView) addFieldOne(name string, err error) { if err != nil { p.tutView.ShowError( fmt.Sprintf("Couldn't add name. Error: %v\n", err), ) return } - if !valid { + if len(name) == 0 { p.tutView.ShowError("Name can't be empty.") return } - value, valid, err := OpenEditorLengthLimit(p.tutView, "value", 255) + if p.tutView.tut.Config.General.UseInternalEditor { + p.tutView.EditorView.Init("value", 255, false, func(input string) { + p.addFieldTwo(name, input, nil) + }) + } else { + value, err := OpenEditorLengthLimit(p.tutView, "value", 255) + p.addFieldTwo(name, value, err) + } +} + +func (p *PreferenceView) addFieldTwo(name string, value string, err error) { if err != nil { p.tutView.ShowError( fmt.Sprintf("Couldn't add value. Error: %v\n", err), ) return } - if !valid { + if len(value) == 0 { p.tutView.ShowError("Value can't be empty.") return } @@ -223,25 +243,45 @@ func (p *PreferenceView) EditField() { return } curr := p.preferences.fields[index] - name, valid, err := OpenEditorLengthLimit(p.tutView, curr.Name, 255) + if p.tutView.tut.Config.General.UseInternalEditor { + p.tutView.EditorView.Init(curr.Name, 255, true, func(input string) { + p.editFieldOne(index, input, curr.Value, nil) + }) + } else { + name, err := OpenEditorLengthLimit(p.tutView, curr.Name, 255) + p.editFieldOne(index, name, curr.Value, err) + } +} + +func (p *PreferenceView) editFieldOne(index int, name string, value string, err error) { if err != nil { p.tutView.ShowError( fmt.Sprintf("Couldn't edit name. Error: %v\n", err), ) return } - if !valid { + if len(name) == 0 { p.tutView.ShowError("Name can't be empty.") return } - value, valid, err := OpenEditorLengthLimit(p.tutView, curr.Value, 255) + if p.tutView.tut.Config.General.UseInternalEditor { + p.tutView.EditorView.Init(value, 255, false, func(input string) { + p.editFieldTwo(index, name, input, nil) + }) + } else { + value, err := OpenEditorLengthLimit(p.tutView, value, 255) + p.editFieldTwo(index, name, value, err) + } +} + +func (p *PreferenceView) editFieldTwo(index int, name string, value string, err error) { if err != nil { p.tutView.ShowError( fmt.Sprintf("Couldn't edit value. Error: %v\n", err), ) return } - if !valid { + if len(value) == 0 { p.tutView.ShowError("Value can't be empty.") return } @@ -268,7 +308,17 @@ func (p *PreferenceView) DeleteField() { func (p *PreferenceView) EditBio() { bio := p.preferences.bio - text, _, err := OpenEditorLengthLimit(p.tutView, bio, 500) + if p.tutView.tut.Config.General.UseInternalEditor { + p.tutView.EditorView.Init(bio, 500, true, func(input string) { + p.editBio(input, nil) + }) + } else { + text, err := OpenEditorLengthLimit(p.tutView, bio, 500) + p.editBio(text, err) + } +} + +func (p *PreferenceView) editBio(text string, err error) { if err != nil { p.tutView.ShowError( fmt.Sprintf("Couldn't edit bio. Error: %v\n", err), @@ -281,7 +331,17 @@ func (p *PreferenceView) EditBio() { func (p *PreferenceView) EditDisplayname() { dn := p.preferences.displayname - text, _, err := OpenEditorLengthLimit(p.tutView, dn, 30) + if p.tutView.tut.Config.General.UseInternalEditor { + p.tutView.EditorView.Init(dn, 30, true, func(input string) { + p.editDisplayname(input, nil) + }) + } else { + text, err := OpenEditorLengthLimit(p.tutView, dn, 30) + p.editDisplayname(text, err) + } +} + +func (p *PreferenceView) editDisplayname(text string, err error) { if err != nil { p.tutView.ShowError( fmt.Sprintf("Couldn't edit display name. Error: %v\n", err), diff --git a/ui/statusbar.go b/ui/statusbar.go index d266380..1bb7abc 100644 --- a/ui/statusbar.go +++ b/ui/statusbar.go @@ -23,6 +23,7 @@ const ( CmdMode ViewMode = iota ComposeMode HelpMode + EditorMode LinkMode ListMode MediaMode @@ -48,6 +49,8 @@ func (sb *StatusBar) SetMode(m ViewMode) { sb.View.SetText("-- LINK --") case ListMode: sb.View.SetText("-- LIST --") + case EditorMode: + sb.View.SetText("-- EDITOR --") case MediaMode: sb.View.SetText("-- MEDIA --") case NotificationsMode: diff --git a/ui/styled_elements.go b/ui/styled_elements.go index 8f28ab8..09408f5 100644 --- a/ui/styled_elements.go +++ b/ui/styled_elements.go @@ -26,6 +26,19 @@ func NewTextView(cnf *config.Config) *tview.TextView { return tw } +func NewTextArea(cnf *config.Config) *tview.TextArea { + ta := tview.NewTextArea() + ta.SetBackgroundColor(cnf.Style.Background) + ta.SetWordWrap(true) + ta.SetTextStyle(tcell.StyleDefault. + Background(cnf.Style.Background). + Foreground(cnf.Style.Text), + ) + //tw.SetTextColor(cnf.Style.Text) + //tw.SetDynamicColors(true) + return ta +} + func NewControlView(cnf *config.Config) *tview.Flex { f := tview.NewFlex().SetDirection(tview.FlexColumn) f.SetBackgroundColor(cnf.Style.Background) @@ -127,3 +140,28 @@ func NewHorizontalLine(cnf *config.Config) *tview.Box { }) return horizontalLine } + +func NewAccButton(tv *TutView, cnf *config.Config, name string, index int, isActive bool) *tview.Button { + btn := tview.NewButton(name) + style := tcell.Style{} + if !isActive { + style = style.Foreground(cnf.Style.Text) + style = style.Background(cnf.Style.Background) + } else { + style = style.Foreground(cnf.Style.ListSelectedText) + style = style.Background(cnf.Style.ListSelectedBackground) + } + btn.SetActivatedStyle(style) + btn.SetStyle(style) + btn.SetMouseCapture(func(action tview.MouseAction, event *tcell.EventMouse) (tview.MouseAction, *tcell.EventMouse) { + if !btn.InRect(event.Position()) { + return action, event + } + if action != tview.MouseLeftClick { + return action, event + } + TutViews.SetFocusedTutView(index) + return action, nil + }) + return btn +} diff --git a/ui/timeline.go b/ui/timeline.go index 29dfe2b..d5cd674 100644 --- a/ui/timeline.go +++ b/ui/timeline.go @@ -8,7 +8,6 @@ import ( ) type FeedHolder struct { - Name string Feeds []*Feed FeedIndex int } @@ -21,31 +20,47 @@ type Timeline struct { scrollSleep *scrollSleep } -func CreateFeed(tv *TutView, ft config.FeedType, data string, showBoosts, showReplies bool) *Feed { +func (fh *FeedHolder) GetTitle() string { + if fh.FeedIndex >= len(fh.Feeds) { + return "" + } + current := fh.Feeds[fh.FeedIndex] + if len(current.Timeline.Name) > 0 { + return current.Timeline.Name + } + for _, f := range fh.Feeds { + if len(f.Timeline.Name) > 0 { + return f.Timeline.Name + } + } + return "" +} + +func CreateFeed(tv *TutView, f *config.Timeline) *Feed { var nf *Feed - switch ft { + switch f.FeedType { case config.TimelineHome: - nf = NewHomeFeed(tv, showBoosts, showReplies) + nf = NewHomeFeed(tv, f) case config.TimelineHomeSpecial: - nf = NewHomeSpecialFeed(tv, showBoosts, showReplies) + nf = NewHomeSpecialFeed(tv, f) case config.Conversations: - nf = NewConversationsFeed(tv) + nf = NewConversationsFeed(tv, f) case config.TimelineLocal: - nf = NewLocalFeed(tv, showBoosts, showReplies) + nf = NewLocalFeed(tv, f) case config.TimelineFederated: - nf = NewFederatedFeed(tv, showBoosts, showReplies) + nf = NewFederatedFeed(tv, f) case config.Saved: - nf = NewBookmarksFeed(tv) + nf = NewBookmarksFeed(tv, f) case config.Favorited: - nf = NewFavoritedFeed(tv) + nf = NewFavoritedFeed(tv, f) case config.Notifications: - nf = NewNotificationFeed(tv, showBoosts, showReplies) + nf = NewNotificationFeed(tv, f) case config.Mentions: - nf = NewNotificatioMentionsFeed(tv, showBoosts, showReplies) + nf = NewNotificatioMentionsFeed(tv, f) case config.Lists: - nf = NewListsFeed(tv) + nf = NewListsFeed(tv, f) case config.Tag: - nf = NewTagFeed(tv, data, showBoosts, showReplies) + nf = NewTagFeed(tv, f) default: fmt.Println("Invalid feed") tv.CleanExit(1) @@ -61,10 +76,12 @@ func NewTimeline(tv *TutView, update chan bool) *Timeline { } tl.scrollSleep = NewScrollSleep(tl.NextItemFeed, tl.PrevItemFeed) for _, f := range tv.tut.Config.General.Timelines { - nf := CreateFeed(tv, f.FeedType, f.Subaction, f.ShowBoosts, f.ShowReplies) + if f.Closed { + continue + } + nf := CreateFeed(tv, f) tl.Feeds = append(tl.Feeds, &FeedHolder{ Feeds: []*Feed{nf}, - Name: f.Name, }) } for i := 1; i < len(tl.Feeds); i++ { @@ -76,10 +93,75 @@ func NewTimeline(tv *TutView, update chan bool) *Timeline { return tl } -func (tl *Timeline) AddFeed(f *Feed) { - fh := tl.Feeds[tl.FeedFocusIndex] - fh.Feeds = append(fh.Feeds, f) - fh.FeedIndex = fh.FeedIndex + 1 +func (tl *Timeline) AddFeed(f *Feed, newPane bool) { + if f.Timeline.Name == "" && tl.tutView.tut.Config.General.DynamicTimelineName { + name := f.Data.Name() + switch f.Timeline.FeedType { + case config.Favorited: + f.Timeline.Name = "Favorited" + case config.Notifications: + f.Timeline.Name = "Notifications" + case config.Mentions: + f.Timeline.Name = "Mentions" + case config.Tag: + parts := strings.Split(name, " ") + for i, p := range parts { + parts[i] = fmt.Sprintf("#%s", p) + } + f.Timeline.Name = strings.Join(parts, " ") + case config.Thread: + f.Timeline.Name = "Thread" + case config.History: + f.Timeline.Name = "History" + case config.TimelineFederated: + f.Timeline.Name = "Federated" + case config.TimelineHome: + f.Timeline.Name = "Home" + case config.TimelineHomeSpecial: + f.Timeline.Name = "Special" + case config.TimelineLocal: + f.Timeline.Name = "Local" + case config.Saved: + f.Timeline.Name = "Bookmarked" + case config.User: + f.Timeline.Name = fmt.Sprintf("@%s", name) + case config.UserList: + f.Timeline.Name = fmt.Sprintf("Search %s", name) + case config.Conversations: + f.Timeline.Name = "Direct" + case config.Lists: + f.Timeline.Name = "Lists" + case config.List: + f.Timeline.Name = fmt.Sprintf("List %s", name) + case config.Boosts: + f.Timeline.Name = "Boosts" + case config.Favorites: + f.Timeline.Name = "Favorites" + case config.Followers: + f.Timeline.Name = "Followers" + case config.Following: + f.Timeline.Name = "Following" + case config.FollowRequests: + f.Timeline.Name = "Follow requests" + case config.Blocking: + f.Timeline.Name = "Blocking" + case config.ListUsersAdd: + f.Timeline.Name = fmt.Sprintf("Add users to %s", name) + case config.ListUsersIn: + f.Timeline.Name = fmt.Sprintf("Delete users from %s", name) + } + } + + if newPane { + tl.Feeds = append(tl.Feeds, &FeedHolder{ + Feeds: []*Feed{f}, + }) + tl.tutView.FocusFeed(len(tl.Feeds)-1, nil) + } else { + fh := tl.Feeds[tl.FeedFocusIndex] + fh.Feeds = append(fh.Feeds, f) + fh.FeedIndex = fh.FeedIndex + 1 + } tl.tutView.Shared.Top.SetText(tl.GetTitle()) tl.update <- true } @@ -106,7 +188,7 @@ func (tl *Timeline) RemoveCurrent(quit bool) bool { return false } -func (tl *Timeline) MoveCurrentWindowLeft() { +func (tl *Timeline) MoveCurrentPaneLeft() { length := len(tl.Feeds) if length < 2 { return @@ -116,10 +198,10 @@ func (tl *Timeline) MoveCurrentWindowLeft() { return } tl.Feeds[tl.FeedFocusIndex], tl.Feeds[ni] = tl.Feeds[ni], tl.Feeds[tl.FeedFocusIndex] - tl.tutView.FocusFeed(ni) + tl.tutView.FocusFeed(ni, nil) } -func (tl *Timeline) MoveCurrentWindowRight() { +func (tl *Timeline) MoveCurrentPaneRight() { length := len(tl.Feeds) if length < 2 { return @@ -129,31 +211,31 @@ func (tl *Timeline) MoveCurrentWindowRight() { return } tl.Feeds[tl.FeedFocusIndex], tl.Feeds[ni] = tl.Feeds[ni], tl.Feeds[tl.FeedFocusIndex] - tl.tutView.FocusFeed(ni) + tl.tutView.FocusFeed(ni, nil) } -func (tl *Timeline) MoveCurrentWindowHome() { +func (tl *Timeline) MoveCurrentPaneHome() { length := len(tl.Feeds) if length < 2 { return } ni := 0 tl.Feeds[tl.FeedFocusIndex], tl.Feeds[ni] = tl.Feeds[ni], tl.Feeds[tl.FeedFocusIndex] - tl.tutView.FocusFeed(ni) + tl.tutView.FocusFeed(ni, nil) } -func (tl *Timeline) MoveCurrentWindowEnd() { +func (tl *Timeline) MoveCurrentPaneEnd() { length := len(tl.Feeds) if length < 2 { return } ni := len(tl.Feeds) - 1 tl.Feeds[tl.FeedFocusIndex], tl.Feeds[ni] = tl.Feeds[ni], tl.Feeds[tl.FeedFocusIndex] - tl.tutView.FocusFeed(ni) + tl.tutView.FocusFeed(ni, nil) } -func (tl *Timeline) CloseCurrentWindow() { - if len(tl.Feeds) == 0 { +func (tl *Timeline) CloseCurrentPane() { + if len(tl.Feeds) < 2 { return } feeds := tl.Feeds[tl.FeedFocusIndex] @@ -165,7 +247,7 @@ func (tl *Timeline) CloseCurrentWindow() { if ni < 0 { ni = 0 } - tl.tutView.FocusFeed(ni) + tl.tutView.FocusFeed(ni, nil) } func (tl *Timeline) NextFeed() { @@ -191,14 +273,14 @@ func (tl *Timeline) PrevFeed() { tl.update <- true } -func (tl *Timeline) FindAndGoTo(ft config.FeedType, data string, showBoosts, showReplies bool) bool { +func (tl *Timeline) FindAndGoTo(ft config.FeedType, data string, hideBoosts, hideReplies bool) bool { for i, fh := range tl.Feeds { for j, f := range fh.Feeds { - if f.Data.Type() == ft && f.ShowBoosts == showBoosts && f.ShowReplies == showReplies { + if f.Data.Type() == ft && f.Timeline.HideBoosts == hideBoosts && f.Timeline.HideReplies == hideReplies { if ft == config.Tag && f.Data.Name() != data { continue } - tl.tutView.FocusFeed(i) + tl.tutView.FocusFeed(i, nil) fh.FeedIndex = j tl.tutView.Shared.Top.SetText(tl.GetTitle()) tl.update <- true @@ -244,11 +326,11 @@ func (tl *Timeline) GetTitle() string { for i, p := range parts { parts[i] = fmt.Sprintf("#%s", p) } - ct = fmt.Sprintf("tag %s", strings.Join(parts, " ")) + ct = strings.Join(parts, " ") case config.Thread: - ct = "thread feed" + ct = "thread" case config.History: - ct = "history feed" + ct = "history" case config.TimelineFederated: ct = "federated" case config.TimelineHome: @@ -258,7 +340,7 @@ func (tl *Timeline) GetTitle() string { case config.TimelineLocal: ct = "local" case config.Saved: - ct = "saved/bookmarked toots" + ct = "bookmarked" case config.User: ct = fmt.Sprintf("user %s", name) case config.UserList: @@ -268,7 +350,7 @@ func (tl *Timeline) GetTitle() string { case config.Lists: ct = "lists" case config.List: - ct = fmt.Sprintf("list named %s", name) + ct = fmt.Sprintf("list %s", name) case config.Boosts: ct = "boosts" case config.Favorites: diff --git a/ui/top.go b/ui/top.go index 4aeb6cc..16ede00 100644 --- a/ui/top.go +++ b/ui/top.go @@ -25,7 +25,7 @@ func NewTop(tv *TutView) *Top { } func (t *Top) SetText(s string) { - if t.TutView.tut.Client != nil { + if t.TutView.tut.Client != nil && t.TutView.tut.Client.Me != nil { acct := t.TutView.tut.Client.Me us := acct.Acct u, err := url.Parse(acct.URL) @@ -48,7 +48,7 @@ func (t *Top) SetText(s string) { func (t *Top) setText(s string) { t.View.SetText(s) - if t.TutView.tut.Config.General.TerminalTitle > 0 { + if t.TutView.tut.Config.General.TerminalTitle > 0 && t.TutView.tut.Config.General.TerminalTitle != 3 { util.SetTerminalTitle(s) } } diff --git a/ui/tutview.go b/ui/tutview.go index 06e390e..5494c8c 100644 --- a/ui/tutview.go +++ b/ui/tutview.go @@ -35,6 +35,22 @@ type Tut struct { Config *config.Config } +var App *tview.Application +var Config *config.Config +var Accounts *auth.AccountData +var TutViews *TutViewsHolder + +type TutViewsHolder struct { + Views []*TutView + Current int +} + +func SetVars(config *config.Config, app *tview.Application, accounts *auth.AccountData) { + Config = config + App = app + Accounts = accounts +} + type TutView struct { tut *Tut Timeline *Timeline @@ -53,10 +69,14 @@ type TutView struct { PollView *PollView PreferenceView *PreferenceView HelpView *HelpView + EditorView *EditorView ModalView *ModalView + + FileList []string } func (tv *TutView) CleanExit(code int) { + tv.ClearTemp() os.Exit(code) } @@ -95,10 +115,19 @@ func (l *Leader) Content() string { return l.content } -func NewTutView(t *Tut, accs *auth.AccountData, selectedUser string) *TutView { +func NewTutView(selectedUser string) { + if TutViews == nil { + TutViews = &TutViewsHolder{} + } + accs := Accounts tv := &TutView{ - tut: t, - View: tview.NewPages(), + tut: &Tut{ + Client: &api.AccountClient{}, + App: App, + Config: Config, + }, + View: tview.NewPages(), + FileList: []string{}, } tv.Leader = NewLeader(tv) tv.Shared = NewShared(tv) @@ -131,7 +160,52 @@ func NewTutView(t *Tut, accs *auth.AccountData, selectedUser string) *TutView { } else { tv.loggedIn(accs.Accounts[0]) } - return tv + TutViews.Views = append(TutViews.Views, tv) + TutViews.SetFocusedTutView(len(TutViews.Views) - 1) +} + +func (tvh *TutViewsHolder) SetFocusedTutView(index int) { + if index < 0 && index >= len(tvh.Views) { + return + } + tvh.Current = index + curr := tvh.Views[tvh.Current] + App.SetRoot(curr.View, true) + App.SetInputCapture(curr.Input) + if Config.General.MouseSupport { + App.SetMouseCapture(curr.MouseInput) + } + if curr.MainView != nil { + curr.MainView.ForceUpdate() + } +} + +func (tvh *TutViewsHolder) Next() { + if len(tvh.Views) < 2 { + return + } + next := tvh.Current + 1 + if next >= len(tvh.Views) { + next = 0 + } + tvh.SetFocusedTutView(next) +} + +func (tvh *TutViewsHolder) Prev() { + if len(tvh.Views) < 2 { + return + } + prev := tvh.Current - 1 + if prev < 0 { + prev = len(tvh.Views) - 1 + } + tvh.SetFocusedTutView(prev) +} + +func DoneAdding() { + if len(TutViews.Views) > 0 { + TutViews.SetFocusedTutView(0) + } } func (tv *TutView) loggedIn(acc auth.Account) { @@ -181,6 +255,7 @@ func (tv *TutView) loggedIn(acc auth.Account) { tv.PollView = NewPollView(tv) tv.PreferenceView = NewPreferenceView(tv) tv.HelpView = NewHelpView(tv) + tv.EditorView = NewEditorView(tv) tv.ModalView = NewModalView(tv) tv.View.AddPage("main", tv.MainView.View, true, false) @@ -188,13 +263,14 @@ func (tv *TutView) loggedIn(acc auth.Account) { tv.View.AddPage("compose", tv.ComposeView.View, true, false) tv.View.AddPage("vote", tv.VoteView.View, true, false) tv.View.AddPage("help", tv.HelpView.View, true, false) + tv.View.AddPage("editor", tv.EditorView.View, true, false) tv.View.AddPage("poll", tv.PollView.View, true, false) tv.View.AddPage("preference", tv.PreferenceView.View, true, false) tv.View.AddPage("modal", tv.ModalView.View, true, false) tv.SetPage(MainFocus) } -func (tv *TutView) FocusFeed(index int) { +func (tv *TutView) FocusFeed(index int, ct *config.Timeline) { if index < 0 || index >= len(tv.Timeline.Feeds) { return } @@ -210,6 +286,12 @@ func (tv *TutView) FocusFeed(index int) { } } } + for i, tl := range tv.Timeline.Feeds[index].Feeds { + if ct == tl.Timeline { + tv.Timeline.Feeds[index].FeedIndex = i + break + } + } tv.Shared.Top.SetText(tv.Timeline.GetTitle()) tv.Timeline.update <- true } @@ -219,7 +301,7 @@ func (tv *TutView) NextFeed() { if index >= len(tv.Timeline.Feeds) { index = 0 } - tv.FocusFeed(index) + tv.FocusFeed(index, nil) } func (tv *TutView) PrevFeed() { @@ -227,5 +309,5 @@ func (tv *TutView) PrevFeed() { if index < 0 { index = len(tv.Timeline.Feeds) - 1 } - tv.FocusFeed(index) + tv.FocusFeed(index, nil) } diff --git a/ui/view.go b/ui/view.go index 84ff3eb..92519fd 100644 --- a/ui/view.go +++ b/ui/view.go @@ -1,6 +1,8 @@ package ui import ( + "log" + "github.com/RasmusLindroth/go-mastodon" "github.com/RasmusLindroth/tut/api" ) @@ -19,6 +21,7 @@ const ( CmdFocus VoteFocus HelpFocus + EditorFocus PollFocus PreferenceFocus ) @@ -80,6 +83,7 @@ func (tv *TutView) SetPage(f PageFocusAt) { } switch f { case LoginFocus: + log.Fatalln(tv.tut.Client.Me.Acct) tv.PageFocus = LoginFocus tv.View.SwitchToPage("login") tv.Shared.Bottom.StatusBar.SetMode(UserMode) @@ -134,6 +138,11 @@ func (tv *TutView) SetPage(f PageFocusAt) { tv.View.SwitchToPage("help") tv.Shared.Bottom.StatusBar.SetMode(HelpMode) tv.tut.App.SetFocus(tv.HelpView.content) + case EditorFocus: + tv.PageFocus = EditorFocus + tv.View.SwitchToPage("editor") + tv.Shared.Bottom.StatusBar.SetMode(EditorMode) + tv.tut.App.SetFocus(tv.EditorView.editor) case ModalFocus: tv.PageFocus = ModalFocus tv.View.SwitchToPage("modal") diff --git a/util/util.go b/util/util.go index 6f4629e..738a313 100644 --- a/util/util.go +++ b/util/util.go @@ -11,6 +11,7 @@ import ( "github.com/RasmusLindroth/go-mastodon" "github.com/adrg/xdg" "github.com/microcosm-cc/bluemonday" + "github.com/rivo/tview" "golang.org/x/net/html" ) @@ -53,18 +54,48 @@ type URL struct { } func CleanHTML(content string) (string, []URL) { - stripped := bluemonday.NewPolicy().AllowElements("p", "br").AllowAttrs("href", "class").OnElements("a").Sanitize(content) + stripped := bluemonday.NewPolicy().AllowElements("p", "br", "li", "ul", "ol").AllowAttrs("href", "class").OnElements("a").Sanitize(content) urls := getURLs(stripped) - stripped = bluemonday.NewPolicy().AllowElements("p", "br").Sanitize(content) + stripped = bluemonday.NewPolicy().AllowElements("p", "br", "li", "ul", "ol").Sanitize(content) stripped = strings.ReplaceAll(stripped, "
", "\n") stripped = strings.ReplaceAll(stripped, "
", "\n") stripped = strings.ReplaceAll(stripped, "

", "") stripped = strings.ReplaceAll(stripped, "

", "\n\n") + stripped = strings.ReplaceAll(stripped, "
  • ", "* ") + stripped = strings.ReplaceAll(stripped, "
  • ", "\n") + stripped = strings.ReplaceAll(stripped, "
      ", "") + stripped = strings.ReplaceAll(stripped, "
    ", "\n") + stripped = strings.ReplaceAll(stripped, "
      ", "") + stripped = strings.ReplaceAll(stripped, "
    ", "\n") stripped = strings.TrimSpace(stripped) stripped = html.UnescapeString(stripped) return stripped, urls } +func CleanHTMLStyled(content string) (string, []URL) { + stripped := bluemonday.NewPolicy().AllowElements("p", "br", "li", "ul", "ol", "strong", "em").AllowAttrs("href", "class").OnElements("a").Sanitize(content) + urls := getURLs(stripped) + stripped = bluemonday.NewPolicy().AllowElements("p", "br", "li", "ul", "ol", "strong", "em").Sanitize(content) + stripped = strings.ReplaceAll(stripped, "
    ", "\n") + stripped = strings.ReplaceAll(stripped, "
    ", "\n") + stripped = strings.ReplaceAll(stripped, "

    ", "") + stripped = strings.ReplaceAll(stripped, "

    ", "\n\n") + stripped = strings.ReplaceAll(stripped, "
  • ", "* ") + stripped = strings.ReplaceAll(stripped, "
  • ", "\n") + stripped = strings.ReplaceAll(stripped, "
      ", "") + stripped = strings.ReplaceAll(stripped, "
    ", "\n") + stripped = strings.ReplaceAll(stripped, "
      ", "") + stripped = strings.ReplaceAll(stripped, "
    ", "\n") + stripped = html.UnescapeString(stripped) + stripped = tview.Escape(stripped) + stripped = strings.ReplaceAll(stripped, "", TextFlags("b")) + stripped = strings.ReplaceAll(stripped, "", TextFlags("-")) + stripped = strings.ReplaceAll(stripped, "", TextFlags("i")) + stripped = strings.ReplaceAll(stripped, "", TextFlags("-")) + stripped = strings.TrimSpace(stripped) + return stripped, urls +} + func getURLs(text string) []URL { doc := html.NewTokenizer(strings.NewReader(text)) var urls []URL @@ -209,3 +240,7 @@ func StatusOrReblog(s *mastodon.Status) *mastodon.Status { func SetTerminalTitle(s string) { fmt.Printf("\033]0;%s\a", s) } + +func TextFlags(s string) string { + return fmt.Sprintf("[::%s]", s) +} diff --git a/util/xdg.go b/util/xdg.go index e38da9b..1e3449c 100644 --- a/util/xdg.go +++ b/util/xdg.go @@ -1,7 +1,26 @@ package util -import "os/exec" +import ( + "os/exec" + "runtime" +) + +func GetDefaultForOS() (program string, args []string) { + switch runtime.GOOS { + case "windows": + program = "start" + args = []string{"/wait"} + case "darwin": + program = "open" + args = []string{"-W"} + default: + program = "xdg-open" + } + return program, args +} func OpenURL(url string) { - exec.Command("xdg-open", url).Start() + program, args := GetDefaultForOS() + args = append(args, url) + exec.Command(program, args...).Start() }