{"id":17680333,"url":"https://github.com/docelic/taskman","last_synced_at":"2025-10-30T13:36:19.975Z","repository":{"id":32671671,"uuid":"36260122","full_name":"docelic/taskman","owner":"docelic","description":"Advanced console-based personal task scheduler with themes and styles","archived":false,"fork":false,"pushed_at":"2015-07-04T03:13:55.000Z","size":624,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-30T18:48:38.152Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"http://techpubs.spinlocksolutions.com/taskman/","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":"uber/phabricator-jenkins-plugin","license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/docelic.png","metadata":{"files":{"readme":"README","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-05-25T23:34:33.000Z","updated_at":"2017-07-09T19:10:11.000Z","dependencies_parsed_at":"2022-08-29T06:11:32.749Z","dependency_job_id":null,"html_url":"https://github.com/docelic/taskman","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/docelic/taskman","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/docelic%2Ftaskman","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/docelic%2Ftaskman/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/docelic%2Ftaskman/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/docelic%2Ftaskman/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/docelic","download_url":"https://codeload.github.com/docelic/taskman/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/docelic%2Ftaskman/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":281816414,"owners_count":26566766,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-30T02:00:06.501Z","response_time":61,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-24T09:06:33.668Z","updated_at":"2025-10-30T13:36:19.932Z","avatar_url":"https://github.com/docelic.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n              GENERAL INFORMATION ON THE TASKMAN TASK SCHEDULER\n\n                               Version 1.06(08)\n                             2015-06-09T03:53:51Z\n\n                    Copyright 2014-2015 Spinlock Solutions\n                http://techpubs.spinlocksolutions.com/taskman/\n\n\n\nTable of Contents:\n\n1. Basic Information\n2. Task Creation \u0026 Features\n3. Scheduling / Reminder Syntax\n4. Database Sources\n5. Developer Overview\n6. Command Line Options\n\n-----------------------------------------------------------------------------\n\n1. BASIC INFORMATION\n\nTaskman allows creation of task/TODO/event lists and offers extensive\ntracking, scheduling and reminding features.\n\nTaskman is primarily a personal scheduler program, but shared calendars and\nevent lists are planned, as well as plugins for other calendar and task\ntracking apps. Please report your wishes in that regard to help prioritize\nwork.\n\nTaskman's scheduling features have been inspired by a program called Remind,\nalthough Taskman is more powerful and elaborate.\n\nTaskman's default user interface has been inspired by the venerable mailer\nprogram Pine (or free Alpine), although Taskman supports completely\ndifferent/customizable layouts (\"themes\") and color schemes (\"styles\").\n\nTaskman's displayed key bindings and shortcuts have largely been adjusted to\nmatch those of Pine. However, Taskman has also been influenced by\nstandard/generic Unix shortcuts, bash, vim and mutt. (E.g. Ctrl+L for clear\nscreen, / and ? for searching, : for Go-to-Line, N and P for Find Next/Find\nPrev, e for View/Edit).\n\nTaskman's data store is powered by ActiveRecord. Tasks can be viewed and/or\nedited depending on your privileges in the corresponding databases.\n\n-----------------------------------------------------------------------------\n\n2. TASK CREATION \u0026 FEATURES\n\nTaskman's functionality revolves around creating tasks, defining their\nscheduling (recurrence) and reminding options, setting their statuses and\ntracking progress / time spent.\n\nTasks can be created by starting Taskman and navigating to \"CREATE TASK\" in\nthe main menu, or pressing \"C\" (case insensitive). From the command line, task\ncreation can be opened directly by passing option \"-w create\" to the program\n(and the same for all other windows; --window\nmain|create|index|list|help|colortest).\n\nThe most important window is the Create/Edit window, and creating or editing a\ntask supports defining the following options/attributes:\n\n1. Subject: task title / summary\n\n2. Category: list of categories the task belongs to. (A task can belong to\nmultiple categories and it has a separate/individual Priority in each.)\n\n3. Status: status of the task. (A task can only have one status (or no status\nat all).)\n\n4. Start: absolute start date for the task. Before the start date, task will\nbe considered inactive and Taskman will never show it as due or remind you\nabout it. By default, there is no start date (the task is always considered\nactive if it matches other criteria).\n\n5. End: absolute end date for the task. After the end date, task will be\nconsidered inactive and Taskman will never show it as due or remind you about\nit. By default, there is no end date (the task is always considered active if\nit matches other criteria).\n\n6. Time: time of day when the task is due. By default, it is set to 12:00:00.\n\n7. Due dates: days on which the task is due. A task can be due on multiple\ndays, defined as specific dates (e.g. Jan 1, 2015) as well as various\nrepetition rules (e.g. every Monday, every two days between 12th and 22nd of a\nmonth, last working day of a week, etc.).\n\n8. Omit dates: days on which the task should not be due, even if it would\notherwise be due on those days. This field accounts for days being holidays,\nnon-working days (weekends) or any other exceptions to the rule. It supports\nthe same syntax as Due dates.\n\n9. Omit shift: behavior in case a task would be due on an omitted day. Task\ncan either be ignored (not triggered) or Taskman can reschedule it, either to\nthe first earlier or first later non-omitted day.\n\n10. Remind: days and times when Taskman should remind you about the upcoming\ntask or event. It can remind on a specific day or relative to the due date.\nFor example, you might desire Taskman to start reminding you 90 minutes before\nthe event and remind a total of 3 times with 10 minutes in between.\n\n11. Omit remind: behavior in case that a reminder is due on an omitted day.\nReminder can be ignored (not triggered), or Taskman can remind anyway.\n\n-----------------------------------------------------------------------------\n\n3. SUPPORTED SYNTAX\n\nHere's the syntax supported by the above-described fields:\n\nSUBJECT\nSyntax: free-form text\nExample: Test 123\nExample: Meeting with X\n\nCATEGORY\nSyntax: none or more of configured/available task categories\nExample: Work\nExample: Private\n\nSTATUS\nSyntax: none or one of configured/available task statuses\nExample: OPEN\nExample: DONE\n\nSTART\nSyntax: simple date specification\nExample: 2014-01-30\nExample: 20140130\nExample: 30th Dec 2014\n\nEND\nSyntax: simple date specification\nExample: 2015-01-30\nExample: 20150130\nExample: 30th Dec 2015\n\nTIME\nSyntax: HH:MM | HH:MM:SS | -HH:MM | -HH:MM:SS\nExample: 12:30\nExample: 12:30:15\nExample: -2:00 (two hours before midnight)\nExample: -2:00:15\n\nDUE DATES\nSyntax: DAY_NR | -DAY_NR | YEAR | DAY_NAME | MON_NAME | DAY_NR..DAY_NR(STEP)\nSyntax: DAY_NAME..DAY_NAME(STEP) | MON_NAME..MON_NAME(STEP)\nSyntax: YEAR_NR..YEAR_NR(STEP) | *+-aXb | \u003eYEAR_NR\nExample: 3 (every 3rd of month)\nExample: -1 (every last day of month)\nExample: 2012, 2013 (every day in 2012 and 2013)\nExample: MON, TUE (every Monday and Tuesday)\nExample: JAN, FEB (every day in January and February)\nExample: 12..22(2) (every 2 days from 12th to 22nd of month)\nExample: MON..FRI (every workday)\nExample: JAN..DEC(2) (every day of every second month)\nExample: 2012..2014 (every day in 2012, 2013 and 2014)\nExample: *+-aXb\nExample: \u003e2014 (every day in 2014 and onward)\n\nOMIT DATES\nSyntax: same as above\nExamples: same as above\n\nOMIT SHIFT\nSyntax: 1 | 0 | -1\nExample: 1 (reschedule to the first earlier non-omitted day)\nExample: 0 (ignore the task / event, do not reschedule it)\nExample: -1 (reschedule to the first later non-omitted day)\n\nREMIND\nSyntax: -90M*10Mx3 | MON_NAME DAY_NR YEAR HH:MM | YYYY-MM-DD\nExample: -90M*10Mx3 (remind 90 minutes earlier, 3 times every 10 minutes)\nExample: Jan 1 2014 12:00 (specific date and time)\nExample: 2014-01-01 (specific date)\n\nOMIT REMIND\nSyntax: 0 | 1\nExample: 0 (remind regardless of being an omitted day)\nExample: 1 (ignore the reminder, do not trigger it)\n\n-----------------------------------------------------------------------------\n\n4. DATABASE SOURCES\n\nTaskman supports defining multiple databases, with one database always being\nprimary. The idea behind this feature is that users would have their own,\nprimary database. However, they could connect to other users' databases to see\nthe tasks that have been shared with them.\n\nIf multiple databases are configured, the tasks do not show all at once.\nInstead, the view shows tasks from the database currently selected as primary.\nTo see tasks from another database, in the \"Main\", \"Folder List\" or \"Index\"\nwindows press Backspace to see the list of available, configured databases.\n\nCreating new or editing existing tasks in other databases works as well, as\nlong as the user has sufficient privileges in the corresponding databases.\nTasks that a person clones can be cloned into either the current database, or\ninto the user's primary one.\n\n-----------------------------------------------------------------------------\n\n5. DEVELOPER OVERVIEW\n\nTaskman execution starts in main.rb. It instantiates the TASKMAN::Application\nclass in global variable $app to represent the application (similar to how Qt\nwould do it), defines and parses command line options, loads Ruby and local\nmodules, prints --help if it was requested, initializes the console, and\nstarts the main loop.\n\nThe following global variables are available at all times:\n\n1. $LINES (int), $COLUMNS (int) - terminal height and width\n2. $opts (hash) - current program options (program defaults modified by config\nfile and/or the command line)\n3. $getopts (array) - getopt definitions (probably not relevant if not doing\nsomething specifically related to parsing command line options)\n4. $app (object) - instance of TASKMAN::Application. Most useful accessors are\n$app.ui (STFL object) and $app.screen (Ruby object of toplevel element visible\non screen, and from there you can access all children regardless of position\nin hierarchy via $app.screen[WIDGET_NAME])\n5. $app.usage() - function that returns a formatted string of command line\noptions (output of taskman -h)\n6. $session - store for current/running parameters, e.g. the current folder a\nperson wants to be in.\n\nMain loop:\n----------\n\nOnce main.rb sets everything up, it calls $app.start() which creates the\nrequested window and runs the main loop. Window is created by invoking\n$app.exec(), the same function that is used to switch from one window to\nanother.\n\nThe main loop that start() runs is based around STFL's main loop\nimplementation + a couple conveniences that you therefore don't need to be\nimplementing yourself. Here's their listing:\n\n1) Our main_loop runs in the context of a window, instead of globally, so the\ncurrent window object is always readily available as 'self'. Similarly, all\nyour functions that Taskman core will call will be called with a rich set of\narguments, allowing for easy and comfortable implementation of various\nhandlers, menu actions, etc. (all defined in TASKMAN::MenuAction\n(menuaction.rb)).\n\n2) The main loop automatically creates pointer to the current/focused widget\nobject (instead of you having to look for it manually), and it automatically\ndisplays the focused widget's tooltip in the \"status\" line, if the focused\nwidget has a tooltip and the status widget exists on the current form. An\nexample of this is e.g. seeing syntax help in the status line while you are\npressing Up/Down over the various task option inputs in the \"Create Task\"\nwindow.\n\n3) If the widget under focus has an action associated with it, the main loop\nautomatically adjusts the \"hotkey\" entry in the window menu, if \"hotkey_in\"\naction exists on the current form. (If there are multiple actions associated,\nit takes the first one which is suitable, i.e. the first one which is not\nexplicitly set not to be a default action with \"default: false\".) An example\nof this is e.g. when you are in the program's main window and are pressing\nUp/Down to move between the options (Create Task, Task Index, etc.). The\nHotkey field in the menu at the bottom changes automatically.\n\n4) If ENTER is pressed on a widget, the main loop will automatically execute\nits first default action, if one is associated with it. An example of this is\ne.g. when you are in the program's main window and you press ENTER on 'CREATE\nTASK' in the list. It executes the associated action which opens the Create\nTask window.\n\n5) Keypresses other than ENTER which are not handled by the widget go through\na search list: we check whether the key pressed matches any of the hotkeys\nassociated with the widget itself, then the window's menus, then all the\nwidget's parents up to the top of the tree. An example of this is e.g. when\nyou are in the CREATE TASK window, and press Ctrl+X to create the task. The\nCtrl+X is a hotkey matching one of the entries in the window's menu, and it\ngets executed. After it has processed the key, each keypress handler is\nexpected to return the (possibly new/different) event key, or nil to stop\nfurther processing. (If the return value from an action is not nil, the\noriginal event will be replaced with the return value and searching for\nmatching actions will continue down the list.)\n\nNow, a chapter on STFL:\n-----------------------\n\nIn its essence, STFL is a textual GUI definition language and it is not\nobject-oriented. In Taskman, a complete wrapper has been written so that you\nwould only manipulate Ruby objects, and the STFL part would automagically take\ncare of itself.\n\nFor example, to create a window named \"Hello\" and display a label in it, you\nwould simply call:\n\nmodule TASKMAN\n  class Theme::Window::Hello \u003c Theme::Window\n\n    def initialize *arg\n      super\n      @widget= 'vbox'\n      self\u003c\u003c Label.new( name: 'lbl1', text: 'Hello, World!')\n    end\n\n  end\nend\n\nIf you saved that to a file theme/alpine/window/hello.rb and ran 'ruby main.rb\n-w hello', it would work! (In fact, that file exists in the Taskman\ndistribution for demonstration purposes; simply run taskman with '-w hello' to\nsee it.)\n\nInsights to pick up from that example:\n\n1) Every class wishing to use STFL needs to inherit from one of STFL-derived\nclasses (in this case Theme::Window), which in turn all inherit from our base\nclass named StflBase, which ultimately uses Stfl.rb that comes from the STFL\ndistribution.\n\n2) When a window (or any StflBase-derived object) needs to be converted to\nSTFL text, it happens via calling .to_stfl() on it. That function is defined\nin our stfl_base.rb and it outputs a desired element in STFL with the\ncorresponding name and options, and it also STFL-izes all its children\nelements and returns the complete STFL. If the object has no name (because it\nis e.g. just a supporting element that you will never want to access or be\ninterested in, such as a supporting HBox or VBox) then its name will be\nautogenerated, in the pattern of W_0, W_1, W_2, etc. (To be technically\ncorrect, auto-assigning a name happens during calling .new() on a\nStflBase-derived class if the name parameter is not provided among the\noptions, and not during a later call to .to_stfl()).\n\n3) The toplevel STFL element seen in the text generated by .to_stfl() is\ncoming from instance variable @widget, and all typical widget types (such as\nlabel, checkbox etc., found in the widget/ subdirectory) have their values for\n@widget appropriately set. But you could manipulate @widget manually to\noverride the element type in STFL, or set @widget = nil to just STFL-ize the\nchildren without creating a STFL representation of the current object.\n\n4) In the \"Hello, World!\" example we can also see the syntax \"self\u003c\u003c\nLabel.new(...)\". The \"\u003c\u003c\" is an operator defined in stfl_base.rb, and it is\nused to add a widget (Label in our case) as a child of parent ('self' in our\ncase). It does this by adding a widget to parent's variables @widgets and\n@widgets_hash, and also handles hotkeys if the child being added is a\nMenuAction instead of a simple widget. In any case, please always use this\n'parent\u003c\u003c child' syntax instead of manually manipulating the described\nvariables.\n\n5) Final thing to know re. the basic STFL is that it is flat, there is no\nhierarchy and all elements can be accessed by their name directly from\ntoplevel API.\n\nIn Taskman, $app.ui always points to this basic STFL form currently shown, and\nit allows one to e.g. read form value of e.g. an input field by simply calling\n$app.ui.get 'OBJ_NAME_text'. This is basic, standard STFL usage as one would\nlearn from reading STFL docs.\n\nHowever, our Ruby wrapper is much more elaborate. First of all, it is aware of\nthe hierarchy. Each widget has a .parent() pointing to its parent, and\n@widgets/@widgets_hash accessors pointing to its children. When \u003c\u003c is used, it\nforcibly sets child widget's parent to the widget it was added to. (Similar to\nhow Qt does it.) It is also what allows one to call widget.to_stfl() and get\nthe complete structure of Ruby objects (the parent and all children) converted\nto STFL text ready for displaying on the screen. If you will be doing any\ntrickery besides plain using \u003c\u003c and \u003e\u003e to add and remove children on\nStflBase-derived objects, consider calling OBJ.clear_caches() once you are\ndone. It will rebuild the cached output of .all_widgets_hash() and .to_stfl()\non your next access.\n\nFurthermore, as part of our Ruby wrapper, $app.screen always points to the\ntoplevel widget object that was used in generating the STFL. (This is\ntypically an object inheriting from Theme::Window (which, as described,\ninherits from Window, which inherits from StflBase, which uses Stfl.rb from\nSTFL distribution). However, any StflBase-based object could be used, since\nSTFL itself places on restrictions on the toplevel element that is used to\ninitialize/draw a window.)\n\nSince $app.screen is a widget, it allows the usual access to @widgets and\n@widgets_hash to access all of the children (which means all of the widgets on\nthe screen). Most of the time, however, the widget you want to access is not\ndirectly under the parent but under some intermediate element(s) such as\nVBoxes, HBoxes, menus, etc. and it means you would have to search through a\nchain of children trees for it. To alleviate that, there is a function\n$app.screen.all_widgets_hash() which returns all of the widget's children and\nsub-children in a flat structure, so you can simply call\n$app.screen.all_widgets_hash['YOUR_WIDGET'] to obtain a reference to your\nobject. Since this is a so commonly used function, even more convenient\nshorthand exists -- simply $app.screen[CHILD_NAME].\n\nAlso, since the Ruby wrapper calls all action handlers with a rich standard\nset of parameters (window, widget, action, function and event), all the child\nwidgets in the form are generally always available to you by simply calling\narg[:window][NAME] in your handler code. Or if arg[:window] is not there, you\ncan access the intended child widget via $app.screen[NAME].\n\nOnce you have the object, you can call arbitrary functions on it that affect\nboth internal variables and visual representation in real-time. For example,\nto read or set the text of a label you would call lbl.var_text and\nlbl.var_text= \"New text!\" respectively. Note that the setter function\n(.var_text=) operates in real-time-- it will set both the internal variable\nand the value in STFL, and the change will appear on the screen immediately.\nHowever, the getter function (.var_text) will only return the value of the\nwidget's variable 'text', which does not necessarily match its current value\non the form. To read the actual value from the form, as well as update the\ninternal variable, call .var_VARNAME_now(), such as .var_text_now().\n\nThis way, STFL has been completely abstracted and there are even advantages to\nnot using it directly, but reading STFL documentation will certainly help you\nbetter understand how it all works. You'll be able to recognize which parts of\nTaskman are application-level design and which are simple necessities of STFL.\n\nTo see how this works in an upgraded 'Hello, World!'-style example, please\njust run Taskman with option -w hello2, and after seeing it run also examine\nthe source file in theme/alpine/window/hello2.rb.\n\nOn actions:\n-----------\n\nIn addition to adding children widgets to parents (via parent\u003c\u003c child), one\ncan also add \"actions\" in the same way.\n\nActions are basically functions to execute on events, usually keypresses. For\nexample, pressing ENTER in a field could serve as form's OK function that\ntriggers further processing, or pressing Ctrl+X in create window would create\na new task.\n\nActions can be invisible (be added to widgets and work, but not be\nvisible/advertised anywhere), or they can have a visual representation to be\nmore convenient or better indicate their availability. When they are visible,\nthey are usually found in the window's menu bar. An example of it are all the\nmenu options visible the bottom of the main window as soon as you start the\nprogram.\n\nInvisible actions are created by either using obj\u003c\u003c MenuAction.new( name:\nNAME), which instantiates a stock action, or by creating an action manually\nand overriding/defining its functionality in more detail (a= MenuAction.new(\n...)) and then using obj\u003c\u003c a.\nAnother way to add invisible (but pre-defined) stock actions to a widget\n(especially when you want to add many stock actions at once), is to call \u003cyour\nwidget\u003e.add_action( :name1, :name2, ...).\n\nOn the other hand, actions that have a visible representation are created\nusing Theme::MenuAction( name: NAME), and the rest of the notes apply as for\nMenuAction above. Examples of these are most commonly found in menus displayed\nwithin windows, with their exact look/layout implemented in\ntheme/NAME/MenuAction.rb.\n\nGenerally, all your theme-agnostic actions should be implemented in\nmenuaction.rb, and all your theme-specific actions should be implemented in\ntheme/NAME/menuaction.rb. A function in a theme that is generally useful\nshould be adjusted to be generic and added to the basic menuaction.rb.\n\nA chapter on themes and styles:\n-------------------------------\n\nTaskman supports both themes (different GUI layouts) and styles (different\ncolor schemes).\n\nThere is no direct relation between a theme and style. The more general a\nstyle is (that is, the more it targets broad widget names and classes rather\nthan specifics), the more likely is it to apply correctly to another theme.\n(No harm in trying; worst case would be that no selector applies to a\nparticular widget, leaving it unstyled, or that multiple selectors match, of\nwhich the most specific one would win.)\n\nThere is currently only one theme (GUI layout) available -- Alpine, mimicking\nthe layout of the infamous mail program Pine. At least one more theme needs to\nbe written to actually test the supposedly theme-agnostic code in practice and\nto make sure different themes are realistically possible. For example, one\ncould make a theme resembling the mail program 'mutt', or something different\naltogether. If you embark on that journey, get in touch to ensure proper\nsupport from my side.\n\nThere are currently a couple styles (color schemes) available. The default one\nis called 'alpine', matching the default theme. Others available are 'none'\nfor no any special styling and 'random' for a completely random color scheme\non each display of a window (and best random results are had with --style\nrandom --colors 256, if your terminal supports 256 colors - which most Linux\nterminals do). Style files are very simple and it is expected that they will\nbe customized - or that new ones will be created - much more often than\ncomplete themes.\n\nLocally, you can place your themes in ~/.taskman/lib/theme/THEME_NAME/ and\ndesign them by using the default Alpine theme as a reference. To use your\ntheme, simply invoke Taskman with -t THEME_NAME.\n\nYou can place your styles in ~/.taskman/lib/style/, using existing styles for\nreference. To use your style, simply invoke Taskman with -s STYLE_NAME.\n\nThe most important thing in writing a style is knowing how the element you\nwant to style is called, so that you can write a selector for it. The names\nwill inevitably vary from theme to theme, but it is advised that theme authors\nfollow the names used in the Alpine theme where ever applicable.\n\nAnother thing to understand is how styles are applied. For each widget,\nTaskman determines its hierarchical position in the form and then tries to\nfind the its matching style definition, with decreasing specificity.\n\nFor example: when you start Taskman using the default Alpine theme, you will\nnotice the program name and version displayed in the top-left of the window.\nThis widget is called \"header_program_name_version\", it is of type 'label',\nand it displays the program name and version.\n\nTaskman is dynamically aware that this widget is a child of 'header', which in\nturn is a child of 'main' (the main program window), and it tries to apply the\nstyle by searching for the style keys in the following order:\n\n\"main header header_program_name_version\"\n\"main header @label\"\n\"main header\"\n\"header header_program_name_version\"\n\"header @label\"\n\"header\"\n\"main header_program_name_version\"\n\"main @label\"\n\"main\"\n\"header_program_name_version\"\n\"@label\"\n\nSimilarly, here's a lookup that takes place when one part (the hotkey label)\nof one of the available menu actions is being rendered:\n\n\"main menu menu_create_hotkey\"\n\"main menu @hotkey\"\n\"main menu @label\"\n\"main menu\"\n\"menu menu_create_hotkey\"\n\"menu @hotkey\"\n\"menu @label\"\n\"menu\"\n\"main menu_create_hotkey\"\n\"main @hotkey\"\n\"main @label\"\n\"main\"\n\"menu_create_hotkey\"\n\"@hotkey\"\n\"@label\"\n\nHere, we notice that Taskman searched not only for the widget name and its\ntype (@label), but also for its class name (@hotkey). When the widget's class\nname does not match its STFL element, Taskman also searches for the class\nname, allowing for very convenient styling that would otherwise be hard to\napply to multiple widgets of otherwise the same type in a single rule.\n\nThe search for each style key is done in such a way that Taskman iterates over\nall defined style keys in your style file, testing them sequentially in the\norder as they were specified. The first selector that matches \"wins\".\n\nIt is important to know that the selectors in your style definitions can be\nliteral strings, regexes or code blocks. A selector will win (be chosen) if\nthe literal string is equal, the regex matches, or the code block returns\ntrue.\nSimilarly, the actual style definitions for the key can be hashes (specifying\nvalues for \"normal\", \"focus\" and \"selected\" styles - but more on that below),\nor they can be code blocks which are ran and expected to return such hashes.\nAn example of using code blocks can be seen in the style \"random\", where it is\nused to randomly color each widget.\n\nIt is also useful to know that the style is not applied to widgets that are\nnot to be rendered in STFL (those with @widget= nil). Also, if you change the\nSTFL widget type of an object (for example, if you create a Label but set its\n@widget to be e.g. 'input'), then after all the \"@label\" lookups, Taskman wil\nalso search for \"@input\" before shifting the leftmost element from the path\nand trying another round.\n\nAlso, in searching for styles, Taskman automatically removes the numbers that\nimmediately follow letters at the end of widget names. Specifically, if your\nwidget is named \"menu2\" and is found under \"main\", Taskman will search for\n\"main menu\", not \"main menu2\".\n\nDiscovering widget names and hierarchy:\n---------------------------------------\n\nWhen writing your style (or modifying an existing one) there are multiple ways\nin which you can discover the names or the hierarchy of the widgets you want\nto style:\n\n1. Look up existing definitions in style/alpine.rb. It is possible the\nselectors are already there and you simply need to change their definitions in\nyour own style file.\n\n2. Look up the program source of the window you are styling, and locate the\nnames of the widgets you are interested in.\n\n3. Run the program with option --debug-stfl and redirect STDERR output to a\ntemporary file, such as: taskman --debug-stfl 2\u003e /tmp/debug.log. In the debug\nfile, you will see the complete STFL text that was used to produce the current\nform, and you will be able to find your widget by searching for chunks of text\nvisible in or around it. Alternatively, if you know the name of the toplevel\nwidget or one of its parents, you can narrow the log by dumping only their\nSTFL; you can do this with --debug-stfl-widget WIDGET_NAME.\n\n4. You could also run the program with option --debug-style and redirect\nSTDERR output to a temporary file. The log would show you all style lookups\nthe program has performed, and you could recognize the name of your widget in\nthe list.\n\nOnce you have the name of the target widget you want to style, you can run\nTaskman with option --debug-style-widget WIDGET_NAME (redirecting STDERR to a\ntemp file as usual), and you will see the exact keys being looked up in trying\nto style the widget. From there, simply pick the one having the desired level\nof specificity and define a style for it in style/YOUR_STYLE.rb.\n\nFinally, it needs to be said that Taskman directly inherits STFL's styling\ncapabilities, which means that there are 3 styles you can define for each\nwidget: style 'normal', 'focus' and 'selected'.\n\nStyle 'normal' is the default representation. Style 'focus' is the\nrepresentation when the widget is focused. Style 'selected' applies to lists\nand defines the representation of the selected item in the list when the list\nitself is not focused.\n\nTaskman also uses direct style syntax that is used by STFL; it is a comma\nseparated list of key=value pairs, where the key can be 'bg' for background,\n'fg' for foreground and 'attr' for text attributes.\n\nTo define a blue background with white, bold and blinking text on it, you\nwould use:\n\n  \"bg=blue,fg=white,attr=bold,attr=blink\"\n\nThe following fg and bg colors are supported:\n\n  black red green yellow blue magenta cyan white\n\nThe following text attributes are supported:\n\n  standout underline reverse blink dim bold protect invis\n\nOn terminals that support more than 8 colors (16, 256) it is also possible to\nuse extended colors, by using \"color\u003cnumber\u003e\" as color names, where \"\u003cnumber\u003e\"\nis a number between 0 and 255. For a complete chart of numbers and their\ncorresponding colors, you could run \"taskman --co 256 -w colortest\", or see\nhttp://www.calmar.ws/vim/256-xterm-24bit-rgb-color-chart.html.\n\n(The built-in color test program is invoked with taskman -w colortest, and\nwill display all colors currently supported by your terminal. Adding --colors\n\u003cNUM\u003e to that would force a specific number of colors and then display the\ncolortest.)\n\nColors (--co) can set the number of colors to something other than the default\n8. The most common color settings are --co 16 and --co 256. (These depend on\navailable terminal types and capabilities in terminfo, can't be arbitrary.)\n\nAnd one last note, please note that STFL uses the default terminal colors when\nno background or foreground are specified. Generally, you should avoid\nspecifying only fg= or bg=, as the particular combination of one setting and\nthe terminal's default style for the other might be unreadable.\n\n\n6. COMMAND LINE OPTIONS\n\n                                   Taskman\n                                Ver. 1.06(08)\n\nOPTION                SHORT       ARG?  DEFAULT  DESCRIPTION\n------------------------------------------------------------------------------\n--help                -h           N             List program options\n--help-all            -H           N             Display full help text\n--help-install        --hinstall   N             Display install help text\n--version             -v           N    1.06(08) Display program version\n--garbage-collector   --gc         N    true     Run garbage collector?\n--stress-collector    --stress     N    false    Stress garbage collector?\n--window              -w           Y             Program window to open\n--theme               -t           Y    alpine   GUI layout scheme to use\n--style               -s           Y    alpine   GUI color scheme to use\n--state-save          --ss         N    true     Save state on exit?\n--state-load          --sl         N    true     Load state on startup?\n--data-dir            --dd         Y    /root/.taskmanData directory\n--data-file           --df         Y             Tasklist file\n--exit-key            --ek         Y    F10      One-key exit from program\n--main-db             --db         Y    main     Name of main/own database\n--echo-time           --ec         Y    0.3      Duration of msg visibility\n--timeout             --loop       Y    0        Force main loop every X ms\n--tooltips            --tips       N    true     Show automatic tooltips\n--history-lines       --hist       Y    10       Length of history buffers\n--focus-on-edit       --foe        Y    message  Field to focus on Edit Task\n--focus-on-create     --foc        Y    subject  Field to focus on Create -\n--follow-jump         --fj         N    true     In lists, follow current task\n--term                --te         Y             Value for TERM= emulator\n--term-width          --tw         Y             Term width to use\n--colors              --co         Y    8        Nr. of colors (8, 16, 256)\n--cache-stfl          --tsc        N    true     Enable/disable STFL cache\n--cache-avh           --avhc       N    true     - internal hierarchy cache\n--connection --conn Y {:main=\u003e[{:adapter=\u003e\"mysql2\", :host=\u003e\"localhost\",\n:username=\u003e\"taskman\", :password=\u003e\"taskman\", :database=\u003e\"taskman\"}]}Define\nMySQL connections\n--priority-granularity--pg         Y    1        Priority = 0-9 * this_value\n--debug               -d           N    false    Show general debug\n--debug-widget        --dw         Y             Show all for named widget\n--debug-keys          --dk         N    false    Show keypresses\n--debug-keys-widget   --dkw        Y             Show keys on named widget\n--debug-opts          --do         N    false    Show command line options\n--debug-style         --ds         N    false    Show application of style\n--debug-style-widget  --dsw        Y             Show style for named widget\n--debug-stfl          --dstfl      N    false    Show generated STFL\n--debug-stfl-widget   --dstflw     Y             Show STFL for named widget\n--debug-mvc           --dmvc       N    false    Show MVC logic\n--debug-mvc-widget    --dmvcw      Y             Show MVC on named widget\n--debug-sql           --dsql       N    false    Show SQL queries\n--index-fields --fields Y [:pre, :flags, :id, :status, :subject]Columns to\nshow in Task Index\n--index-delimiter     --delimiter  Y             Delimiter between fields\n--content-pre         --cpre       Y             Content for spacer element\n--format-pre          --fpre       Y    %s       Sprintf format for pre\n--format-flags        --fflags     Y    %1s      Sprintf format for flags\n--format-id           --fid        Y    %-4s     Sprintf format for id\n--format-status       --fstatus    Y    %-7s     Sprintf format for status\n--format-subject      --fsubject   Y    %s       Sprintf format for subject\n--format-priority     --fpriority  Y    %3s      Sprintf format for priority\n--format-DEFAULT      --fDEFAULT   Y    %10s     Sprintf format (DEFAULT)\n--title-pre           --tpre       Y             Column title for pre\n--title-flags         --tflags     Y             Column title for flags\n--title-id            --tid        Y    ID       Column title for id\n--title-status        --tstatus    Y    STATUS   Column title for status\n--title-subject       --tsubject   Y    SUBJECT  Column title for subject\n--title-priority      --tpriority  Y    PRI      Column title for priority\n--title-DEFAULT       --tDEFAULT   Y             Column title (DEFAULT)\n------------------------------------------------------------------------------\n\nEnjoy!\n\n \n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdocelic%2Ftaskman","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdocelic%2Ftaskman","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdocelic%2Ftaskman/lists"}