Full Code of wtfutil/wtf for AI

trunk 108cab807b76 cached
519 files
1.1 MB
358.6k tokens
2585 symbols
1 requests
Download .txt
Showing preview only (1,266K chars total). Download the full file or copy to clipboard to get everything.
Repository: wtfutil/wtf
Branch: trunk
Commit: 108cab807b76
Files: 519
Total size: 1.1 MB

Directory structure:
gitextract_iprghv20/

├── .editorconfig
├── .gitattributes
├── .github/
│   ├── FUNDING.yml
│   ├── ISSUE_TEMPLATE/
│   │   ├── Bug.md
│   │   ├── Feature.md
│   │   └── Support.md
│   ├── PULL_REQUEST_TEMPLATE/
│   │   ├── Improvement.md
│   │   └── Other.md
│   ├── PULL_REQUEST_TEMPLATE.md
│   ├── dependabot.yml
│   ├── stale.yml
│   └── workflows/
│       ├── codeql-analysis.yml
│       ├── golangci-lint.yml
│       ├── goreleaser.yml
│       ├── pr-checks.yml
│       └── staticcheck.yml
├── .gitignore
├── .gitmodules
├── .golangci.yml
├── .goreleaser.yml
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE.md
├── Makefile
├── README.md
├── SECURITY.md
├── _sample_configs/
│   ├── bargraph_config.yml
│   ├── dynamic_sizing.yml
│   ├── kubernetes_config.yml
│   ├── sample_config.yml
│   ├── small_config.yml
│   └── uniconfig.yml
├── app/
│   ├── app_manager.go
│   ├── display.go
│   ├── exit_message.go
│   ├── exit_message_test.go
│   ├── focus_tracker.go
│   ├── module_validator.go
│   ├── module_validator_test.go
│   ├── scheduler.go
│   ├── scheduler_test.go
│   ├── widget_maker.go
│   ├── widget_maker_test.go
│   └── wtf_app.go
├── cfg/
│   ├── common_settings.go
│   ├── common_settings_test.go
│   ├── config_files.go
│   ├── copy.go
│   ├── default_color_theme.go
│   ├── default_color_theme_test.go
│   ├── default_config_file.go
│   ├── error_messages.go
│   ├── parsers.go
│   ├── parsers_test.go
│   ├── position_settings.go
│   ├── position_validation.go
│   ├── position_validation_test.go
│   ├── secrets.go
│   ├── validatable.go
│   ├── validations.go
│   └── validations_test.go
├── checklist/
│   ├── checklist.go
│   ├── checklist_item.go
│   ├── checklist_item_test.go
│   └── checklist_test.go
├── flags/
│   └── flags.go
├── generator/
│   ├── settings.tpl
│   ├── textwidget.go
│   └── textwidget.tpl
├── go.mod
├── go.sum
├── help/
│   └── help.go
├── logger/
│   └── log.go
├── main.go
├── modules/
│   ├── airbrake/
│   │   ├── client.go
│   │   ├── group_info_table.go
│   │   ├── keyboard.go
│   │   ├── result_table.go
│   │   ├── settings.go
│   │   ├── util.go
│   │   └── widget.go
│   ├── asana/
│   │   ├── client.go
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── azuredevops/
│   │   ├── client.go
│   │   ├── example-conf.yml
│   │   ├── settings.go
│   │   └── widget.go
│   ├── azurelogs/
│   │   ├── config.go
│   │   ├── config_test.go
│   │   ├── query.go
│   │   ├── query_concurrent_test.go
│   │   ├── query_test.go
│   │   ├── session.go
│   │   ├── session_test.go
│   │   ├── settings.go
│   │   ├── settings_test.go
│   │   ├── widget.go
│   │   └── widget_test.go
│   ├── bamboohr/
│   │   ├── calendar.go
│   │   ├── client.go
│   │   ├── employee.go
│   │   ├── item.go
│   │   ├── request.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── bargraph/
│   │   ├── settings.go
│   │   └── widget.go
│   ├── buildkite/
│   │   ├── client.go
│   │   ├── keyboard.go
│   │   ├── pipelines_display_data.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── cds/
│   │   ├── favorites/
│   │   │   ├── display.go
│   │   │   ├── keyboard.go
│   │   │   ├── settings.go
│   │   │   └── widget.go
│   │   ├── queue/
│   │   │   ├── display.go
│   │   │   ├── keyboard.go
│   │   │   ├── settings.go
│   │   │   └── widget.go
│   │   └── status/
│   │       ├── display.go
│   │       ├── keyboard.go
│   │       ├── settings.go
│   │       └── widget.go
│   ├── circleci/
│   │   ├── build.go
│   │   ├── client.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── clocks/
│   │   ├── clock.go
│   │   ├── clock_collection.go
│   │   ├── display.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── cmdrunner/
│   │   ├── settings.go
│   │   └── widget.go
│   ├── cryptocurrency/
│   │   ├── bittrex/
│   │   │   ├── bittrex.go
│   │   │   ├── display.go
│   │   │   ├── settings.go
│   │   │   └── widget.go
│   │   ├── blockfolio/
│   │   │   ├── settings.go
│   │   │   └── widget.go
│   │   ├── cryptolive/
│   │   │   ├── price/
│   │   │   │   ├── price.go
│   │   │   │   ├── settings.go
│   │   │   │   └── widget.go
│   │   │   ├── settings.go
│   │   │   ├── toplist/
│   │   │   │   ├── display.go
│   │   │   │   ├── settings.go
│   │   │   │   ├── toplist.go
│   │   │   │   └── widget.go
│   │   │   └── widget.go
│   │   └── mempool/
│   │       ├── settings.go
│   │       └── widget.go
│   ├── datadog/
│   │   ├── client.go
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── devto/
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── digitalclock/
│   │   ├── clocks.go
│   │   ├── display.go
│   │   ├── fonts.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── digitalocean/
│   │   ├── display.go
│   │   ├── droplet.go
│   │   ├── droplet_properties_table.go
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── docker/
│   │   ├── client.go
│   │   ├── example-conf.yml
│   │   ├── settings.go
│   │   ├── utils.go
│   │   └── widget.go
│   ├── feedreader/
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   ├── widget.go
│   │   └── widget_test.go
│   ├── football/
│   │   ├── client.go
│   │   ├── settings.go
│   │   ├── types.go
│   │   ├── util.go
│   │   └── widget.go
│   ├── gcal/
│   │   ├── cal_event.go
│   │   ├── client.go
│   │   ├── display.go
│   │   ├── display_test.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── gerrit/
│   │   ├── display.go
│   │   ├── gerrit_repo.go
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── git/
│   │   ├── display.go
│   │   ├── git_repo.go
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   ├── variables.go
│   │   ├── variables_win.go
│   │   └── widget.go
│   ├── github/
│   │   ├── display.go
│   │   ├── github_repo.go
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── gitlab/
│   │   ├── display.go
│   │   ├── gitlab_project.go
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── gitlabtodo/
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── gitter/
│   │   ├── client.go
│   │   ├── gitter.go
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── googleanalytics/
│   │   ├── client.go
│   │   ├── display.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── grafana/
│   │   ├── client.go
│   │   ├── display.go
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── gspreadsheets/
│   │   ├── client.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── hackernews/
│   │   ├── client.go
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   ├── story.go
│   │   ├── story_test.go
│   │   └── widget.go
│   ├── healthchecks/
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── hibp/
│   │   ├── client.go
│   │   ├── hibp_breach.go
│   │   ├── hibp_status.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── ipaddresses/
│   │   ├── ipapi/
│   │   │   ├── settings.go
│   │   │   └── widget.go
│   │   └── ipinfo/
│   │       ├── settings.go
│   │       └── widget.go
│   ├── jenkins/
│   │   ├── client.go
│   │   ├── job.go
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   ├── view.go
│   │   └── widget.go
│   ├── jira/
│   │   ├── client.go
│   │   ├── client_test.go
│   │   ├── issues.go
│   │   ├── keyboard.go
│   │   ├── search_result.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── krisinformation/
│   │   ├── client.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── kubernetes/
│   │   ├── client.go
│   │   ├── settings.go
│   │   ├── widget.go
│   │   └── widget_test.go
│   ├── logger/
│   │   ├── settings.go
│   │   └── widget.go
│   ├── lunarphase/
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── mercurial/
│   │   ├── display.go
│   │   ├── hg_repo.go
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── nbascore/
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── newrelic/
│   │   ├── client/
│   │   │   ├── README.md
│   │   │   ├── alert_conditions.go
│   │   │   ├── alert_events.go
│   │   │   ├── application_deployments.go
│   │   │   ├── application_host_metrics.go
│   │   │   ├── application_hosts.go
│   │   │   ├── application_instance_metrics.go
│   │   │   ├── application_instances.go
│   │   │   ├── application_metrics.go
│   │   │   ├── applications.go
│   │   │   ├── array.go
│   │   │   ├── browser_applications.go
│   │   │   ├── component_metrics.go
│   │   │   ├── http_helper.go
│   │   │   ├── key_transactions.go
│   │   │   ├── legacy_alert_policies.go
│   │   │   ├── main.go
│   │   │   ├── metrics.go
│   │   │   ├── mobile_application_metrics.go
│   │   │   ├── mobile_applications.go
│   │   │   ├── notification_channels.go
│   │   │   ├── server_metrics.go
│   │   │   ├── servers.go
│   │   │   └── usages.go
│   │   ├── client.go
│   │   ├── display.go
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── nextbus/
│   │   ├── settings.go
│   │   └── widget.go
│   ├── opsgenie/
│   │   ├── client.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── pagerduty/
│   │   ├── client.go
│   │   ├── settings.go
│   │   ├── sort.go
│   │   └── widget.go
│   ├── pihole/
│   │   ├── client.go
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   ├── view.go
│   │   └── widget.go
│   ├── ping/
│   │   ├── settings.go
│   │   └── widget.go
│   ├── pivotal/
│   │   ├── client.go
│   │   ├── display.go
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   ├── structs.go
│   │   ├── view.go
│   │   └── widget.go
│   ├── pocket/
│   │   ├── client.go
│   │   ├── item_service.go
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── power/
│   │   ├── battery.go
│   │   ├── battery_freebsd.go
│   │   ├── battery_linux.go
│   │   ├── managed_device_test.go
│   │   ├── managed_devices.go
│   │   ├── settings.go
│   │   ├── source.go
│   │   ├── source_freebsd.go
│   │   ├── source_linux.go
│   │   └── widget.go
│   ├── progress/
│   │   ├── settings.go
│   │   └── widget.go
│   ├── resourceusage/
│   │   ├── settings.go
│   │   └── widget.go
│   ├── rollbar/
│   │   ├── client.go
│   │   ├── keyboard.go
│   │   ├── rollbar.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── security/
│   │   ├── dns.go
│   │   ├── firewall.go
│   │   ├── security_data.go
│   │   ├── settings.go
│   │   ├── users.go
│   │   ├── widget.go
│   │   └── wifi.go
│   ├── spacex/
│   │   ├── client.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── spotify/
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── spotifyweb/
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── status/
│   │   ├── settings.go
│   │   └── widget.go
│   ├── steam/
│   │   ├── client.go
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── stocks/
│   │   ├── finnhub/
│   │   │   ├── client.go
│   │   │   ├── quote.go
│   │   │   ├── settings.go
│   │   │   └── widget.go
│   │   └── yfinance/
│   │       ├── settings.go
│   │       ├── widget.go
│   │       └── yquote.go
│   ├── subreddit/
│   │   ├── api.go
│   │   ├── keyboard.go
│   │   ├── link.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── system/
│   │   ├── settings.go
│   │   ├── system_info.go
│   │   ├── system_info_windows.go
│   │   └── widget.go
│   ├── textfile/
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── todo/
│   │   ├── display.go
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── todo_plus/
│   │   ├── backend/
│   │   │   ├── backend.go
│   │   │   ├── project.go
│   │   │   ├── todoist.go
│   │   │   └── trello.go
│   │   ├── display.go
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── transmission/
│   │   ├── display.go
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── travisci/
│   │   ├── client.go
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   ├── travis.go
│   │   └── widget.go
│   ├── twitch/
│   │   ├── client.go
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── twitter/
│   │   ├── client.go
│   │   ├── keyboard.go
│   │   ├── request.go
│   │   ├── settings.go
│   │   ├── tweet.go
│   │   ├── user.go
│   │   └── widget.go
│   ├── twitterstats/
│   │   ├── client.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── unknown/
│   │   ├── settings.go
│   │   └── widget.go
│   ├── updown/
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── uptimekuma/
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── uptimerobot/
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── urlcheck/
│   │   ├── client.go
│   │   ├── client_test.go
│   │   ├── settings.go
│   │   ├── urlResult.go
│   │   ├── urlResult_test.go
│   │   ├── view.go
│   │   └── widget.go
│   ├── victorops/
│   │   ├── client.go
│   │   ├── oncallresponse.go
│   │   ├── oncallteam.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── weatherservices/
│   │   ├── arpansagovau/
│   │   │   ├── client.go
│   │   │   ├── settings.go
│   │   │   └── widget.go
│   │   ├── prettyweather/
│   │   │   ├── settings.go
│   │   │   └── widget.go
│   │   └── weather/
│   │       ├── display.go
│   │       ├── emoji.go
│   │       ├── keyboard.go
│   │       ├── settings.go
│   │       └── widget.go
│   └── zendesk/
│       ├── client.go
│       ├── keyboard.go
│       ├── settings.go
│       ├── tickets.go
│       └── widget.go
├── scripts/
│   └── check-uncommitted-vendor-files.sh
├── support/
│   └── github.go
├── utils/
│   ├── colors.go
│   ├── colors_test.go
│   ├── conversions.go
│   ├── conversions_test.go
│   ├── email_addresses.go
│   ├── email_addresses_test.go
│   ├── help_parser.go
│   ├── homedir.go
│   ├── homedir_test.go
│   ├── init.go
│   ├── init_test.go
│   ├── reflective.go
│   ├── sums.go
│   ├── sums_test.go
│   ├── text.go
│   ├── text_test.go
│   ├── utils.go
│   └── utils_test.go
├── view/
│   ├── bargraph.go
│   ├── bargraph_test.go
│   ├── base.go
│   ├── base_test.go
│   ├── billboard_modal.go
│   ├── info_table.go
│   ├── info_table_test.go
│   ├── keyboard_widget.go
│   ├── keyboard_widget_test.go
│   ├── multisource_widget.go
│   ├── scrollable_widget.go
│   ├── text_widget.go
│   └── text_widget_test.go
└── wtf/
    ├── colors.go
    ├── colors_test.go
    ├── datetime.go
    ├── datetime_test.go
    ├── enablable.go
    ├── numbers.go
    ├── numbers_test.go
    ├── schedulable.go
    ├── stoppable.go
    ├── terminal.go
    └── wtfable.go

================================================
FILE CONTENTS
================================================

================================================
FILE: .editorconfig
================================================
root = true

[*]
end_of_line = lf
insert_final_newline = true
max_line_length=120

[*.go]
indent_style = tab
indent_size = 4
charset = utf-8
trim_trailing_whitespace = true

[*.html]
indent_style = tab
indent_size = 4
charset = utf-8
trim_trailing_whitespace = false

[*.md]
trim_trailing_whitespace = false


================================================
FILE: .gitattributes
================================================
_site/* linguist-vendored
docs/* linguist-vendored
vendor/* linguist-vendored


================================================
FILE: .github/FUNDING.yml
================================================
github: FelicianoTech


================================================
FILE: .github/ISSUE_TEMPLATE/Bug.md
================================================
---
name:  🐞 Report a Bug
about: Tell us what's broken
---

## What's broken?


================================================
FILE: .github/ISSUE_TEMPLATE/Feature.md
================================================
---
name: ⚡️ Request a Feature
about: Tell us what it should do
---

## What should it do?


================================================
FILE: .github/ISSUE_TEMPLATE/Support.md
================================================
---
name: ❓Ask a Question
about: Tell us how we can help
---

## How can we help?




================================================
FILE: .github/PULL_REQUEST_TEMPLATE/Improvement.md
================================================
---
name: Improvement
about: You have some improvement to make wtf better?
---

Thanks for submitting a pull request. Please provide enough information so that others can review your pull request.




================================================
FILE: .github/PULL_REQUEST_TEMPLATE/Other.md
================================================
---
name: Other
about: You have some other ideas you want to introduce?
---

Thanks for submitting a pull request. Please provide enough information so that others can review your pull request.





================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================

Thanks for submitting a pull request. Please provide enough information so that others can review your pull request.




================================================
FILE: .github/dependabot.yml
================================================
version: 2
updates:
- package-ecosystem: gomod
  directory: "/"
  schedule:
    interval: daily
  open-pull-requests-limit: 10
  assignees:
  - FelicianoTech
- package-ecosystem: "github-actions"
  directory: "/"
  schedule:
    interval: daily
  open-pull-requests-limit: 10
  assignees:
  - FelicianoTech


================================================
FILE: .github/stale.yml
================================================
# Configuration for probot-stale - https://github.com/probot/stale

# Number of days of inactivity before an Issue or Pull Request becomes stale
daysUntilStale: 180

# Number of days of inactivity before an Issue or Pull Request with the stale label is closed.
# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale.
daysUntilClose: false

# Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled)
onlyLabels: []

# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable
exemptLabels:
  - pinned
  - security
  - "[Status] Maybe Later"

# Set to true to ignore issues in a project (defaults to false)
exemptProjects: false

# Set to true to ignore issues in a milestone (defaults to false)
exemptMilestones: false

# Set to true to ignore issues with an assignee (defaults to false)
exemptAssignees: true

# Label to use when marking as stale
staleLabel: wontfix

# Comment to post when marking as stale. Set to `false` to disable
markComment: >
  This issue has been automatically marked as stale because it has not had
  recent activity. It will be closed if no further activity occurs. Thank you
  for your contributions.

# Comment to post when removing the stale label.
# unmarkComment: >
#   Your comment here.

# Comment to post when closing a stale Issue or Pull Request.
# closeComment: >
#   Your comment here.

# Limit the number of actions per hour, from 1-30. Default is 30
limitPerRun: 30

# Limit to only `issues` or `pulls`
# only: issues

# Optionally, specify configuration settings that are specific to just 'issues' or 'pulls':
# pulls:
#   daysUntilStale: 30
#   markComment: >
#     This pull request has been automatically marked as stale because it has not had
#     recent activity. It will be closed if no further activity occurs. Thank you
#     for your contributions.

# issues:
#   exemptLabels:
#     - confirmed


================================================
FILE: .github/workflows/codeql-analysis.yml
================================================
name: "CodeQL Analysis"

on:
  push:
    branches:
      - trunk
  pull_request:

jobs:
  analyze:
    runs-on: ubuntu-24.04
    steps:
    - name: "Checkout repository"
      uses: actions/checkout@v6.0.2
    - name: "Initialize CodeQL"
      uses: github/codeql-action/init@v4
    - name: "Compile code"
      uses: github/codeql-action/autobuild@v4
    - name: "Perform CodeQL Analysis"
      uses: github/codeql-action/analyze@v4


================================================
FILE: .github/workflows/golangci-lint.yml
================================================
name: golangci-lint
on:
  push:
    tags:
      - v*
    branches:
      - trunk
  pull_request:
jobs:
  golangci:
    name: lint
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6.0.2
      - name: golangci-lint
        uses: golangci/golangci-lint-action@v9
        with:
          version: v2.11
          args: ./... --timeout=10m


================================================
FILE: .github/workflows/goreleaser.yml
================================================
name: goreleaser

on:
  push:
    tags:
      - '*'

jobs:
  goreleaser:
    runs-on: ubuntu-24.04
    steps:
      - name: Checkout
        uses: actions/checkout@v6.0.2
        with:
          fetch-depth: 0
      - name: Set up Go
        uses: actions/setup-go@v6.2.0
        with:
          go-version-file: 'go.mod'
      - name: Run GoReleaser
        uses: goreleaser/goreleaser-action@v6.4.0
        with:
          version: 2.14.0
          args: release --clean
        env:
          GITHUB_TOKEN: ${{ secrets.GH_PAT }}
          MASTODON_CLIENT_ID: ${{ secrets.MASTODON_CLIENT_ID }}
          MASTODON_CLIENT_SECRET: ${{ secrets.MASTODON_CLIENT_SECRET }}
          MASTODON_ACCESS_TOKEN: ${{ secrets.MASTODON_ACCESS_TOKEN }}
          BLUESKY_APP_PASSWORD: ${{ secrets.BLUESKY_APP_PASSWORD }}
          DISCOURSE_API_KEY: ${{ secrets.DISCOURSE_API_KEY }}


================================================
FILE: .github/workflows/pr-checks.yml
================================================
name: "PR Checks"

on:
  pull_request:
    branches:
      - trunk

jobs:
  goreleaser:
    runs-on: ubuntu-24.04
    steps:
      - name: "Checkout code"
        uses: actions/checkout@v6.0.2
        with:
          fetch-depth: 0
      - name: "Set up Go"
        uses: actions/setup-go@v6.2.0
        with:
          go-version-file: 'go.mod'
      - name: Run GoReleaser
        uses: goreleaser/goreleaser-action@v6.4.0
        with:
          version: 2.14.0
          args: release --snapshot


================================================
FILE: .github/workflows/staticcheck.yml
================================================
name: static check
on: pull_request

jobs:
  imports:
    name: Imports
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v6.0.2
    - name: check
      # uses: grandcolline/golang-github-actions@4356d0458ea4bfdb55fcb296437812acef970f9b
      uses: senorprogrammer/golang-github-actions@c2675d08254b17c070e524b3d907cfaf05fbae6f
      with:
        run: imports
        token: ${{ secrets.GITHUB_TOKEN }}

  errcheck:
    name: Errcheck
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v6.0.2
    - name: check
      # uses: grandcolline/golang-github-actions@4356d0458ea4bfdb55fcb296437812acef970f9b
      uses: senorprogrammer/golang-github-actions@c2675d08254b17c070e524b3d907cfaf05fbae6f
      with:
        run: errcheck
        token: ${{ secrets.GITHUB_TOKEN }}

  #lint:
    #name: Lint
    #runs-on: ubuntu-latest
    #steps:
    #- uses: actions/checkout@v6.0.2
    #- name: check
      #uses: grandcolline/golang-github-actions@4356d04
      #with:
        #run: lint
        #token: ${{ secrets.GITHUB_TOKEN }}

  shadow:
    name: Shadow
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v6.0.2
    - name: check
      # uses: grandcolline/golang-github-actions@4356d0458ea4bfdb55fcb296437812acef970f9b
      uses: senorprogrammer/golang-github-actions@c2675d08254b17c070e524b3d907cfaf05fbae6f
      with:
        run: shadow
        token: ${{ secrets.GITHUB_TOKEN }}

  staticcheck:
    name: StaticCheck
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v6.0.2
    - name: check
      # uses: grandcolline/golang-github-actions@4356d0458ea4bfdb55fcb296437812acef970f9b
      uses: senorprogrammer/golang-github-actions@c2675d08254b17c070e524b3d907cfaf05fbae6f
      with:
        run: staticcheck
        token: ${{ secrets.GITHUB_TOKEN }}

  #sec:
    #name: Sec
    #runs-on: ubuntu-latest
    #steps:
    #- uses: actions/checkout@v6.0.2
    #- name: check
      #uses: grandcolline/golang-github-actions@4356d04
      #with:
        #run: sec
        #token: ${{ secrets.GITHUB_TOKEN }}
        #flags: "-exclude=G104"


================================================
FILE: .gitignore
================================================
### Go ###
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
ftw*
*.so
*.dylib

# Test binary, build with `go test -c`
*.test

# Output of the go coverage tool, specifically when used with LiteIDE
*.out

# Misc
.DS_Store
gcal/client_secret.json
gspreadsheets/client_secret.json
profile.pdf
report.*
.vscode

# All things node
node_modules/
package-lock.json

#intellij idea
.idea/

dist/*
bin/


================================================
FILE: .gitmodules
================================================


================================================
FILE: .golangci.yml
================================================
version: "2"

run:
  timeout: 3m

linters:
  enable:
    - govet
    - errcheck
    - staticcheck
    - unconvert
  exclusions:
    rules:
      - linters:
        - errcheck
        source: "^\\s*defer\\s+"

formatters:
  enable:
    - gofmt


================================================
FILE: .goreleaser.yml
================================================
version: 2

builds:
  - binary: wtfutil
    goos:
      - darwin
      - linux
    goarch:
      - amd64
      - arm
      - arm64

archives:
  - id: default

homebrew_casks:
  - name: wtfutil
    homepage: 'https://wtfutil.com'
    description: 'The personal information dashboard for your terminal.'
    repository:
      owner: wtfutil
      name: homebrew-wtfutil
    hooks:
      post:
        # This hook is needed until this binary is signed and notarized
        install: |
          if system_command("/usr/bin/xattr", args: ["-h"]).exit_status == 0
            system_command "/usr/bin/xattr", args: ["-dr", "com.apple.quarantine", "#{staged_path}/wtfutil"]
          end
  - name: wtfutil
    homepage: 'https://wtfutil.com'
    description: 'The personal information dashboard for your terminal.'
    repository:
      owner: linodians
      name: homebrew-tap
    hooks:
      post:
        # This hook is needed until this binary is signed and notarized
        install: |
          if system_command("/usr/bin/xattr", args: ["-h"]).exit_status == 0
            system_command "/usr/bin/xattr", args: ["-dr", "com.apple.quarantine", "#{staged_path}/wtfutil"]
          end

announce:
  mastodon:
    enabled: true
    server: "https://social.linodians.com"
  bluesky:
    enabled: true
    username: "wtfutil.bsky.social"
  discourse:
    enabled: true
    server: "https://discuss.linodians.com"
    username: "system"
    category_id: 7


================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct

## Our Pledge

In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.

## Our Standards

Examples of behavior that contributes to creating a positive environment include:

* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members

Examples of unacceptable behavior by participants include:

* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting

## Our Responsibilities

Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.

Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.

## Scope

This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.

## Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at chriscummer+wtf@me.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.

Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.

## Attribution

This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]

[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/


================================================
FILE: CONTRIBUTING.md
================================================
# Contributing

## Pull Request Process

1. Ensure any install or build dependencies are removed before the end of the layer when doing a
   build.
2. Update the static documentation with details of changes to the interface, this includes new environment
   variables, useful file locations and configuration parameters.

Documentation lives at [wtfdocs](https://github.com/wtfutil/wtfdocs) and is a [Hugo](https://gohugo.io) app. See Hugo's documentation for usage.

## Code of Conduct

### Our Pledge

In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, gender identity and expression, level of experience,
nationality, personal appearance, race, religion, or sexual identity and
orientation.

### Our Standards

Examples of behavior that contributes to creating a positive environment
include:

* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members

Examples of unacceptable behavior by participants include:

* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
  address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
  professional setting

### Our Responsibilities

Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.

Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.

### Scope

This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.

### Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project owner at chriscummer@me.com. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.

Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.

### Attribution

This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at [http://contributor-covenant.org/version/1/4][version]

[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/


================================================
FILE: LICENSE.md
================================================
*Mozilla Public License, version 2.0* 

1. Definitions 
 
1.1. “Contributor” means each individual or legal entity that creates, contributes to the creation of, or owns Covered Software.

1.2. “Contributor Version” means the combination of the Contributions of others (if any) used by a Contributor and that particular Contributor’s Contribution.

1.3. “Contribution” means Covered Software of a particular Contributor.

1.4. “Covered Software” means Source Code Form to which the initial Contributor has attached the notice in Exhibit A, the Executable Form of such Source Code Form, and Modifications of such Source Code Form, in each case including portions thereof.

1.5. “Incompatible With Secondary Licenses” means

that the initial Contributor has attached the notice described in Exhibit B to the Covered Software; or

that the Covered Software was made available under the terms of version 1.1 or earlier of the License, but not also under the terms of a Secondary License.

1.6. “Executable Form” means any form of the work other than Source Code Form.

1.7. “Larger Work” means a work that combines Covered Software with other material, in a separate file or files, that is not Covered Software.

1.8. “License” means this document.

1.9. “Licensable” means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently, any and all of the rights conveyed by this License.

1.10. “Modifications” means any of the following:

  any file in Source Code Form that results from an addition to, deletion from, or modification of the contents of Covered Software; or

  any new file in Source Code Form that contains any Covered Software.

1.11. “Patent Claims” of a Contributor means any patent claim(s), including without limitation, method, process, and apparatus claims, in any patent Licensable by such Contributor that would be infringed, but for the grant of the License, by the making, using, selling, offering for sale, having made, import, or transfer of either its Contributions or its Contributor Version.

1.12. “Secondary License” means either the GNU General Public License, Version 2.0, the GNU Lesser General Public License, Version 2.1, the GNU Affero General Public License, Version 3.0, or any later versions of those licenses.

1.13. “Source Code Form” means the form of the work preferred for making modifications.

1.14. “You” (or “Your”) means an individual or a legal entity exercising rights under this License. For legal entities, “You” includes any entity that controls, is controlled by, or is under common control with You. For purposes of this definition, “control” means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity.

2. License Grants and Conditions 
 
2.1. Grants Each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license:

under intellectual property rights (other than patent or trademark) Licensable by such Contributor to use, reproduce, make available, modify, display, perform, distribute, and otherwise exploit its Contributions, either on an unmodified basis, with Modifications, or as part of a Larger Work; and

under Patent Claims of such Contributor to make, use, sell, offer for sale, have made, import, and otherwise transfer either its Contributions or its Contributor Version.

2.2. Effective Date The licenses granted in Section 2.1 with respect to any Contribution become effective for each Contribution on the date the Contributor first distributes such Contribution.

2.3. Limitations on Grant Scope The licenses granted in this Section 2 are the only rights granted under this License. No additional rights or licenses will be implied from the distribution or licensing of Covered Software under this License. Notwithstanding Section 2.1(b) above, no patent license is granted by a Contributor:

for any code that a Contributor has removed from Covered Software; or

for infringements caused by: (i) Your and any other third party’s modifications of Covered Software, or (ii) the combination of its Contributions with other software (except as part of its Contributor Version); or

under Patent Claims infringed by Covered Software in the absence of its Contributions.

This License does not grant any rights in the trademarks, service marks, or logos of any Contributor (except as may be necessary to comply with the notice requirements in Section 3.4).

2.4. Subsequent Licenses No Contributor makes additional grants as a result of Your choice to distribute the Covered Software under a subsequent version of this License (see Section 10.2) or under the terms of a Secondary License (if permitted under the terms of Section 3.3).

2.5. Representation Each Contributor represents that the Contributor believes its Contributions are its original creation(s) or it has sufficient rights to grant the rights to its Contributions conveyed by this License.

2.6. Fair Use This License is not intended to limit any rights You have under applicable copyright doctrines of fair use, fair dealing, or other equivalents.

2.7. Conditions Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in Section 2.1.

3. Responsibilities

3.1. Distribution of Source Form All distribution of Covered Software in Source Code Form, including any Modifications that You create or to which You contribute, must be under the terms of this License. You must inform recipients that the Source Code Form of the Covered Software is governed by the terms of this License, and how they can obtain a copy of this License. You may not attempt to alter or restrict the recipients’ rights in the Source Code Form.

3.2. Distribution of Executable Form If You distribute Covered Software in Executable Form then:

such Covered Software must also be made available in Source Code Form, as described in Section 3.1, and You must inform recipients of the Executable Form how they can obtain a copy of such Source Code Form by reasonable means in a timely manner, at a charge no more than the cost of distribution to the recipient; and

You may distribute such Executable Form under the terms of this License, or sublicense it under different terms, provided that the license for the Executable Form does not attempt to limit or alter the recipients’ rights in the Source Code Form under this License.

3.3. Distribution of a Larger Work You may create and distribute a Larger Work under terms of Your choice, provided that You also comply with the requirements of this License for the Covered Software. If the Larger Work is a combination of Covered Software with a work governed by one or more Secondary Licenses, and the Covered Software is not Incompatible With Secondary Licenses, this License permits You to additionally distribute such Covered Software under the terms of such Secondary License(s), so that the recipient of the Larger Work may, at their option, further distribute the Covered Software under the terms of either this License or such Secondary License(s).

3.4. Notices You may not remove or alter the substance of any license notices (including copyright notices, patent notices, disclaimers of warranty, or limitations of liability) contained within the Source Code Form of the Covered Software, except that You may alter any license notices to the extent required to remedy known factual inaccuracies.

3.5. Application of Additional Terms You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Software. However, You may do so only on Your own behalf, and not on behalf of any Contributor. You must make it absolutely clear that any such warranty, support, indemnity, or liability obligation is offered by You alone, and You hereby agree to indemnify every Contributor for any liability incurred by such Contributor as a result of warranty, support, indemnity or liability terms You offer. You may include additional disclaimers of warranty and limitations of liability specific to any jurisdiction.

4. Inability to Comply Due to Statute or Regulation 

If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Software due to statute, judicial order, or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be placed in a text file included with all distributions of the Covered Software under this License. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it.

5. Termination 

5.1. The rights granted under this License will terminate automatically if You fail to comply with any of its terms. However, if You become compliant, then the rights granted under this License from a particular Contributor are reinstated (a) provisionally, unless and until such Contributor explicitly and finally terminates Your grants, and (b) on an ongoing basis, if such Contributor fails to notify You of the non-compliance by some reasonable means prior to 60 days after You have come back into compliance. Moreover, Your grants from a particular Contributor are reinstated on an ongoing basis if such Contributor notifies You of the non-compliance by some reasonable means, this is the first time You have received notice of non-compliance with this License from such Contributor, and You become compliant prior to 30 days after Your receipt of the notice.

5.2. If You initiate litigation against any entity by asserting a patent infringement claim (excluding declaratory judgment actions, counter-claims, and cross-claims) alleging that a Contributor Version directly or indirectly infringes any patent, then the rights granted to You by any and all Contributors for the Covered Software under Section 2.1 of this License shall terminate.

5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user license agreements (excluding distributors and resellers) which have been validly granted by You or Your distributors under this License prior to termination shall survive termination.

6. Disclaimer of Warranty 

Covered Software is provided under this License on an “as is” basis, without warranty of any kind, either expressed, implied, or statutory, including, without limitation, warranties that the Covered Software is free of defects, merchantable, fit for a particular purpose or non-infringing. The entire risk as to the quality and performance of the Covered Software is with You. Should any Covered Software prove defective in any respect, You (not any Contributor) assume the cost of any necessary servicing, repair, or correction. This disclaimer of warranty constitutes an essential part of this License. No use of any Covered Software is authorized under this License except under this disclaimer.

7. Limitation of Liability 

Under no circumstances and under no legal theory, whether tort (including negligence), contract, or otherwise, shall any Contributor, or anyone who distributes Covered Software as permitted above, be liable to You for any direct, indirect, special, incidental, or consequential damages of any character including, without limitation, damages for lost profits, loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses, even if such party shall have been informed of the possibility of such damages. This limitation of liability shall not apply to liability for death or personal injury resulting from such party’s negligence to the extent applicable law prohibits such limitation. Some jurisdictions do not allow the exclusion or limitation of incidental or consequential damages, so this exclusion and limitation may not apply to You.

8. Litigation 

Any litigation relating to this License may be brought only in the courts of a jurisdiction where the defendant maintains its principal place of business and such litigation shall be governed by laws of that jurisdiction, without reference to its conflict-of-law provisions. Nothing in this Section shall prevent a party’s ability to bring cross-claims or counter-claims.

9. Miscellaneous 

This License represents the complete agreement concerning the subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not be used to construe this License against a Contributor.

10. Versions of the License 

10.1. New Versions Mozilla Foundation is the license steward. Except as provided in Section 10.3, no one other than the license steward has the right to modify or publish new versions of this License. Each version will be given a distinguishing version number.

10.2. Effect of New Versions You may distribute the Covered Software under the terms of the version of the License under which You originally received the Covered Software, or under the terms of any subsequent version published by the license steward.

10.3. Modified Versions If you create software not governed by this License, and you want to create a new license for such software, you may create and use a modified version of this License if you rename the license and remove any references to the name of the license steward (except to note that such modified license differs from this License).

10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses If You choose to distribute Source Code Form that is Incompatible With Secondary Licenses under the terms of this version of the License, the notice described in Exhibit B of this License must be attached.

Exhibit A - Source Code Form License Notice 

This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.

If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.

You may add additional accurate notices of copyright ownership.

Exhibit B - “Incompatible With Secondary Licenses” Notice 

This Source Code Form is “Incompatible With Secondary Licenses”, as defined by the Mozilla Public License, v. 2.0.


================================================
FILE: Makefile
================================================
.PHONY: build clean contrib_check coverage docker-build docker-install help install isntall lint run size test uninstall

# detect GOPATH if not set
ifndef $(GOPATH)
	$(info GOPATH is not set, autodetecting..)
	TESTPATH := $(dir $(abspath ../../..))
	DIRS := bin pkg src

	# create a ; separated line of tests and pass it to shell
	MISSING_DIRS := $(shell $(foreach entry,$(DIRS),test -d "$(TESTPATH)$(entry)" || echo "$(entry)";))
	ifeq ($(MISSING_DIRS),)
		$(info Found GOPATH: $(TESTPATH))
		export GOPATH := $(TESTPATH)
	else
		$(info ..missing dirs "$(MISSING_DIRS)" in "$(TESTDIR)")
		$(info GOPATH autodetection failed)
	endif
endif

# Set go modules to on and use GoCenter for immutable modules
export GO111MODULE = on
export GOPROXY = https://proxy.golang.org,direct

# Determines the path to this Makefile
THIS_FILE := $(lastword $(MAKEFILE_LIST))

GOBIN := $(GOPATH)/bin

APP=wtfutil

define HEADER
____    __    ____ .___________. _______
\   \  /  \  /   / |           ||   ____|
 \   \/    \/   /  `---|  |----`|  |__
  \            /       |  |     |   __|
   \    /\    /        |  |     |  |
    \__/  \__/         |__|     |__|

endef
export HEADER

# -------------------- Actions -------------------- #

## build: builds a local version
build:
	@echo "$$HEADER"
	@echo "Building..."
	go build -o bin/${APP}
	@echo "Done building"

## clean: removes old build cruft
clean:
	rm -rf ./dist
	rm -rf ./bin/${APP}
	@echo "Done cleaning"

## contrib-check: checks for any contributors who have not been given due credit
contrib-check:
	npx all-contributors-cli check

## coverage: figures out and displays test code coverage
coverage:
	go test -coverprofile=coverage.out ./...
	go tool cover -html=coverage.out

## docker-build: builds in docker
docker-build:
	@echo "Building ${APP} in Docker..."
	docker build -t wtfutil:build --build-arg=version=master -f Dockerfile.build .
	@echo "Done with docker build"

## docker-install: installs a local version of the app from docker build
docker-install:
	@echo "Installing..."
	docker create --name wtf_build wtfutil:build
	docker cp wtf_build:/usr/local/bin/wtfutil ~/.local/bin/
	$(eval INSTALLPATH = $(shell which ${APP}))
	@echo "${APP} installed into ${INSTALLPATH}"
	docker rm wtf_build

## gosec: runs the gosec static security scanner against the source code
gosec: $(GOBIN)/gosec
	gosec -tests ./...

$(GOBIN)/gosec:
	cd && go install github.com/securego/gosec/v2/cmd/gosec@latest

## help: prints this help message
help:
	@echo "Usage: \n"
	@sed -n 's/^##//p' ${MAKEFILE_LIST} | column -t -s ':' |  sed -e 's/^/ /'

## isntall: an alias for 'install'
isntall:
	@$(MAKE) -f $(THIS_FILE) install

## install: installs a local version of the app
install:
	$(eval GOVERS = $(shell go version))
	@echo "$$HEADER"
	@echo "Installing ${APP} with ${GOVERS}..."
	@go clean
	@go install -ldflags="-s -w"
	$(eval INSTALLPATH = $(shell which ${APP}))
	@echo "${APP} installed into ${INSTALLPATH}"

## lint: runs a number of code quality checks against the source code
lint: $(GOBIN)/golangci-lint
	golangci-lint cache clean
	golangci-lint run

$(GOBIN)/golangci-lint:
	cd && go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest

# lint:
# 	@echo "\033[35mhttps://github.com/kisielk/errcheck\033[0m"
# 	errcheck ./app
# 	errcheck ./cfg
# 	errcheck ./flags
# 	errcheck ./help
# 	errcheck ./logger
# 	errcheck ./modules/...
# 	errcheck ./utils
# 	errcheck ./view
# 	errcheck ./wtf
# 	errcheck ./main.go

# 	@echo "\033[35mhttps://golang.org/cmd/vet/k\033[0m"
# 	go vet ./app
# 	go vet ./cfg
# 	go vet ./flags
# 	go vet ./help
# 	go vet ./logger
# 	go vet ./modules/...
# 	go vet ./utils
# 	go vet ./view
# 	go vet ./wtf
# 	go vet ./main.go

# 	@echo "\033[35m# https://staticcheck.io/docs/k\033[0m"
# 	staticcheck ./app
# 	staticcheck ./cfg
# 	staticcheck ./flags
# 	staticcheck ./help
# 	staticcheck ./logger
# 	staticcheck ./modules/...
# 	staticcheck ./utils
# 	staticcheck ./view
# 	staticcheck ./wtf
# 	staticcheck ./main.go

# 	@echo "\033[35m# https://github.com/mdempsky/unconvert\033[0m"
# 	unconvert ./...

## loc: displays the lines of code (LoC) count
loc:
	@loc --exclude _sample_configs/ _site/ docs/ Makefile *.md

## run: executes the locally-installed version
run: build
	@echo "$$HEADER"
	bin/${APP}

## test: runs the test suite
test: build
	@echo "$$HEADER"
	go test ./...

## uninstall: uninstals a locally-installed version
uninstall:
	@rm $(GOBIN)/${APP}


================================================
FILE: README.md
================================================
<p align="center">
    <img src="./images/logo_transparent.png?raw=true" title="WTF" alt="WTF" width="560" height="560" />
</p>

[![GitHub Release](https://img.shields.io/github/v/release/wtfutil/wtf?logo=github&style=for-the-badge)](https://github.com/wtfutil/wtf/releases)
[![Go Report Card](https://goreportcard.com/badge/github.com/wtfutil/wtf?style=for-the-badge)](https://goreportcard.com/report/github.com/wtfutil/wtf)
[![Discourse Status](https://img.shields.io/discourse/status?server=https%3A%2F%2Fdiscuss.linodians.com&logo=discourse&style=for-the-badge)](https://discuss.linodians.com/c/projects/wtf/7)
[![Bluesky followers](https://img.shields.io/bluesky/followers/wtfutil.bsky.social?logo=bluesky&style=for-the-badge)](https://bsky.app/profile/wtfutil.bsky.social)
[![Mastodon followers](https://img.shields.io/mastodon/follow/115007297910718188?domain=social.linodians.com&logo=mastodon&style=for-the-badge)](https://social.linodians.com/@WTFutil)
![Static Badge](https://img.shields.io/badge/LICENSE-MPL--2.0-orange?style=for-the-badge)

---

WTF (aka 'wtfutil') is the personal information dashboard for your terminal, providing at-a-glance access to your very important but infrequently-needed stats and data.

Used by thousands of developers and tech people around the world, WTF is free and open-source. To support the continued use and development of WTF, please consider sponsoring WTF via [GitHub Sponsors](https://github.com/sponsors/FelicianoTech).

### Are you a contributor or sponsor?

Awesome! [See here](https://wtfutil.com/sponsors/exit_message/) for how you can change the exit message, the message WTF shows when quitting, to something special just for you.

---

* [Installation](#installation)
    * [Installing via Homebrew](#installing-via-homebrew)
    * [Installing via `go install`](#installing-via-go-install)
    * [Installing via MacPorts](#installing-via-macports)
    * [Installing a Binary](#installing-a-binary)
    * [Installing from Source](#installing-from-source)
    * [Running via Docker](#running-via-docker)
* [Communication](#communication)
    * [GitHub Discussions](#github-discussions)
    * [Twitter](#twitter)
* [Documentation](#documentation)
* [Modules](#modules)
* [Getting Bugs Fixed or Features Added](#getting-bugs-fixed-or-features-added)
* [Contributing to the Source Code](#contributing-to-the-source-code)
    * [Adding Dependencies](#adding-dependencies)
* [Contributing to the Documentation](#contributing-to-the-documentation)
* [Contributors](#contributors)
* [Acknowledgements](#acknowledgments)

<p align="center">
<img src="./images/screenshot.jpg" title="screenshot" width="720" height="420" />
</p>

## Installation

### Installing via Homebrew

The simplest way from Homebrew:

```console
brew install wtfutil

wtfutil
```

That version can sometimes lag a bit, as recipe updates take time to get accepted into `homebrew-core`. If you always want the bleeding edge of releases, you can tap it:

```console
brew install linodians/tap/wtfutil

wtfutil
```

### Installing via `go install`

Just run

```sh
go install github.com/wtfutil/wtf@latest
```

### Installing via MacPorts

You can also install via [MacPorts](https://www.macports.org/):

```console
sudo port selfupdate
sudo port install wtfutil

wtfutil
```

### Installing a Binary

[Download the latest binary](https://github.com/wtfutil/wtf/releases) from GitHub.

WTF is a stand-alone binary. Once downloaded, copy it to a location you can run executables from (ie: `/usr/local/bin/`), and set the permissions accordingly:

```bash
chmod a+x /usr/local/bin/wtfutil
```

and you should be good to go.

### Installing from Source

If you want to run the build command from within your `$GOPATH`:

```bash
# Set the Go proxy
export GOPROXY="https://proxy.golang.org,direct"

# Disable the Go checksum database
export GOSUMDB=off

# Enable Go modules
export GO111MODULE=on

go get -u github.com/wtfutil/wtf
cd $GOPATH/src/github.com/wtfutil/wtf
make install
make run
```

If you want to run the build command from a folder that is not in your `$GOPATH`:

```bash
# Set the Go proxy
export GOPROXY="https://proxy.golang.org,direct"

go get -u github.com/wtfutil/wtf
cd $GOPATH/src/github.com/wtfutil/wtf
make install
make run
```

### Installing via Arch User Repository

Arch Linux users can utilise the [wtfutil](https://aur.archlinux.org/packages/wtfutil) package to build it from source, or [wtfutil-bin](https://aur.archlinux.org/packages/wtfutil-bin/) to install pre-built binaries.


## Documentation

See [https://wtfutil.com](https://wtfutil.com) for the definitive
documentation. Here's some short-cuts:

* [Installation](https://wtfutil.com/quick_start/)
* [Configuration](https://wtfutil.com/configuration/files/)
* [Module Documentation](https://wtfutil.com/modules/)


## Modules

Modules are the chunks of functionality that make WTF useful. Modules are added and configured by including their configuration values in your `config.yml` file. The documentation for each module describes how to configure them.

Some interesting modules you might consider adding to get you started:

* [DigitalOcean](https://wtfutil.com/modules/digitalocean/)
* [GitHub](https://wtfutil.com/modules/github/)
* [Google Calendar](https://wtfutil.com/modules/google/gcal/)
* [HackerNews](https://wtfutil.com/modules/hackernews/)
* [Have I Been Pwned](https://wtfutil.com/modules/hibp/)
* [NewRelic](https://wtfutil.com/modules/newrelic/)
* [OpsGenie](https://wtfutil.com/modules/opsgenie/)
* [Security](https://wtfutil.com/modules/security/)
* [Transmission](https://wtfutil.com/modules/transmission/)
* [Trello](https://wtfutil.com/modules/trello/)

## Getting Bugs Fixed or Features Added

WTF is open-source software, informally maintained by a small collection of volunteers who come and go at their leisure. There are absolutely no guarantees that, even if an issue is opened for them, bugs will be fixed or features added.

If there is a bug that you really need to have fixed or a feature you really want to have implemented, you can greatly increase your chances of that happening by creating a bounty on [BountySource](https://www.bountysource.com) to provide an incentive for someone to tackle it.

## Contributing to the Source Code

First, kindly read [Talk, then code](https://dave.cheney.net/2019/02/18/talk-then-code) by Dave Cheney. It's great advice and will often save a lot of time and effort.

Next, kindly read [CONTRIBUTING.md](CONTRIBUTING.md) for details on our code of conduct, and the process for submitting pull requests.

Then create your branch, write your code, submit your PR, and join the rest of the awesome people who've contributed their time and effort towards WTF. Without their contributors, WTF wouldn't be possible.

Don't worry if you've never written Go before, or never contributed to an open source project before, or that your code won't be good enough. For a surprising number of people WTF has been their first Go project, or first open source contribution. If you're here, and you've read this far, you're the right stuff.

## Contributing to the Documentation

Documentation now lives in its own repository here: [https://github.com/wtfutil/wtfdocs](https://github.com/wtfutil/wtfdocs).

Please make all additions and updates to documentation in that repository.

### Adding Dependencies

Dependency management in WTF is handled by [Go modules](https://github.com/golang/go/wiki/Modules). Please check out that page for more details on how Go modules work.


## Acknowledgments

The inspiration for `WTF` came from Monica Dinculescu's
[tiny-care-terminal](https://github.com/notwaldorf/tiny-care-terminal).

WTF is built atop [tcell](https://github.com/gdamore/tcell) and [tview](https://github.com/rivo/tview), fantastic projects both. WTF is built, packaged, and deployed via [GoReleaser](https://goreleaser.com).

<p align="center">
<img src="./images/dude_wtf.png?raw=true" title="Dude WTF" width="251" height="201" />
</p>


================================================
FILE: SECURITY.md
================================================
# Security Policy

To file a security issue, open a new Issue in the Issues tab.


================================================
FILE: _sample_configs/bargraph_config.yml
================================================
wtf:
  colors:
    border:
      focusable: darkslateblue
      focused: orange
      normal: gray
  grid:
    columns: [40, 40]
    rows: [13, 13, 4]
  refreshInterval: 1
  mods:
    bargraph:
      enabled: true
      graphIcon: "💀"
      graphStars: 25
      position:
        top: 1
        left: 0
        height: 2
        width: 2
      refreshInterval: 30

================================================
FILE: _sample_configs/dynamic_sizing.yml
================================================
wtf:
  mods:
    battery:
      type: power
      title: "⚡️"
      enabled: true
      position:
        top: 0
        left: 0
        height: 1
        width: 1
      refreshInterval: 15
    security_info:
      type: security
      enabled: true
      position:
        top: 0
        left: 1
        height: 1
        width: 1
      refreshInterval: 3600

================================================
FILE: _sample_configs/kubernetes_config.yml
================================================
wtf:
  colors:
    border:
      focusable: darkslateblue
      focused: orange
      normal: gray
  grid:
    columns: [32, 32, 32, 32, 32, 32]
    rows: [10, 10, 10, 10, 10, 10]
  refreshInterval: 2
  mods:
    kubernetes:
      enabled: true
      kubeconfig: /Users/testuser/.kube/config
      namespaces: ["demo", "kube-system"]
      objects: ["nodes","deployments", "pods"]
      position:
        top: 0
        left: 0
        height: 6
        width: 3


================================================
FILE: _sample_configs/sample_config.yml
================================================
wtf:
  colors:
    background: black
    border:
      focusable: darkslateblue
      focused: orange
      normal: gray
    checked: yellow
    highlight:
      fore: black
      back: gray
    rows:
      even: yellow
      odd: white
  grid:
    # How _wide_ the columns are, in terminal characters. In this case we have
    # four columns, each of which are 35 characters wide.
    columns: [35, 35, 35, 35]
    # How _high_ the rows are, in terminal lines. In this case we have four rows
    # that support ten line of text and one of four.
    rows: [10, 10, 10, 10, 4]
  refreshInterval: 1
  openFileUtil: "open"
  mods:
    # You can have multiple widgets of the same type.
    # The "key" is the name of the widget and the type is the actual
    # widget you want to implement.
    europe_time:
      title: "Europe"
      type: clocks
      colors:
        rows:
          even: "lightblue"
          odd: "white"
      enabled: true
      locations:
        GMT: "Etc/GMT"
        Amsterdam: "Europe/Amsterdam"
        Berlin: "Europe/Berlin"
        Barcelona: "Europe/Madrid"
        Copenhagen: "Europe/Copenhagen"
        London: "Europe/London"
        Rome: "Europe/Rome"
        Stockholm: "Europe/Stockholm"
      position:
        top: 0
        left: 0
        height: 1
        width: 1
      refreshInterval: 15
      sort: "alphabetical"
    americas_time:
      title: "Americas"
      type: clocks
      colors:
        rows:
          even: "lightblue"
          odd: "white"
      enabled: true
      locations:
        UTC: "Etc/UTC"
        Vancouver: "America/Vancouver"
        New_York: "America/New_York"
        Sao_Paulo: "America/Sao_Paulo"
        Denver: "America/Denver"
        Iqaluit: "America/Iqaluit"
        Bahamas: "America/Nassau"
        Chicago: "America/Chicago"
      position:
        top: 0
        left: 1
        height: 1
        width: 1
      refreshInterval: 15
      sort: "alphabetical"
    battery:
      type: power
      title: "⚡️"
      enabled: true
      position:
        top: 1
        left: 3
        height: 1
        width: 1
      refreshInterval: 15
    todolist:
      type: todo
      checkedIcon: "X"
      colors:
        checked: gray
        highlight:
          fore: "black"
          back: "orange"
      enabled: true
      filename: "todo.yml"
      position:
        top: 1
        left: 0
        height: 2
        width: 1
      refreshInterval: 3600
    ip:
      type: ipinfo
      title: "My IP"
      colors:
        name: "lightblue"
        value: "white"
      enabled: true
      position:
        top: 0
        left: 2
        height: 1
        width: 2
      refreshInterval: 150
    security_info:
      type: security
      title: "Staying safe"
      enabled: true
      position:
        top: 1
        left: 2
        height: 1
        width: 1
      refreshInterval: 3600
    readme:
      type: textfile
      enabled: true
      filePaths:
        - "~/.config/wtf/config.yml"
      format: true
      formatStyle: "monokai"
      position:
        top: 1
        left: 1
        height: 1
        width: 1
      refreshInterval: 15
    news:
      type: hackernews
      title: "HackerNews"
      enabled: true
      numberOfStories: 10
      position:
        top: 2
        left: 1
        height: 1
        width: 3
      storyType: top
      refreshInterval: 900
    resources:
      type: resourceusage
      enabled: true
      position:
        top: 3
        left: 0
        height: 2
        width: 1
      refreshInterval: 1
    uptime:
      type: cmdrunner
      args: []
      cmd: "uptime"
      enabled: true
      position:
        top: 4
        left: 1
        height: 1
        width: 3
      refreshInterval: 30
    disks:
      type: cmdrunner
      cmd: "df"
      args: ["-h"]
      enabled: true
      position:
        top: 3
        left: 1
        height: 1
        width: 3
      refreshInterval: 3600


================================================
FILE: _sample_configs/small_config.yml
================================================
wtf:
  grid:
    columns: [20, 20]
    rows: [3, 3]
  refreshInterval: 1
  mods:
    uptime:
      type: cmdrunner
      args: []
      cmd: "uptime"
      enabled: true
      position:
        top: 0
        left: 0
        height: 1
        width: 1
      refreshInterval: 30


================================================
FILE: _sample_configs/uniconfig.yml
================================================
wtf:
  colors:
    background: black
    border:
      focusable: darkslateblue
  grid:
    columns: [40, 40]
    rows: [16]
  refreshInterval: 1
  mods:
    americas_time:
      title: "Americas"
      type: clocks
      enabled: true
      locations:
        UTC: "Etc/UTC"
        Vancouver: "America/Vancouver"
        New_York: "America/New_York"
        Sao_Paolo: "America/Sao_Paulo"
        Denver: "America/Denver"
        Iqaluit: "America/Iqaluit"
        Bahamas: "America/Nassau"
        Chicago: "America/Chicago"
      position:
        top: 0
        left: 0
        height: 1
        width: 1
      refreshInterval: 15
      sort: "chronological"
    textfile:
      enabled: true
      filePaths:
        - "~/.config/wtf/config.yml"
      format: true
      formatStyle: "vim"
      position:
        top: 0
        left: 1
        height: 1
        width: 1
      refreshInterval: 15


================================================
FILE: app/app_manager.go
================================================
package app

import (
	"errors"

	"github.com/olebedev/config"
	"github.com/rivo/tview"
)

// WtfAppManager handles the instances of WtfApp, ensuring that they're displayed as requested
type WtfAppManager struct {
	WtfApps []*WtfApp

	selected int
}

// NewAppManager creates and returns an instance of AppManager
func NewAppManager() WtfAppManager {
	appMan := WtfAppManager{
		WtfApps: []*WtfApp{},
	}

	return appMan
}

// MakeNewWtfApp creates and starts a new instance of WtfApp from a set of configuration params
func (appMan *WtfAppManager) MakeNewWtfApp(config *config.Config, configFilePath string) {
	wtfApp := NewWtfApp(tview.NewApplication(), config, configFilePath)
	appMan.Add(wtfApp)

	wtfApp.Start()
}

// Add adds a WtfApp to the collection of apps that the AppManager manages.
// This app is then available for display onscreen.
func (appMan *WtfAppManager) Add(wtfApp *WtfApp) {
	appMan.WtfApps = append(appMan.WtfApps, wtfApp)
}

// Current returns the currently-displaying instance of WtfApp
func (appMan *WtfAppManager) Current() (*WtfApp, error) {
	if appMan.selected < 0 || appMan.selected >= len(appMan.WtfApps) {
		return nil, errors.New("invalid app index selected")
	}

	return appMan.WtfApps[appMan.selected], nil
}

// Next cycles the WtfApps forward by one, making the next one in the list
// the current one. If there are none after the current one, it wraps around.
func (appMan *WtfAppManager) Next() (*WtfApp, error) {
	appMan.selected++

	if appMan.selected >= len(appMan.WtfApps) {
		appMan.selected = 0
	}

	return appMan.Current()
}

// Prev cycles the WtfApps backwards by one, making the previous one in the
// list the current one. If there are none before the current one, it wraps around.
func (appMan *WtfAppManager) Prev() (*WtfApp, error) {
	appMan.selected--

	if appMan.selected < 0 {
		appMan.selected = len(appMan.WtfApps) - 1
	}

	return appMan.Current()
}


================================================
FILE: app/display.go
================================================
package app

import (
	"github.com/olebedev/config"
	"github.com/rivo/tview"
	"github.com/wtfutil/wtf/utils"
	"github.com/wtfutil/wtf/wtf"
)

// Display is the container for the onscreen representation of a WtfApp
type Display struct {
	Grid   *tview.Grid
	config *config.Config
}

// NewDisplay creates and returns a Display
func NewDisplay(widgets []wtf.Wtfable, config *config.Config) *Display {
	display := Display{
		Grid:   tview.NewGrid(),
		config: config,
	}

	firstWidget := widgets[0]
	display.Grid.SetBackgroundColor(
		wtf.ColorFor(
			firstWidget.CommonSettings().Colors.Background,
		),
	)

	display.build(widgets)

	return &display
}

/* -------------------- Unexported Functions -------------------- */

func (display *Display) add(widget wtf.Wtfable) {
	if widget.Disabled() {
		return
	}

	display.Grid.AddItem(
		widget.TextView(),
		widget.CommonSettings().Top,
		widget.CommonSettings().Left,
		widget.CommonSettings().Height,
		widget.CommonSettings().Width,
		0,
		0,
		false,
	)
}

func (display *Display) build(widgets []wtf.Wtfable) *tview.Grid {
	cols := utils.ToInts(display.config.UList("wtf.grid.columns"))
	rows := utils.ToInts(display.config.UList("wtf.grid.rows"))

	display.Grid.SetColumns(cols...)
	display.Grid.SetRows(rows...)
	display.Grid.SetBorder(false)

	for _, widget := range widgets {
		display.add(widget)
	}

	return display.Grid
}


================================================
FILE: app/exit_message.go
================================================
package app

import (
	"fmt"
	"os"
	"strings"

	"github.com/logrusorgru/aurora/v4"
	"github.com/olebedev/config"
)

const exitMessageHeader = `
	      ____    __    ____ .___________. _______
	      \   \  /  \  /   / |           ||   ____|
	       \   \/    \/   /  ----|  |-----|  |__
	        \            /       |  |     |   __|
	         \    /\    /        |  |     |  |
	          \__/  \__/         |__|     |__|

    the personal information dashboard for your terminal
`

// DisplayExitMessage displays the onscreen exit message when the app quits
func (wtfApp *WtfApp) DisplayExitMessage() {
	exitMessageIsDisplayable := readDisplayableConfig(wtfApp.config)

	wtfApp.displayExitMsg(exitMessageIsDisplayable)
}

/* -------------------- Unexported Functions -------------------- */

func (wtfApp *WtfApp) displayExitMsg(exitMessageIsDisplayable bool) string {
	// If a sponsor or contributor and opt out of seeing the exit message, do not display it
	if (wtfApp.ghUser.IsContributor || wtfApp.ghUser.IsSponsor) && !exitMessageIsDisplayable {
		return ""
	}

	msgs := []string{}

	msgs = append(msgs, aurora.Magenta(exitMessageHeader).String())

	if wtfApp.ghUser.IsContributor {
		msgs = append(msgs, wtfApp.contributorThankYouMessage())
	}

	if wtfApp.ghUser.IsSponsor {
		msgs = append(msgs, wtfApp.sponsorThankYouMessage())
	}

	if !wtfApp.ghUser.IsContributor && !wtfApp.ghUser.IsSponsor {
		msgs = append(msgs, wtfApp.supportRequestMessage())
	}

	displayMsg := strings.Join(msgs, "\n")

	fmt.Println(displayMsg)

	return displayMsg
}

// readDisplayableConfig figures out whether or not the exit message should be displayed
// per the user's wishes. It allows contributors and sponsors to opt out of the exit message
func readDisplayableConfig(cfg *config.Config) bool {
	displayExitMsg := cfg.UBool("wtf.exitMessage.display", true)
	return displayExitMsg
}

// readGitHubAPIKey attempts to find a GitHub API key somewhere in the configuration file
func readGitHubAPIKey(cfg *config.Config) string {
	apiKey := cfg.UString("wtf.exitMessage.githubAPIKey", os.Getenv("WTF_GITHUB_TOKEN"))
	if apiKey != "" {
		return apiKey
	}

	moduleConfig, err := cfg.Get("wtf.mods.github")
	if err != nil {
		return ""
	}

	return moduleConfig.UString("apiKey", "")
}

/* -------------------- Messaging -------------------- */

func (wtfApp *WtfApp) contributorThankYouMessage() string {
	str := "    On behalf of all the users of WTF, thank you for contributing to the source code."
	str += fmt.Sprintf(" %s", aurora.Green("\n\n    You rock."))

	return str
}

func (wtfApp *WtfApp) sponsorThankYouMessage() string {
	str := "    Your sponsorship of WTF makes a difference. Thank you for sponsoring and supporting WTF."
	str += fmt.Sprintf(" %s", aurora.Green("\n\n    You're awesome."))

	return str
}

func (wtfApp *WtfApp) supportRequestMessage() string {
	str := "    The development and maintenance of WTF is supported by sponsorships.\n"
	str += fmt.Sprintf("    Sponsor the development of WTF at %s\n", aurora.Green("https://github.com/sponsors/FelicianoTech"))

	return str
}


================================================
FILE: app/exit_message_test.go
================================================
package app

import (
	"strings"
	"testing"

	"github.com/wtfutil/wtf/support"
	"gotest.tools/assert"
)

func Test_displayExitMessage(t *testing.T) {
	tests := []struct {
		name          string
		isDisplayable bool
		isContributor bool
		isSponsor     bool
		compareWith   string
		expected      string
	}{
		{
			name:          "when not displayable",
			isDisplayable: false,
			isContributor: true,
			isSponsor:     true,
			compareWith:   "equals",
			expected:      "",
		},
		{
			name:          "when contributor",
			isDisplayable: true,
			isContributor: true,
			compareWith:   "contains",
			expected:      "thank you for contributing",
		},
		{
			name:          "when sponsor",
			isDisplayable: true,
			isSponsor:     true,
			compareWith:   "contains",
			expected:      "Thank you for sponsoring",
		},
		{
			name:          "when user",
			isDisplayable: true,
			isContributor: false,
			isSponsor:     false,
			compareWith:   "contains",
			expected:      "supported by sponsorships",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			wtfApp := WtfApp{}
			wtfApp.ghUser = &support.GitHubUser{
				IsContributor: tt.isContributor,
				IsSponsor:     tt.isSponsor,
			}

			actual := wtfApp.displayExitMsg(tt.isDisplayable)

			if tt.compareWith == "equals" {
				assert.Equal(t, actual, tt.expected)
			}

			if tt.compareWith == "contains" {
				assert.Equal(t, true, strings.Contains(actual, tt.expected))
			}
		})
	}
}


================================================
FILE: app/focus_tracker.go
================================================
package app

import (
	"fmt"
	"sort"

	"github.com/olebedev/config"
	"github.com/rivo/tview"
	"github.com/wtfutil/wtf/wtf"
)

// FocusState is a custom type that differentiates focusable scopes
type FocusState int

const (
	widgetFocused FocusState = iota
	appBoardFocused
	neverFocused
)

// FocusTracker is used by the app to track which onscreen widget currently has focus,
// and to move focus between widgets.
type FocusTracker struct {
	Idx       int
	IsFocused bool
	Widgets   []wtf.Wtfable

	config   *config.Config
	tviewApp *tview.Application
}

// NewFocusTracker creates and returns an instance of FocusTracker
func NewFocusTracker(tviewApp *tview.Application, widgets []wtf.Wtfable, config *config.Config) FocusTracker {
	focusTracker := FocusTracker{
		tviewApp:  tviewApp,
		Idx:       -1,
		IsFocused: false,
		Widgets:   widgets,

		config: config,
	}

	focusTracker.assignHotKeys()

	return focusTracker
}

/* -------------------- Exported Functions -------------------- */

// FocusOn puts the focus on the item that belongs to the focus character passed in
func (tracker *FocusTracker) FocusOn(char string) bool {
	if !tracker.useNavShortcuts() {
		return false
	}

	if tracker.focusState() == appBoardFocused {
		return false
	}

	hasFocusable := false

	for idx, focusable := range tracker.focusables() {
		if focusable.FocusChar() == char {
			tracker.blur(tracker.Idx)
			tracker.Idx = idx
			tracker.focus(tracker.Idx)

			hasFocusable = true
			tracker.IsFocused = true
			break
		}
	}

	return hasFocusable
}

// Next sets the focus on the next widget in the widget list. If the current widget is
// the last widget, sets focus on the first widget.
func (tracker *FocusTracker) Next() {
	if tracker.focusState() == appBoardFocused {
		return
	}

	tracker.blur(tracker.Idx)
	tracker.increment()
	tracker.focus(tracker.Idx)

	tracker.IsFocused = true
}

// None removes focus from the currently-focused widget.
func (tracker *FocusTracker) None() {
	if tracker.focusState() == appBoardFocused {
		return
	}

	tracker.blur(tracker.Idx)
}

// Prev sets the focus on the previous widget in the widget list. If the current widget is
// the last widget, sets focus on the last widget.
func (tracker *FocusTracker) Prev() {
	if tracker.focusState() == appBoardFocused {
		return
	}

	tracker.blur(tracker.Idx)
	tracker.decrement()
	tracker.focus(tracker.Idx)

	tracker.IsFocused = true
}

// Refocus forces the focus back to the currently-selected item
func (tracker *FocusTracker) Refocus() {
	tracker.focus(tracker.Idx)
}

/* -------------------- Unexported Functions -------------------- */

// AssignHotKeys assigns an alphabetic keyboard character to each focusable
// widget so that the widget can be brought into focus by pressing that keyboard key
// Valid numbers are between 1 and 9, inclusive
func (tracker *FocusTracker) assignHotKeys() {
	if !tracker.useNavShortcuts() {
		return
	}

	usedKeys := make(map[string]bool)
	focusables := tracker.focusables()

	// First, block out the explicitly-defined characters so they can't be automatically
	// assigned to other modules
	for _, focusable := range focusables {
		if focusable.FocusChar() != "" {
			usedKeys[focusable.FocusChar()] = true
		}
	}

	focusNum := 1

	// Range over all the modules and assign focus characters to any that are focusable
	// and don't have explicitly-defined focus characters
	for _, focusable := range focusables {
		if focusable.FocusChar() != "" {
			continue
		}

		if _, foundKey := usedKeys[fmt.Sprint(focusNum)]; foundKey {
			for ; foundKey; _, foundKey = usedKeys[fmt.Sprint(focusNum)] {
				focusNum++
			}
		}

		// Don't allow focus characters > "9"
		if focusNum >= 10 {
			break
		}

		focusable.SetFocusChar(fmt.Sprint(focusNum))
		focusNum++
	}
}

func (tracker *FocusTracker) blur(idx int) {
	widget := tracker.focusableAt(idx)
	if widget == nil {
		return
	}

	view := widget.TextView()
	view.Blur()

	view.SetBorderColor(
		wtf.ColorFor(
			widget.BorderColor(),
		),
	)

	tracker.IsFocused = false
}

func (tracker *FocusTracker) decrement() {
	tracker.Idx--

	if tracker.Idx < 0 {
		tracker.Idx = len(tracker.focusables()) - 1
	}
}

func (tracker *FocusTracker) focus(idx int) {
	widget := tracker.focusableAt(idx)
	if widget == nil {
		return
	}

	view := widget.TextView()
	view.SetBorderColor(
		wtf.ColorFor(
			widget.CommonSettings().Colors.Focused,
		),
	)
	tracker.tviewApp.SetFocus(view)
}

func (tracker *FocusTracker) focusables() []wtf.Wtfable {
	focusable := []wtf.Wtfable{}

	for _, widget := range tracker.Widgets {
		if widget.Focusable() {
			focusable = append(focusable, widget)
		}
	}

	// Sort for deterministic ordering
	sort.SliceStable(focusable, func(i, j int) bool {
		iTop := focusable[i].CommonSettings().Top
		jTop := focusable[j].CommonSettings().Top

		if iTop < jTop {
			return true
		}
		if iTop == jTop {
			return focusable[i].CommonSettings().Left < focusable[j].CommonSettings().Left
		}
		return false
	})

	return focusable
}

func (tracker *FocusTracker) focusableAt(idx int) wtf.Wtfable {
	if idx < 0 || idx >= len(tracker.focusables()) {
		return nil
	}

	return tracker.focusables()[idx]
}

func (tracker *FocusTracker) focusState() FocusState {
	if tracker.Idx < 0 {
		return neverFocused
	}

	for _, widget := range tracker.Widgets {
		if widget.TextView() == tracker.tviewApp.GetFocus() {
			return widgetFocused
		}
	}

	return appBoardFocused
}

func (tracker *FocusTracker) increment() {
	tracker.Idx++

	if tracker.Idx == len(tracker.focusables()) {
		tracker.Idx = 0
	}
}

func (tracker *FocusTracker) useNavShortcuts() bool {
	return tracker.config.UBool("wtf.navigation.shortcuts", true)
}


================================================
FILE: app/module_validator.go
================================================
package app

import (
	"fmt"
	"os"

	"github.com/logrusorgru/aurora/v4"
	"github.com/wtfutil/wtf/cfg"
	"github.com/wtfutil/wtf/wtf"
)

// ModuleValidator is responsible for validating the state of a module's configuration
type ModuleValidator struct{}

type widgetError struct {
	name             string
	validationErrors []cfg.Validatable
}

// NewModuleValidator creates and returns an instance of ModuleValidator
func NewModuleValidator() *ModuleValidator {
	return &ModuleValidator{}
}

// Validate rolls through all the enabled widgets and looks for configuration errors.
// If it finds any it stringifies them, writes them to the console, and kills the app gracefully
func (val *ModuleValidator) Validate(widgets []wtf.Wtfable) {
	validationErrors := validate(widgets)

	if len(validationErrors) > 0 {
		fmt.Println()
		for _, error := range validationErrors {
			for _, message := range error.errorMessages() {
				fmt.Println(message)
			}
		}
		fmt.Println()

		os.Exit(1)
	}
}

func validate(widgets []wtf.Wtfable) (widgetErrors []widgetError) {
	for _, widget := range widgets {
		err := widgetError{name: widget.Name()}

		for _, val := range widget.CommonSettings().Validations() {
			if val.HasError() {
				err.validationErrors = append(err.validationErrors, val)
			}
		}

		if len(err.validationErrors) > 0 {
			widgetErrors = append(widgetErrors, err)
		}
	}

	return widgetErrors
}

func (err widgetError) errorMessages() (messages []string) {
	widgetMessage := fmt.Sprintf(
		"%s in %s configuration",
		aurora.Red("Errors"),
		aurora.Yellow(
			fmt.Sprintf(
				"%s.position",
				err.name,
			),
		),
	)
	messages = append(messages, widgetMessage)

	for _, e := range err.validationErrors {
		configMessage := fmt.Sprintf(" - %s\t%s %v", e.String(), aurora.Red("Error:"), e.Error())

		messages = append(messages, configMessage)
	}

	return messages
}


================================================
FILE: app/module_validator_test.go
================================================
package app

import (
	"fmt"
	"testing"

	"github.com/logrusorgru/aurora/v4"
	"github.com/olebedev/config"
	"github.com/stretchr/testify/assert"
	"github.com/wtfutil/wtf/wtf"
)

const (
	valid = `
wtf:
  mods:
    clocks:
      enabled: true
      position:
        top: 0
        left: 0
        height: 1
        width: 1
      refreshInterval: 30`

	invalid = `
wtf:
  mods:
    clocks:
      enabled: true
      position:
        top: abc
        left: 0
        height: 1
        width: 1
      refreshInterval: 30`
)

func Test_NewModuleValidator(t *testing.T) {
	assert.IsType(t, &ModuleValidator{}, NewModuleValidator())
}

func Test_validate(t *testing.T) {
	tests := []struct {
		name       string
		moduleName string
		config     *config.Config
		expected   []string
	}{
		{
			name:       "valid config",
			moduleName: "clocks",
			config: func() *config.Config {
				cfg, _ := config.ParseYaml(valid)
				return cfg
			}(),
			expected: []string{},
		},
		{
			name:       "invalid config",
			moduleName: "clocks",
			config: func() *config.Config {
				cfg, _ := config.ParseYaml(invalid)
				return cfg
			}(),
			expected: []string{
				fmt.Sprintf("%s in %s configuration", aurora.Red("Errors"), aurora.Yellow("clocks.position")),
				fmt.Sprintf(
					" - Invalid value for %s:	0	%s strconv.ParseInt: parsing \"abc\": invalid syntax",
					aurora.Yellow("top"),
					aurora.Red("Error:"),
				),
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			widget := MakeWidget(nil, nil, tt.moduleName, tt.config, make(chan bool))

			if widget == nil {
				t.Logf("Failed to create widget %s", tt.moduleName)
				t.FailNow()
			}

			errs := validate([]wtf.Wtfable{widget})

			if len(tt.expected) == 0 {
				assert.Empty(t, errs)
			} else {
				assert.NotEmpty(t, errs)

				var actual []string
				for _, err := range errs {
					actual = append(actual, err.errorMessages()...)
				}

				assert.Equal(t, tt.expected, actual)
			}
		})
	}
}


================================================
FILE: app/scheduler.go
================================================
package app

import (
	"time"

	"github.com/wtfutil/wtf/wtf"
)

// Schedule kicks off the first refresh of a module's data and then queues the rest of the
// data refreshes on a timer
func Schedule(widget wtf.Wtfable) {
	widget.Refresh()

	interval := widget.CommonSettings().RefreshInterval

	if interval <= 0 {
		return
	}

	timer := time.NewTicker(interval)

	for {
		select {
		case <-timer.C:
			if widget.Enabled() {
				widget.Refresh()
			} else {
				timer.Stop()
				return
			}
		case quit := <-widget.QuitChan():
			if quit {
				timer.Stop()
				return
			}
		}
	}
}


================================================
FILE: app/scheduler_test.go
================================================
package app

import (
	"testing"
	"time"

	"github.com/olebedev/config"
)

const (
	configExample = `
  wtf:
    mods:
      clocks:
        enabled: true
        position:
          top: 0
          left: 0
          height: 1
          width: 1
        refreshInterval: 2`

	new = `
  wtf:
    mods:
      clocks:
        enabled: true
        position:
          top: 0
          left: 0
          height: 1
          width: 1
        refreshInterval: 100ms`
)

func Test_RefreshInterval(t *testing.T) {
	t.Skip() // slow running test because a ticker is tested
	tests := []struct {
		name         string
		moduleName   string
		config       *config.Config
		testAttempts int
		expected     time.Duration
	}{
		{
			name:       "slow ticking module",
			moduleName: "clocks",
			config: func() *config.Config {
				cfg, _ := config.ParseYaml(configExample)
				return cfg
			}(),
			testAttempts: 10,
			expected:     2 * time.Second,
		},
		{
			name:       "fast ticking module",
			moduleName: "clocks",
			config: func() *config.Config {
				cfg, _ := config.ParseYaml(new)
				return cfg
			}(),
			testAttempts: 10,
			expected:     100 * time.Millisecond,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			widget := MakeWidget(nil, nil, tt.moduleName, tt.config, make(chan bool))

			interval := widget.CommonSettings().RefreshInterval // same declaration as in scheduler.go#Schedule
			timer := time.NewTicker(interval)

			attempts := 0
			for {
				select {
				case <-timer.C:
					attempts++
					if attempts == tt.testAttempts {
						return
					}
				// allow for small window (50ms) where a timeout is not triggered
				case <-time.After(tt.expected + 50*time.Millisecond):
					t.Error("Timeout")
				}
			}

		})
	}
}


================================================
FILE: app/widget_maker.go
================================================
package app

import (
	"github.com/olebedev/config"
	"github.com/rivo/tview"
	"github.com/wtfutil/wtf/modules/airbrake"
	"github.com/wtfutil/wtf/modules/asana"
	"github.com/wtfutil/wtf/modules/azuredevops"
	"github.com/wtfutil/wtf/modules/azurelogs"
	"github.com/wtfutil/wtf/modules/bamboohr"
	"github.com/wtfutil/wtf/modules/bargraph"
	"github.com/wtfutil/wtf/modules/buildkite"
	cdsfavorites "github.com/wtfutil/wtf/modules/cds/favorites"
	cdsqueue "github.com/wtfutil/wtf/modules/cds/queue"
	cdsstatus "github.com/wtfutil/wtf/modules/cds/status"
	"github.com/wtfutil/wtf/modules/circleci"
	"github.com/wtfutil/wtf/modules/clocks"
	"github.com/wtfutil/wtf/modules/cmdrunner"
	"github.com/wtfutil/wtf/modules/cryptocurrency/bittrex"
	"github.com/wtfutil/wtf/modules/cryptocurrency/blockfolio"
	"github.com/wtfutil/wtf/modules/cryptocurrency/cryptolive"
	"github.com/wtfutil/wtf/modules/cryptocurrency/mempool"
	"github.com/wtfutil/wtf/modules/datadog"
	"github.com/wtfutil/wtf/modules/devto"
	"github.com/wtfutil/wtf/modules/digitalclock"
	"github.com/wtfutil/wtf/modules/digitalocean"
	"github.com/wtfutil/wtf/modules/docker"
	"github.com/wtfutil/wtf/modules/feedreader"
	"github.com/wtfutil/wtf/modules/football"
	"github.com/wtfutil/wtf/modules/gcal"
	"github.com/wtfutil/wtf/modules/gerrit"
	"github.com/wtfutil/wtf/modules/git"
	"github.com/wtfutil/wtf/modules/github"
	"github.com/wtfutil/wtf/modules/gitlab"
	"github.com/wtfutil/wtf/modules/gitlabtodo"
	"github.com/wtfutil/wtf/modules/gitter"
	"github.com/wtfutil/wtf/modules/googleanalytics"
	"github.com/wtfutil/wtf/modules/grafana"
	"github.com/wtfutil/wtf/modules/gspreadsheets"
	"github.com/wtfutil/wtf/modules/hackernews"
	"github.com/wtfutil/wtf/modules/healthchecks"
	"github.com/wtfutil/wtf/modules/hibp"
	"github.com/wtfutil/wtf/modules/ipaddresses/ipapi"
	"github.com/wtfutil/wtf/modules/ipaddresses/ipinfo"
	"github.com/wtfutil/wtf/modules/jenkins"
	"github.com/wtfutil/wtf/modules/jira"
	"github.com/wtfutil/wtf/modules/krisinformation"
	"github.com/wtfutil/wtf/modules/kubernetes"
	"github.com/wtfutil/wtf/modules/logger"
	"github.com/wtfutil/wtf/modules/lunarphase"
	"github.com/wtfutil/wtf/modules/mercurial"
	"github.com/wtfutil/wtf/modules/nbascore"
	"github.com/wtfutil/wtf/modules/newrelic"
	"github.com/wtfutil/wtf/modules/nextbus"
	"github.com/wtfutil/wtf/modules/opsgenie"
	"github.com/wtfutil/wtf/modules/pagerduty"
	"github.com/wtfutil/wtf/modules/pihole"
	"github.com/wtfutil/wtf/modules/ping"
	"github.com/wtfutil/wtf/modules/pivotal"
	"github.com/wtfutil/wtf/modules/pocket"
	"github.com/wtfutil/wtf/modules/power"
	"github.com/wtfutil/wtf/modules/progress"
	"github.com/wtfutil/wtf/modules/resourceusage"
	"github.com/wtfutil/wtf/modules/rollbar"
	"github.com/wtfutil/wtf/modules/security"
	"github.com/wtfutil/wtf/modules/spacex"
	"github.com/wtfutil/wtf/modules/spotify"
	"github.com/wtfutil/wtf/modules/spotifyweb"
	"github.com/wtfutil/wtf/modules/status"
	"github.com/wtfutil/wtf/modules/steam"
	"github.com/wtfutil/wtf/modules/stocks/finnhub"
	"github.com/wtfutil/wtf/modules/stocks/yfinance"
	"github.com/wtfutil/wtf/modules/subreddit"
	"github.com/wtfutil/wtf/modules/textfile"
	"github.com/wtfutil/wtf/modules/todo"
	"github.com/wtfutil/wtf/modules/todo_plus"
	"github.com/wtfutil/wtf/modules/transmission"
	"github.com/wtfutil/wtf/modules/travisci"
	"github.com/wtfutil/wtf/modules/twitch"
	"github.com/wtfutil/wtf/modules/twitter"
	"github.com/wtfutil/wtf/modules/twitterstats"
	"github.com/wtfutil/wtf/modules/unknown"
	"github.com/wtfutil/wtf/modules/updown"
	"github.com/wtfutil/wtf/modules/uptimekuma"
	"github.com/wtfutil/wtf/modules/uptimerobot"
	"github.com/wtfutil/wtf/modules/urlcheck"
	"github.com/wtfutil/wtf/modules/victorops"
	"github.com/wtfutil/wtf/modules/weatherservices/arpansagovau"
	"github.com/wtfutil/wtf/modules/weatherservices/prettyweather"
	"github.com/wtfutil/wtf/modules/weatherservices/weather"
	"github.com/wtfutil/wtf/modules/zendesk"
	"github.com/wtfutil/wtf/wtf"
)

// MakeWidget creates and returns instances of widgets
func MakeWidget(
	tviewApp *tview.Application,
	pages *tview.Pages,
	moduleName string,
	config *config.Config,
	redrawChan chan bool,
) wtf.Wtfable {
	var widget wtf.Wtfable

	moduleConfig, _ := config.Get("wtf.mods." + moduleName)

	// Don' try to initialize modules that don't exist
	if moduleConfig == nil {
		return nil
	}

	// Don't try to initialize modules that aren't enabled
	if enabled := moduleConfig.UBool("enabled", false); !enabled {
		return nil
	}

	// Always in alphabetical order
	switch moduleConfig.UString("type", moduleName) {
	case "airbrake":
		settings := airbrake.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = airbrake.NewWidget(tviewApp, redrawChan, pages, settings)
	case "arpansagovau":
		settings := arpansagovau.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = arpansagovau.NewWidget(tviewApp, redrawChan, settings)
	case "asana":
		settings := asana.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = asana.NewWidget(tviewApp, redrawChan, pages, settings)
	case "azuredevops":
		settings := azuredevops.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = azuredevops.NewWidget(tviewApp, redrawChan, pages, settings)
	case "azurelogs":
		settings := azurelogs.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = azurelogs.NewWidget(tviewApp, redrawChan, pages, settings)
	case "bamboohr":
		settings := bamboohr.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = bamboohr.NewWidget(tviewApp, redrawChan, settings)
	case "bargraph":
		settings := bargraph.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = bargraph.NewWidget(tviewApp, redrawChan, settings)
	case "bittrex":
		settings := bittrex.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = bittrex.NewWidget(tviewApp, redrawChan, settings)
	case "blockfolio":
		settings := blockfolio.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = blockfolio.NewWidget(tviewApp, redrawChan, settings)
	case "buildkite":
		settings := buildkite.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = buildkite.NewWidget(tviewApp, redrawChan, pages, settings)
	case "cdsFavorites":
		settings := cdsfavorites.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = cdsfavorites.NewWidget(tviewApp, redrawChan, pages, settings)
	case "cdsQueue":
		settings := cdsqueue.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = cdsqueue.NewWidget(tviewApp, redrawChan, pages, settings)
	case "cdsStatus":
		settings := cdsstatus.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = cdsstatus.NewWidget(tviewApp, redrawChan, pages, settings)
	case "circleci":
		settings := circleci.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = circleci.NewWidget(tviewApp, redrawChan, settings)
	case "clocks":
		settings := clocks.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = clocks.NewWidget(tviewApp, redrawChan, settings)
	case "cmdrunner":
		settings := cmdrunner.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = cmdrunner.NewWidget(tviewApp, redrawChan, settings)
	case "cryptolive":
		settings := cryptolive.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = cryptolive.NewWidget(tviewApp, redrawChan, settings)
	case "datadog":
		settings := datadog.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = datadog.NewWidget(tviewApp, redrawChan, pages, settings)
	case "devto":
		settings := devto.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = devto.NewWidget(tviewApp, redrawChan, pages, settings)
	case "digitalclock":
		settings := digitalclock.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = digitalclock.NewWidget(tviewApp, redrawChan, settings)
	case "digitalocean":
		settings := digitalocean.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = digitalocean.NewWidget(tviewApp, redrawChan, pages, settings)
	case "docker":
		settings := docker.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = docker.NewWidget(tviewApp, redrawChan, pages, settings)
	case "feedreader":
		settings := feedreader.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = feedreader.NewWidget(tviewApp, redrawChan, pages, settings)
	case "football":
		settings := football.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = football.NewWidget(tviewApp, redrawChan, pages, settings)
	case "gcal":
		settings := gcal.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = gcal.NewWidget(tviewApp, redrawChan, settings)
	case "gerrit":
		settings := gerrit.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = gerrit.NewWidget(tviewApp, redrawChan, pages, settings)
	case "git":
		settings := git.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = git.NewWidget(tviewApp, redrawChan, pages, settings)
	case "github":
		settings := github.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = github.NewWidget(tviewApp, redrawChan, pages, settings)
	case "gitlab":
		settings := gitlab.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = gitlab.NewWidget(tviewApp, redrawChan, pages, settings)
	case "gitlabtodo":
		settings := gitlabtodo.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = gitlabtodo.NewWidget(tviewApp, redrawChan, pages, settings)
	case "gitter":
		settings := gitter.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = gitter.NewWidget(tviewApp, redrawChan, pages, settings)
	case "googleanalytics":
		settings := googleanalytics.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = googleanalytics.NewWidget(tviewApp, redrawChan, settings)
	case "gspreadsheets":
		settings := gspreadsheets.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = gspreadsheets.NewWidget(tviewApp, redrawChan, settings)
	case "grafana":
		settings := grafana.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = grafana.NewWidget(tviewApp, redrawChan, pages, settings)
	case "hackernews":
		settings := hackernews.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = hackernews.NewWidget(tviewApp, redrawChan, pages, settings)
	case "healthchecks":
		settings := healthchecks.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = healthchecks.NewWidget(tviewApp, redrawChan, pages, settings)
	case "hibp":
		settings := hibp.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = hibp.NewWidget(tviewApp, redrawChan, settings)
	case "ipapi":
		settings := ipapi.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = ipapi.NewWidget(tviewApp, redrawChan, settings)
	case "ipinfo":
		settings := ipinfo.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = ipinfo.NewWidget(tviewApp, redrawChan, settings)
	case "jenkins":
		settings := jenkins.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = jenkins.NewWidget(tviewApp, redrawChan, pages, settings)
	case "jira":
		settings := jira.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = jira.NewWidget(tviewApp, redrawChan, pages, settings)
	case "kubernetes":
		settings := kubernetes.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = kubernetes.NewWidget(tviewApp, redrawChan, settings)
	case "krisinformation":
		settings := krisinformation.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = krisinformation.NewWidget(tviewApp, redrawChan, settings)
	case "logger":
		settings := logger.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = logger.NewWidget(tviewApp, redrawChan, settings)
	case "lunarphase":
		settings := lunarphase.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = lunarphase.NewWidget(tviewApp, redrawChan, pages, settings)
	case "mercurial":
		settings := mercurial.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = mercurial.NewWidget(tviewApp, redrawChan, pages, settings)
	case "mempool":
		settings := mempool.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = mempool.NewWidget(tviewApp, redrawChan, pages, settings)
	case "nbascore":
		settings := nbascore.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = nbascore.NewWidget(tviewApp, redrawChan, pages, settings)
	case "newrelic":
		settings := newrelic.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = newrelic.NewWidget(tviewApp, redrawChan, pages, settings)
	case "nextbus":
		settings := nextbus.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = nextbus.NewWidget(tviewApp, redrawChan, pages, settings)
	case "opsgenie":
		settings := opsgenie.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = opsgenie.NewWidget(tviewApp, redrawChan, settings)
	case "pagerduty":
		settings := pagerduty.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = pagerduty.NewWidget(tviewApp, redrawChan, settings)
	case "pihole":
		settings := pihole.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = pihole.NewWidget(tviewApp, redrawChan, pages, settings)
	case "ping":
		settings := ping.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = ping.NewWidget(tviewApp, redrawChan, settings)
	case "power":
		settings := power.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = power.NewWidget(tviewApp, redrawChan, settings)
	case "prettyweather":
		settings := prettyweather.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = prettyweather.NewWidget(tviewApp, redrawChan, settings)
	case "progress":
		settings := progress.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = progress.NewWidget(tviewApp, redrawChan, settings)
	case "pocket":
		settings := pocket.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = pocket.NewWidget(tviewApp, redrawChan, pages, settings)
	case "resourceusage":
		settings := resourceusage.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = resourceusage.NewWidget(tviewApp, redrawChan, settings)
	case "rollbar":
		settings := rollbar.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = rollbar.NewWidget(tviewApp, redrawChan, pages, settings)
	case "security":
		settings := security.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = security.NewWidget(tviewApp, redrawChan, settings)
	case "spacex":
		settings := spacex.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = spacex.NewWidget(tviewApp, redrawChan, settings)
	case "spotify":
		settings := spotify.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = spotify.NewWidget(tviewApp, redrawChan, pages, settings)
	case "spotifyweb":
		settings := spotifyweb.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = spotifyweb.NewWidget(tviewApp, redrawChan, pages, settings)
	case "status":
		settings := status.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = status.NewWidget(tviewApp, redrawChan, settings)
	case "steam":
		settings := steam.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = steam.NewWidget(tviewApp, redrawChan, pages, settings)
	case "subreddit":
		settings := subreddit.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = subreddit.NewWidget(tviewApp, redrawChan, pages, settings)
	case "textfile":
		settings := textfile.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = textfile.NewWidget(tviewApp, redrawChan, pages, settings)
	case "todo":
		settings := todo.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = todo.NewWidget(tviewApp, redrawChan, pages, settings)
	case "todo_plus":
		settings := todo_plus.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = todo_plus.NewWidget(tviewApp, redrawChan, pages, settings)
	case "todoist":
		settings := todo_plus.FromTodoist(moduleName, moduleConfig, config)
		widget = todo_plus.NewWidget(tviewApp, redrawChan, pages, settings)
	case "transmission":
		settings := transmission.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = transmission.NewWidget(tviewApp, redrawChan, pages, settings)
	case "travisci":
		settings := travisci.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = travisci.NewWidget(tviewApp, redrawChan, pages, settings)
	case "trello":
		settings := todo_plus.FromTrello(moduleName, moduleConfig, config)
		widget = todo_plus.NewWidget(tviewApp, redrawChan, pages, settings)
	case "twitch":
		settings := twitch.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = twitch.NewWidget(tviewApp, redrawChan, pages, settings)
	case "twitter":
		settings := twitter.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = twitter.NewWidget(tviewApp, redrawChan, pages, settings)
	case "twitterstats":
		settings := twitterstats.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = twitterstats.NewWidget(tviewApp, redrawChan, pages, settings)
	case "updown":
		settings := updown.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = updown.NewWidget(tviewApp, redrawChan, pages, settings)
	case "uptimekuma":
		settings := uptimekuma.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = uptimekuma.NewWidget(tviewApp, redrawChan, pages, settings)
	case "uptimerobot":
		settings := uptimerobot.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = uptimerobot.NewWidget(tviewApp, redrawChan, pages, settings)
	case "urlcheck":
		settings := urlcheck.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = urlcheck.NewWidget(tviewApp, redrawChan, settings)
	case "victorops":
		settings := victorops.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = victorops.NewWidget(tviewApp, redrawChan, settings)
	case "weather":
		settings := weather.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = weather.NewWidget(tviewApp, redrawChan, pages, settings)
	case "zendesk":
		settings := zendesk.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = zendesk.NewWidget(tviewApp, redrawChan, pages, settings)
	case "pivotal":
		settings := pivotal.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = pivotal.NewWidget(tviewApp, redrawChan, pages, settings)
	case "finnhub":
		settings := finnhub.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = finnhub.NewWidget(tviewApp, redrawChan, settings)
	case "yfinance":
		settings := yfinance.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = yfinance.NewWidget(tviewApp, redrawChan, settings)
	default:
		settings := unknown.NewSettingsFromYAML(moduleName, moduleConfig, config)
		widget = unknown.NewWidget(tviewApp, redrawChan, settings)
	}

	return widget
}

// MakeWidgets creates and returns a collection of enabled widgets
func MakeWidgets(tviewApp *tview.Application, pages *tview.Pages, config *config.Config, redrawChan chan bool) []wtf.Wtfable {
	var widgets []wtf.Wtfable

	moduleNames, _ := config.Map("wtf.mods")

	for moduleName := range moduleNames {
		widget := MakeWidget(tviewApp, pages, moduleName, config, redrawChan)

		if widget != nil {
			widgets = append(widgets, widget)
		}
	}

	return widgets
}


================================================
FILE: app/widget_maker_test.go
================================================
package app

import (
	"testing"

	"github.com/olebedev/config"
	"github.com/stretchr/testify/assert"
	"github.com/wtfutil/wtf/modules/clocks"
	"github.com/wtfutil/wtf/wtf"
)

const (
	disabled = `
wtf:
  mods:
    clocks:
      enabled: false
      position:
        top: 0
        left: 0
        height: 1
        width: 1
      refreshInterval: 30`

	enabled = `
wtf:
  mods:
    clocks:
      enabled: true
      position:
        top: 0
        left: 0
        height: 1
        width: 1
      refreshInterval: 30`
)

func Test_MakeWidget(t *testing.T) {
	tests := []struct {
		name       string
		moduleName string
		config     *config.Config
		expected   wtf.Wtfable
	}{
		{
			name:       "invalid module",
			moduleName: "",
			config:     &config.Config{},
			expected:   nil,
		},
		{
			name:       "valid disabled module",
			moduleName: "clocks",
			config: func() *config.Config {
				cfg, _ := config.ParseYaml(disabled)
				return cfg
			}(),
			expected: nil,
		},
		{
			name:       "valid enabled module",
			moduleName: "clocks",
			config: func() *config.Config {
				cfg, _ := config.ParseYaml(enabled)
				return cfg
			}(),
			expected: &clocks.Widget{},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			actual := MakeWidget(nil, nil, tt.moduleName, tt.config, make(chan bool))
			assert.IsType(t, tt.expected, actual)
		})
	}
}


================================================
FILE: app/wtf_app.go
================================================
package app

import (
	"fmt"
	"log"
	"os"
	"time"

	_ "github.com/gdamore/tcell/terminfo/extended"
	"github.com/gdamore/tcell/v2"
	"github.com/olebedev/config"
	"github.com/radovskyb/watcher"
	"github.com/rivo/tview"

	"github.com/wtfutil/wtf/cfg"
	"github.com/wtfutil/wtf/support"
	"github.com/wtfutil/wtf/utils"
	"github.com/wtfutil/wtf/wtf"
)

// WtfApp is the container for a collection of widgets that are all constructed from a single
// configuration file and displayed together
type WtfApp struct {
	TViewApp *tview.Application

	config         *config.Config
	configFilePath string
	display        *Display
	focusTracker   FocusTracker
	ghUser         *support.GitHubUser
	pages          *tview.Pages
	validator      *ModuleValidator
	widgets        []wtf.Wtfable
	configWatcher  *watcher.Watcher

	// The redrawChan channel is used to allow modules to signal back to the main loop that
	// the screen needs to be explicitly redrawn, instead of waiting for tcell to redraw
	// on a user event, because something has visually changed
	redrawChan chan bool
}

// NewWtfApp creates and returns an instance of WtfApp
func NewWtfApp(tviewApp *tview.Application, config *config.Config, configFilePath string) *WtfApp {
	wtfApp := &WtfApp{
		TViewApp: tviewApp,

		config:         config,
		configFilePath: configFilePath,
		pages:          tview.NewPages(),

		redrawChan: make(chan bool, 1),
	}

	wtfApp.TViewApp.SetBeforeDrawFunc(func(s tcell.Screen) bool {
		s.Clear()
		return false
	})

	wtfApp.widgets = MakeWidgets(wtfApp.TViewApp, wtfApp.pages, wtfApp.config, wtfApp.redrawChan)
	if len(wtfApp.widgets) == 0 {
		fmt.Println("No modules were defined. Make sure you have at least one properly defined widget")
		os.Exit(1)
	}

	wtfApp.display = NewDisplay(wtfApp.widgets, wtfApp.config)
	wtfApp.focusTracker = NewFocusTracker(wtfApp.TViewApp, wtfApp.widgets, wtfApp.config)
	wtfApp.validator = NewModuleValidator()

	githubAPIKey := readGitHubAPIKey(wtfApp.config)
	wtfApp.ghUser = support.NewGitHubUser(githubAPIKey)

	wtfApp.pages.AddPage("grid", wtfApp.display.Grid, true, true)

	wtfApp.validator.Validate(wtfApp.widgets)

	firstWidget := wtfApp.widgets[0]
	wtfApp.pages.SetBackgroundColor(
		wtf.ColorFor(
			firstWidget.CommonSettings().Colors.Background,
		),
	)

	wtfApp.TViewApp.SetInputCapture(wtfApp.keyboardIntercept)
	wtfApp.TViewApp.SetRoot(wtfApp.pages, true)

	// Create a watcher to handle calls to redraw the screen
	go handleRedraws(wtfApp.TViewApp, wtfApp.redrawChan)

	return wtfApp
}

func handleRedraws(tviewApp *tview.Application, redrawChan chan bool) {
	if redrawChan == nil {
		return
	}

	for {
		data, ok := <-redrawChan
		if !ok {
			return
		}

		if data {
			tviewApp.Draw()
		}
	}
}

/* -------------------- Exported Functions -------------------- */

// Exit quits the app
func (wtfApp *WtfApp) Exit() {
	wtfApp.Stop()
	wtfApp.TViewApp.Stop()
	wtfApp.DisplayExitMessage()
	os.Exit(0)
}

// Execute starts the underlying tview app
func (wtfApp *WtfApp) Execute() error {
	if err := wtfApp.TViewApp.Run(); err != nil {
		return err
	}

	return nil
}

// Start initializes the app
func (wtfApp *WtfApp) Start() {
	go wtfApp.scheduleWidgets()
	go wtfApp.watchForConfigChanges()

	// FIXME: This should be moved to the AppManager
	go func() { _ = wtfApp.ghUser.Load() }()
}

// Stop kills all the currently-running widgets in this app
func (wtfApp *WtfApp) Stop() {
	wtfApp.stopAllWidgets()
	if wtfApp.configWatcher != nil {
		wtfApp.configWatcher.Close()
	}
	close(wtfApp.redrawChan)
}

/* -------------------- Unexported Functions -------------------- */

func (wtfApp *WtfApp) stopAllWidgets() {
	for _, widget := range wtfApp.widgets {
		widget.Stop()
	}
}

func (wtfApp *WtfApp) keyboardIntercept(event *tcell.EventKey) *tcell.EventKey {
	// These keys are global keys used by the app. Widgets should not implement these keys
	switch event.Key() {
	case tcell.KeyCtrlC:
		wtfApp.Stop()
		wtfApp.TViewApp.Stop()
		wtfApp.DisplayExitMessage()
	case tcell.KeyCtrlR:
		wtfApp.refreshAllWidgets()
		return nil
	case tcell.KeyCtrlSpace:
		// FIXME: This can't reside in the app, the app doesn't know about
		// the AppManager. The AppManager needs to catch this one
		fmt.Println("Next app")
		return nil
	case tcell.KeyTab:
		wtfApp.focusTracker.Next()
	case tcell.KeyBacktab:
		wtfApp.focusTracker.Prev()
		return nil
	case tcell.KeyEsc:
		wtfApp.focusTracker.None()
	}

	// Checks to see if any widget has been assigned the pressed key as its focus key
	if wtfApp.focusTracker.FocusOn(string(event.Rune())) {
		return nil
	}

	// If no specific widget has focus, then allow the key presses to fall through to the app
	if !wtfApp.focusTracker.IsFocused {
		switch string(event.Rune()) {
		case "q":
			wtfApp.Exit()
		case "/":
			return nil
		default:
		}
	}

	return event
}

func (wtfApp *WtfApp) refreshAllWidgets() {
	for _, widget := range wtfApp.widgets {
		go widget.Refresh()
	}
}

func (wtfApp *WtfApp) scheduleWidgets() {
	for _, widget := range wtfApp.widgets {
		go Schedule(widget)
	}
}

func (wtfApp *WtfApp) watchForConfigChanges() {
	wtfApp.configWatcher = watcher.New()
	watch := wtfApp.configWatcher

	// Notify write events
	watch.FilterOps(watcher.Write)

	go func() {
		for {
			select {
			case <-watch.Event:
				wtfApp.Stop()

				config := cfg.LoadWtfConfigFile(wtfApp.configFilePath)
				newApp := NewWtfApp(wtfApp.TViewApp, config, wtfApp.configFilePath)
				openURLUtil := utils.ToStrs(config.UList("wtf.openUrlUtil", []interface{}{}))
				utils.Init(config.UString("wtf.openFileUtil", "open"), openURLUtil)

				newApp.Start()
			case err := <-watch.Error:
				if err == watcher.ErrWatchedFileDeleted {
					// Usually happens because the watcher looks for the file as the OS is updating it
					continue
				}
				log.Fatalln(err)
			case <-watch.Closed:
				return
			}
		}
	}()

	// Watch config file for changes.
	absPath, _ := utils.ExpandHomeDir(wtfApp.configFilePath)
	if err := watch.Add(absPath); err != nil {
		log.Fatalln(err)
	}

	// Start the watching process - it'll check for changes every 100ms.
	if err := watch.Start(time.Millisecond * 100); err != nil {
		log.Fatalln(err)
	}
}


================================================
FILE: cfg/common_settings.go
================================================
package cfg

import (
	"fmt"
	"strings"
	"time"

	"github.com/olebedev/config"
	"golang.org/x/text/language"
	"golang.org/x/text/message"
)

const (
	defaultLanguageTag = "en-CA"
)

type Module struct {
	Name string
	Type string
}

type Sigils struct {
	Checkbox struct {
		Checked   string
		Unchecked string
	}
	Paging struct {
		Normal   string
		Selected string
	}
}

// Common defines a set of common configuration settings applicable to all modules
type Common struct {
	Module
	PositionSettings `help:"Defines where in the grid this module's widget will be displayed."`
	Sigils

	Colors ColorTheme
	Config *config.Config

	DocPath string

	Bordered        bool          `help:"Whether or not the module should be displayed with a border." values:"true, false" optional:"true" default:"true"`
	Enabled         bool          `help:"Whether or not this module is executed and if its data displayed onscreen." values:"true, false" optional:"true" default:"false"`
	Focusable       bool          `help:"Whether or  not this module is focusable." values:"true, false" optional:"true" default:"false"`
	LanguageTag     string        `help:"The BCP 47 language tag to localize text to." values:"Any supported BCP 47 language tag." optional:"true" default:"en-CA"`
	RefreshInterval time.Duration `help:"How often this module will update its data." values:"A positive integer followed by a time unit (ns, us, ms, s, m, h, or nothing which defaults to s)" optional:"true"`
	Title           string        `help:"The title string to show when displaying this module" optional:"true"`

	focusChar int `help:"Define one of the number keys as a short cut key to access the widget." optional:"true"`
}

// NewCommonSettingsFromModule returns a common settings configuration tailed to the given module
func NewCommonSettingsFromModule(name, defaultTitle string, defaultFocusable bool, moduleConfig *config.Config, globalConfig *config.Config) *Common {
	baseColors := NewDefaultColorTheme()

	colorsConfig, err := globalConfig.Get("wtf.colors")
	if err != nil && strings.Contains(err.Error(), "Nonexistent map") {
		// Create a default colors config to fill in for the missing one
		// This comes into play when the configuration file does not contain a `colors:` key, i.e:
		//
		//     wtf:
		//       # colors:                <- missing
		//       refreshInterval: 1
		//       openFileUtil: "open"
		//
		colorsConfig, _ = NewDefaultColorConfig()
	}

	// And finally create a third instance to be the final default fallback in case there are empty or nil values in
	// the colors extracted from the config file (aka colorsConfig)
	defaultColorTheme := NewDefaultColorTheme()

	baseColors.Focusable = moduleConfig.UString("colors.border.focusable", colorsConfig.UString("border.focusable", defaultColorTheme.Focusable))
	baseColors.Focused = moduleConfig.UString("colors.border.focused", colorsConfig.UString("border.focused", defaultColorTheme.Focused))
	baseColors.Unfocusable = moduleConfig.UString("colors.border.normal", colorsConfig.UString("border.normal", defaultColorTheme.Unfocusable))

	baseColors.Checked = moduleConfig.UString("colors.checked", colorsConfig.UString("checked", defaultColorTheme.Checked))

	baseColors.EvenForeground = moduleConfig.UString("colors.rows.even", colorsConfig.UString("rows.even", defaultColorTheme.EvenForeground))
	baseColors.OddForeground = moduleConfig.UString("colors.rows.odd", colorsConfig.UString("rows.odd", defaultColorTheme.OddForeground))

	baseColors.Label = moduleConfig.UString("colors.label", colorsConfig.UString("label", defaultColorTheme.Label))
	baseColors.Subheading = moduleConfig.UString("colors.subheading", colorsConfig.UString("subheading", defaultColorTheme.Subheading))
	baseColors.Text = moduleConfig.UString("colors.text", colorsConfig.UString("text", defaultColorTheme.Text))
	baseColors.Title = moduleConfig.UString("colors.title", colorsConfig.UString("title", defaultColorTheme.Title))

	baseColors.Background = moduleConfig.UString("colors.background", colorsConfig.UString("background", defaultColorTheme.Background))

	common := Common{
		Colors: baseColors,

		Module: Module{
			Name: name,
			Type: moduleConfig.UString("type", name),
		},

		PositionSettings: NewPositionSettingsFromYAML(moduleConfig),

		Bordered:        moduleConfig.UBool("border", true),
		Config:          moduleConfig,
		Enabled:         moduleConfig.UBool("enabled", false),
		Focusable:       moduleConfig.UBool("focusable", defaultFocusable),
		LanguageTag:     globalConfig.UString("wtf.language", defaultLanguageTag),
		RefreshInterval: ParseTimeString(moduleConfig, "refreshInterval", "300s"),
		Title:           moduleConfig.UString("title", defaultTitle),

		focusChar: moduleConfig.UInt("focusChar", -1),
	}

	sigilsPath := "wtf.sigils"
	common.Checkbox.Checked = globalConfig.UString(sigilsPath+".checkbox.checked", "x")
	common.Checkbox.Unchecked = globalConfig.UString(sigilsPath+".checkbox.unchecked", " ")
	common.Paging.Normal = globalConfig.UString(sigilsPath+".paging.normal", globalConfig.UString("wtf.paging.pageSigil", "*"))
	common.Paging.Selected = globalConfig.UString(sigilsPath+".paging.select", globalConfig.UString("wtf.paging.selectedSigil", "_"))

	return &common
}

/* -------------------- Exported Functions -------------------- */

func (common *Common) DefaultFocusedRowColor() string {
	return fmt.Sprintf(
		"%s:%s",
		common.Colors.HighlightedForeground,
		common.Colors.HighlightedBackground,
	)
}

func (common *Common) DefaultRowColor() string {
	return fmt.Sprintf(
		"%s:%s",
		common.Colors.EvenForeground,
		common.Colors.EvenBackground,
	)
}

// FocusChar returns the keyboard number assigned to the widget used to give onscreen
// focus to this widget, as a string. Focus characters can be a range between 1 and 9
func (common *Common) FocusChar() string {
	if common.focusChar <= 0 {
		return ""
	}

	if common.focusChar > 9 {
		return ""
	}

	return fmt.Sprint(common.focusChar)
}

// LocalizedPrinter returns a message.Printer instance localized to the BCP 47 language
// configuration value defined in 'wtf.language' config. If none exists, it defaults to
// 'en-CA'. Use this to format numbers, etc.
func (common *Common) LocalizedPrinter() (*message.Printer, error) {
	langTag, err := language.Parse(common.LanguageTag)
	if err != nil {
		return nil, err
	}

	prntr := message.NewPrinter(langTag)

	return prntr, nil
}

func (common *Common) RowColor(idx int) string {
	if idx%2 == 0 {
		return fmt.Sprintf(
			"%s:%s",
			common.Colors.EvenForeground,
			common.Colors.EvenBackground,
		)
	}
	return fmt.Sprintf(
		"%s:%s",
		common.Colors.OddForeground,
		common.Colors.OddBackground,
	)
}

func (*Common) RightAlignFormat(width int) string {
	borderOffset := 2
	return fmt.Sprintf("%%%ds", width-borderOffset)
}

// PaginationMarker generates the pagination indicators that appear in the top-right corner
// of multisource widgets
func (common *Common) PaginationMarker(length, pos, width int) string {
	sigils := ""

	if length > 1 {
		sigils = strings.Repeat(common.Paging.Normal, pos)
		sigils += common.Paging.Selected
		sigils += strings.Repeat(common.Paging.Normal, length-1-pos)

		sigils = "[lightblue]" + fmt.Sprintf(common.RightAlignFormat(width), sigils) + "[white]"
	}

	return sigils
}

// SetDocumentationPath is used to explicitly set the documentation path that should be opened
// when the key to open the documentation is pressed.
// Setting this is probably not necessary unless the module documentation is nested inside a
// documentation subdirectory in the /wtfutildocs repo, or the module here has a different
// name than the module's display name in the documentation (which ideally wouldn't be a thing).
func (common *Common) SetDocumentationPath(path string) {
	common.DocPath = path
}

// Validations aggregates all the validations from all the sub-sections in Common into a
// single array of validations
func (common *Common) Validations() []Validatable {
	var validatables []Validatable

	for _, validation := range common.PositionSettings.Validations.validations {
		validatables = append(validatables, validation)
	}

	return validatables
}


================================================
FILE: cfg/common_settings_test.go
================================================
package cfg

import (
	"testing"
	"time"

	"github.com/olebedev/config"
	"github.com/stretchr/testify/assert"
)

var (
	testYaml = `
wtf:
  colors:
`

	moduleConfig, _   = config.ParseYaml(testYaml)
	globalSettings, _ = config.ParseYaml(testYaml)

	testCfg = NewCommonSettingsFromModule(
		"test",
		"Test Config",
		true,
		moduleConfig,
		globalSettings,
	)
)

func Test_NewCommonSettingsFromModule(t *testing.T) {
	assert.Equal(t, true, testCfg.Bordered)
	assert.Equal(t, false, testCfg.Enabled)
	assert.Equal(t, true, testCfg.Focusable)
	assert.Equal(t, "test", testCfg.Name)
	assert.Equal(t, "test", testCfg.Type)
	assert.Equal(t, "", testCfg.FocusChar())
	assert.Equal(t, 300*time.Second, testCfg.RefreshInterval)
	assert.Equal(t, "Test Config", testCfg.Title)
}

func Test_DefaultFocusedRowColor(t *testing.T) {
	assert.Equal(t, "black:green", testCfg.DefaultFocusedRowColor())
}

func Test_DefaultRowColor(t *testing.T) {
	assert.Equal(t, "white:transparent", testCfg.DefaultRowColor())
}

func Test_FocusChar(t *testing.T) {
	tests := []struct {
		name         string
		before       func(testCfg *Common)
		expectedChar string
	}{
		{
			name: "with negative focus char",
			before: func(testCfg *Common) {
				testCfg.focusChar = -1
			},
			expectedChar: "",
		},
		{
			name: "with positive focus char",
			before: func(testCfg *Common) {
				testCfg.focusChar = 3
			},
			expectedChar: "3",
		},
		{
			name: "with zero focus char",
			before: func(testCfg *Common) {
				testCfg.focusChar = 0
			},
			expectedChar: "",
		},
		{
			name: "with large focus char",
			before: func(testCfg *Common) {
				testCfg.focusChar = 10
			},
			expectedChar: "",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			tt.before(testCfg)

			assert.Equal(t, tt.expectedChar, testCfg.FocusChar())
		})
	}
}

func Test_RowColor(t *testing.T) {
	tests := []struct {
		name          string
		idx           int
		expectedColor string
	}{
		{
			name:          "odd rows, default",
			idx:           3,
			expectedColor: "lightblue:transparent",
		},
		{
			name:          "even rows, default",
			idx:           8,
			expectedColor: "white:transparent",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			assert.Equal(t, tt.expectedColor, testCfg.RowColor(tt.idx))
		})
	}
}

func Test_RightAlignFormat(t *testing.T) {
	tests := []struct {
		name     string
		width    int
		expected string
	}{
		{
			name:     "with zero",
			width:    0,
			expected: "%-2s",
		},
		{
			name:     "with positive integer",
			width:    3,
			expected: "%1s",
		},
		{
			name:     "with negative integer",
			width:    -3,
			expected: "%-5s",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			assert.Equal(t, tt.expected, testCfg.RightAlignFormat(tt.width))
		})
	}
}

func Test_PaginationMarker(t *testing.T) {
	tests := []struct {
		name     string
		len      int
		pos      int
		width    int
		expected string
	}{
		{
			name:     "with zero pages",
			len:      0,
			pos:      1,
			width:    5,
			expected: "",
		},
		{
			name:     "with one page",
			len:      1,
			pos:      1,
			width:    5,
			expected: "",
		},
		{
			name:     "with multiple pages",
			len:      3,
			pos:      1,
			width:    5,
			expected: "[lightblue]*_*[white]",
		},
		{
			name:     "with negative pages",
			len:      -3,
			pos:      1,
			width:    5,
			expected: "",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			assert.Equal(t, tt.expected, testCfg.PaginationMarker(tt.len, tt.pos, tt.width))
		})
	}
}

func Test_Validations(t *testing.T) {
	assert.Equal(t, 4, len(testCfg.Validations()))
}


================================================
FILE: cfg/config_files.go
================================================
package cfg

import (
	"errors"
	"fmt"
	"os"
	"os/user"
	"path/filepath"

	"github.com/olebedev/config"
)

const (
	// XdgConfigDir defines the path to the minimal XDG-compatible configuration directory
	XdgConfigDir = "~/.config/"

	// WtfConfigDirV1 defines the path to the first version of configuration. Do not use this
	WtfConfigDirV1 = "~/.wtf/"

	// WtfConfigDirV2 defines the path to the second version of the configuration. Use this.
	WtfConfigDirV2 = "~/.config/wtf/"

	// WtfConfigFile defines the name of the default config file
	WtfConfigFile = "config.yml"
)

/* -------------------- Exported Functions -------------------- */

// CreateFile creates the named file in the config directory, if it does not already exist.
// If the file exists it does not recreate it.
// If successful, returns the absolute path to the file
// If unsuccessful, returns an error
func CreateFile(fileName string) (string, error) {
	configDir, err := WtfConfigDir()
	if err != nil {
		return "", err
	}

	filePath := filepath.Join(configDir, fileName)

	// Check if the file already exists; if it does not, create it
	_, err = os.Stat(filePath)
	if err != nil {
		if os.IsNotExist(err) {
			_, err = os.Create(filePath)
			if err != nil {
				return "", err
			}
		} else {
			return "", err
		}
	}

	return filePath, nil
}

// Initialize takes care of settings up the initial state of WTF configuration
// It ensures necessary directories and files exist
func Initialize(hasCustom bool) {
	if !hasCustom {
		migrateOldConfig()
	}

	// These always get created because this is where modules should write any permanent
	// data they need to persist between runs (i.e.: log, textfile, etc.)
	createWtfConfigDir()

	if !hasCustom {
		createWtfConfigFile()
		chmodConfigFile()
	}
}

// WtfConfigDir returns the absolute path to the configuration directory
func WtfConfigDir() (string, error) {
	configDir := os.Getenv("XDG_CONFIG_HOME")
	if configDir == "" {
		configDir = WtfConfigDirV2
	} else {
		configDir += "/wtf/"
	}
	configDir, err := expandHomeDir(configDir)
	if err != nil {
		return "", err
	}

	return configDir, nil
}

// LoadWtfConfigFile loads the specified config file
func LoadWtfConfigFile(filePath string) *config.Config {
	absPath, _ := expandHomeDir(filePath)

	cfg, err := config.ParseYamlFile(absPath)
	if err != nil {
		displayWtfConfigFileLoadError(absPath, err)
		os.Exit(1)
	}

	return cfg
}

/* -------------------- Unexported Functions -------------------- */

// chmodConfigFile sets the mode of the config file to r+w for the owner only
func chmodConfigFile() {
	configDir, _ := WtfConfigDir()
	relPath := filepath.Join(configDir, WtfConfigFile)
	absPath, _ := expandHomeDir(relPath)

	_, err := os.Stat(absPath)
	if err != nil && os.IsNotExist(err) {
		return
	}

	err = os.Chmod(absPath, 0600)
	if err != nil {
		return
	}
}

// createWtfConfigDir creates the necessary directories for storing the default config file
// If ~/.config/wtf is missing, it will try to create it
func createWtfConfigDir() {
	wtfConfigDir, _ := WtfConfigDir()

	if _, err := os.Stat(wtfConfigDir); os.IsNotExist(err) {
		err := os.MkdirAll(wtfConfigDir, os.ModePerm)
		if err != nil {
			displayWtfConfigDirCreateError(err)
			os.Exit(1)
		}
	}
}

// createWtfConfigFile creates a simple config file in the config directory if
// one does not already exist
func createWtfConfigFile() {
	filePath, err := CreateFile(WtfConfigFile)
	if err != nil {
		displayDefaultConfigCreateError(err)
		os.Exit(1)
	}

	// If the file is empty, write to it
	file, _ := os.Stat(filePath)

	if file.Size() == 0 {
		if os.WriteFile(filePath, []byte(defaultConfigFile), 0600) != nil {
			displayDefaultConfigWriteError(err)
			os.Exit(1)
		}
	}
}

// Expand expands the path to include the home directory if the path
// is prefixed with `~`. If it isn't prefixed with `~`, the path is
// returned as-is.
func expandHomeDir(path string) (string, error) {
	if path == "" {
		return path, nil
	}

	if path[0] != '~' {
		return path, nil
	}

	if len(path) > 1 && path[1] != '/' && path[1] != '\\' {
		return "", errors.New("cannot expand user-specific home dir")
	}

	dir, err := home()
	if err != nil {
		return "", err
	}

	return filepath.Join(dir, path[1:]), nil
}

// Dir returns the home directory for the executing user.
// An error is returned if a home directory cannot be detected.
func home() (string, error) {
	currentUser, err := user.Current()
	if err != nil {
		return "", err
	}
	if currentUser.HomeDir == "" {
		return "", errors.New("cannot find user-specific home dir")
	}

	return currentUser.HomeDir, nil
}

// migrateOldConfig copies any existing configuration from the old location
// to the new, XDG-compatible location
func migrateOldConfig() {
	srcDir, _ := expandHomeDir(WtfConfigDirV1)
	destDir, _ := WtfConfigDir()

	// If the old config directory doesn't exist, do not move
	if _, err := os.Stat(srcDir); os.IsNotExist(err) {
		return
	}

	// If the new config directory already exists, do not move
	if _, err := os.Stat(destDir); err == nil {
		return
	}

	// Time to move
	err := Copy(srcDir, destDir)
	if err != nil {
		panic(err)
	}

	// Delete the old directory if the new one exists
	if _, err := os.Stat(destDir); err == nil {
		err := os.RemoveAll(srcDir)
		if err != nil {
			fmt.Println(err)
		}
	}
}


================================================
FILE: cfg/copy.go
================================================
// Copied verbatim from:
//
//   https://github.com/otiai10/copy/blob/master/copy.go

package cfg

import (
	"io"
	"os"
	"path/filepath"
)

// Copy copies src to dest, doesn't matter if src is a directory or a file
func Copy(src, dest string) error {
	info, err := os.Stat(src)
	if err != nil {
		return err
	}
	return locationCopy(src, dest, info)
}

// "info" must be given here, NOT nil.
func locationCopy(src, dest string, info os.FileInfo) error {
	if info.IsDir() {
		return directoryCopy(src, dest, info)
	}
	return fileCopy(src, dest, info)
}

func fileCopy(src, dest string, info os.FileInfo) error {

	f, err := os.Create(dest)
	if err != nil {
		return err
	}
	defer func() { _ = f.Close() }()

	if err = os.Chmod(f.Name(), info.Mode()); err != nil {
		return err
	}

	s, err := os.Open(filepath.Clean(src))
	if err != nil {
		return err
	}
	defer func() { _ = f.Close() }()

	_, err = io.Copy(f, s)
	return err
}

func directoryCopy(src, dest string, info os.FileInfo) error {

	if err := os.MkdirAll(dest, info.Mode()); err != nil {
		return err
	}

	entries, err := os.ReadDir(src)
	if err != nil {
		return err
	}

	for _, entry := range entries {
		info, err := entry.Info()
		if err != nil {
			continue
		}

		if err := locationCopy(
			filepath.Join(src, info.Name()),
			filepath.Join(dest, info.Name()),
			info,
		); err != nil {
			return err
		}
	}

	return nil
}


================================================
FILE: cfg/default_color_theme.go
================================================
package cfg

import (
	"github.com/olebedev/config"
	"gopkg.in/yaml.v2"
)

// BorderTheme defines the default color scheme for drawing widget borders
type BorderTheme struct {
	Focusable   string
	Focused     string
	Unfocusable string
}

// CheckboxTheme defines the default color scheme for drawing checkable rows in widgets
type CheckboxTheme struct {
	Checked string
}

// RowTheme defines the default color scheme for row text
type RowTheme struct {
	EvenBackground string
	EvenForeground string

	OddBackground string
	OddForeground string

	HighlightedBackground string
	HighlightedForeground string
}

// TextTheme defines the default color scheme for text rendering
type TextTheme struct {
	Label      string
	Subheading string
	Text       string
	Title      string
}

// WidgetTheme defines the default color scheme for the widget rect itself
type WidgetTheme struct {
	Background string
}

// ColorTheme is an alamgam of all the default color settings
type ColorTheme struct {
	BorderTheme
	CheckboxTheme
	RowTheme
	TextTheme
	WidgetTheme
}

// NewDefaultColorTheme creates and returns an instance of DefaultColorTheme
func NewDefaultColorTheme() ColorTheme {
	defaultTheme := ColorTheme{
		BorderTheme: BorderTheme{
			Focusable:   "blue",
			Focused:     "orange",
			Unfocusable: "gray",
		},

		CheckboxTheme: CheckboxTheme{
			Checked: "gray",
		},

		RowTheme: RowTheme{
			EvenBackground: "transparent",
			EvenForeground: "white",

			OddBackground: "transparent",
			OddForeground: "lightblue",

			HighlightedForeground: "black",
			HighlightedBackground: "green",
		},

		TextTheme: TextTheme{
			Label:      "lightblue",
			Subheading: "red",
			Text:       "white",
			Title:      "green",
		},

		WidgetTheme: WidgetTheme{
			Background: "transparent",
		},
	}

	return defaultTheme
}

// NewDefaultColorConfig creates and returns a config.Config-compatible configuration struct
// using a DefaultColorTheme to pre-populate all the relevant values
func NewDefaultColorConfig() (*config.Config, error) {
	colorTheme := NewDefaultColorTheme()

	yamlBytes, err := yaml.Marshal(colorTheme)
	if err != nil {
		return nil, err
	}

	cfg, err := config.ParseYamlBytes(yamlBytes)
	if err != nil {
		return nil, err
	}

	return cfg, nil
}


================================================
FILE: cfg/default_color_theme_test.go
================================================
package cfg

import (
	"testing"

	"github.com/stretchr/testify/assert"
)

func Test_NewDefaultColorTheme(t *testing.T) {
	theme := NewDefaultColorTheme()

	assert.Equal(t, "orange", theme.Focused)
	assert.Equal(t, "red", theme.Subheading)
	assert.Equal(t, "transparent", theme.Background)
}

func Test_NewDefaultColorConfig(t *testing.T) {
	cfg, err := NewDefaultColorConfig()

	assert.Nil(t, err)

	assert.Equal(t, "orange", cfg.UString("bordertheme.focused"))
	assert.Equal(t, "red", cfg.UString("texttheme.subheading"))
	assert.Equal(t, "transparent", cfg.UString("widgettheme.background"))
	assert.Equal(t, "", cfg.UString("widgettheme.missing"))
}


================================================
FILE: cfg/default_config_file.go
================================================
package cfg

const defaultConfigFile = `wtf:
  colors:
    border:
      focusable: darkslateblue
      focused: orange
      normal: gray
  grid:
    columns: [32, 32, 32, 32, 90]
    rows: [10, 10, 10, 4, 4, 90]
  refreshInterval: 1
  mods:
    clocks_a:
      colors:
        rows:
          even: "lightblue"
          odd: "white"
      enabled: true
      locations:
        Vancouver: "America/Vancouver"
        Toronto: "America/Toronto"
      position:
        top: 0
        left: 1
        height: 1
        width: 1
      refreshInterval: 15
      sort: "alphabetical"
      title: "Clocks A"
      type: "clocks"
    clocks_b:
      colors:
        rows:
          even: "lightblue"
          odd: "white"
      enabled: true
      locations:
        Paris: "Europe/Paris"
        Barcelona: "Europe/Madrid"
        Dubai: "Asia/Dubai"
      position:
        top: 0
        left: 2
        height: 1
        width: 1
      refreshInterval: 15
      sort: "alphabetical"
      title: "Clocks B"
      type: "clocks"
    feedreader:
      enabled: true
      feeds:
      - https://feeds.bbci.co.uk/news/rss.xml
      feedLimit: 10
      position:
        top: 1
        left: 1
        width: 2
        height: 1
      refreshInterval: 14400
    ipinfo:
      colors:
        name: "lightblue"
        value: "white"
      enabled: true
      position:
        top: 2
        left: 1
        height: 1
        width: 1
      refreshInterval: 150
    power:
      enabled: true
      position:
        top: 2
        left: 2
        height: 1
        width: 1
      refreshInterval: 15
      title: "⚡️"
    textfile:
      enabled: true
      filePath: "~/.config/wtf/config.yml"
      format: true
      position:
        top: 0
        left: 0
        height: 4
        width: 1
      refreshInterval: 30
      wrapText: false
    uptime:
      args: []
      cmd: "uptime"
      enabled: true
      position:
        top: 3
        left: 1
        height: 1
        width: 2
      refreshInterval: 30
      type: cmdrunner
`


================================================
FILE: cfg/error_messages.go
================================================
package cfg

// This file contains the error messages that get written to the terminal when
// something goes wrong with the configuration process.
//
// As a general rule, if one of these has to be shown the app should then die
// via os.Exit(1)

import (
	"fmt"

	"github.com/logrusorgru/aurora/v4"
)

/* -------------------- Unexported Functions -------------------- */

func displayError(err error) {
	fmt.Printf("%s %s\n\n", aurora.Red("Error:"), err.Error())
}

func displayDefaultConfigCreateError(err error) {
	fmt.Printf("\n%s Could not create the default configuration file.\n", aurora.Red("ERROR"))
	fmt.Println()
	displayError(err)
}

func displayDefaultConfigWriteError(err error) {
	fmt.Printf("\n%s Could not write the default configuration file.\n", aurora.Red("ERROR"))
	fmt.Println()
	displayError(err)
}

func displayWtfConfigDirCreateError(err error) {
	fmt.Printf("\n%s Could not create the '%s' directory.\n", aurora.Red("ERROR"), aurora.Yellow(WtfConfigDirV2))
	fmt.Println()
	displayError(err)
}

func displayWtfConfigFileLoadError(path string, err error) {
	fmt.Printf("\n%s Could not load '%s'.\n", aurora.Red("ERROR"), aurora.Yellow(path))
	fmt.Println()
	fmt.Println("This could mean one of two things:")
	fmt.Println()
	fmt.Println("    1. That file doesn't exist.")
	fmt.Println("    2. That file has a YAML syntax error. Try running it through http://www.yamllint.com to check for errors.")
	fmt.Println()
	displayError(err)
}


================================================
FILE: cfg/parsers.go
================================================
package cfg

import (
	"fmt"
	"time"

	"github.com/olebedev/config"
)

// ParseAsMapOrList takes a configuration key and attempts to parse it first as a map
// and then as a list. Map entries are concatenated as "key/value"
func ParseAsMapOrList(ymlConfig *config.Config, configKey string) []string {
	result := []string{}

	mapItems, err := ymlConfig.Map(configKey)
	if err == nil {
		for key, value := range mapItems {
			result = append(result, fmt.Sprintf("%s/%s", value, key))
		}
		return result
	}

	listItems := ymlConfig.UList(configKey)
	for _, listItem := range listItems {
		result = append(result, listItem.(string))
	}

	return result
}

// ParseTimeString takes a configuration key and attempts to parse it first as an int
// and then as a duration (int + time unit)
func ParseTimeString(cfg *config.Config, configKey string, defaultValue string) time.Duration {
	i, err := cfg.Int(configKey)
	if err == nil {
		return time.Duration(i) * time.Second
	}

	str := cfg.UString(configKey, defaultValue)
	d, err := time.ParseDuration(str)
	if err == nil {
		return d
	}

	return time.Second
}


================================================
FILE: cfg/parsers_test.go
================================================
package cfg

import (
	"testing"
	"time"

	"github.com/olebedev/config"
)

func Test_ParseAsMapOrList(t *testing.T) {
	tests := []struct {
		name          string
		configKey     string
		yaml          string
		expectedCount int
	}{
		{
			name:          "as empty set",
			configKey:     "data",
			yaml:          "",
			expectedCount: 0,
		},
		{
			name:          "as map",
			configKey:     "data",
			yaml:          "data:\n  a: cat\n  b: dog",
			expectedCount: 2,
		},
		{
			name:          "as list",
			configKey:     "data",
			yaml:          "data:\n  - cat\n  - dog\n  - rat\n",
			expectedCount: 3,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			ymlConfig, err := config.ParseYaml(tt.yaml)
			if err != nil {
				t.Errorf("\nexpected: no error\n     got: %v", err)
			}

			actual := ParseAsMapOrList(ymlConfig, tt.configKey)

			if tt.expectedCount != len(actual) {
				t.Errorf("\nexpected: %d\n     got: %d", tt.expectedCount, len(actual))
			}
		})
	}
}

func Test_ParseTimeString(t *testing.T) {
	tests := []struct {
		name          string
		configKey     string
		yaml          string
		expectedCount time.Duration
	}{
		{
			name:          "normal integer",
			configKey:     "refreshInterval",
			yaml:          "refreshInterval: 3",
			expectedCount: 3 * time.Second,
		},
		{
			name:          "microseconds",
			configKey:     "refreshInterval",
			yaml:          "refreshInterval: 5µs",
			expectedCount: 5 * time.Microsecond,
		},
		{
			name:          "microseconds different notation",
			configKey:     "refreshInterval",
			yaml:          "refreshInterval: 5us",
			expectedCount: 5 * time.Microsecond,
		},
		{
			name:          "mixed duration",
			configKey:     "refreshInterval",
			yaml:          "refreshInterval: 2h45m",
			expectedCount: 2*time.Hour + 45*time.Minute,
		},
		{
			name:          "default",
			configKey:     "refreshInterval",
			yaml:          "",
			expectedCount: 60 * time.Second,
		},
		{
			name:          "bad input",
			configKey:     "refreshInterval",
			yaml:          "refreshInterval: abc",
			expectedCount: 1 * time.Second,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			ymlConfig, err := config.ParseYaml(tt.yaml)
			if err != nil {
				t.Errorf("\nexpected: no error\n     got: %v", err)
			}

			actual := ParseTimeString(ymlConfig, tt.configKey, "60s")

			if tt.expectedCount != actual {
				t.Errorf("\nexpected: %d\n     got: %v", tt.expectedCount, actual)
			}
		})
	}
}


================================================
FILE: cfg/position_settings.go
================================================
package cfg

import (
	"github.com/olebedev/config"
)

const (
	positionPath = "position"
)

// PositionSettings represents the onscreen location of a widget
type PositionSettings struct {
	Validations *Validations

	Height int
	Left   int
	Top    int
	Width  int
}

// NewPositionSettingsFromYAML creates and returns a new instance of cfg.Position
func NewPositionSettingsFromYAML(moduleConfig *config.Config) PositionSettings {
	var currVal int
	var err error

	validations := NewValidations()

	// Parse the positional data from the config data
	currVal, err = moduleConfig.Int(positionPath + ".top")
	validations.append("top", newPositionValidation("top", currVal, err))

	currVal, err = moduleConfig.Int(positionPath + ".left")
	validations.append("left", newPositionValidation("left", currVal, err))

	currVal, err = moduleConfig.Int(positionPath + ".width")
	validations.append("width", newPositionValidation("width", currVal, err))

	currVal, err = moduleConfig.Int(positionPath + ".height")
	validations.append("height", newPositionValidation("height", currVal, err))

	pos := PositionSettings{
		Validations: validations,

		Top:    validations.intValueFor("top"),
		Left:   validations.intValueFor("left"),
		Width:  validations.intValueFor("width"),
		Height: validations.intValueFor("height"),
	}

	return pos
}


================================================
FILE: cfg/position_validation.go
================================================
package cfg

import (
	"fmt"

	"github.com/logrusorgru/aurora/v4"
)

// Common examples of invalid position configuration are:
//
//	position:
//	  top: -3
//	  left: 2
//	  width: 0
//	  height: 1
//
//	position:
//	  top: 3
//	  width: 2
//	  height: 1
//
//	position:
//	  top: 3
//	  # left: 2
//	  width: 2
//	  height: 1
//
//	position:
//	top: 3
//	left: 2
//	width: 2
//	height: 1
type positionValidation struct {
	err    error
	name   string
	intVal int
}

func (posVal *positionValidation) Error() error {
	return posVal.err
}

func (posVal *positionValidation) HasError() bool {
	return posVal.err != nil
}

func (posVal *positionValidation) IntValue() int {
	return posVal.intVal
}

// String returns the Stringer representation of the positionValidation
func (posVal *positionValidation) String() string {
	return fmt.Sprintf("Invalid value for %s:\t%d", aurora.Yellow(posVal.name), posVal.intVal)
}

/* -------------------- Unexported Functions -------------------- */

func newPositionValidation(name string, intVal int, err error) *positionValidation {
	posVal := &positionValidation{
		err:    err,
		name:   name,
		intVal: intVal,
	}

	return posVal
}


================================================
FILE: cfg/position_validation_test.go
================================================
package cfg

import (
	"errors"
	"testing"

	"github.com/stretchr/testify/assert"
)

var (
	posVal = &positionValidation{
		err:    errors.New("Busted"),
		name:   "top",
		intVal: -3,
	}
)

func Test_Attributes(t *testing.T) {
	assert.EqualError(t, posVal.Error(), "Busted")
	assert.Equal(t, true, posVal.HasError())
	assert.Equal(t, -3, posVal.IntValue())

	assert.Contains(t, posVal.String(), "Invalid")
	assert.Contains(t, posVal.String(), "top")
	assert.Contains(t, posVal.String(), "-3")
}


================================================
FILE: cfg/secrets.go
================================================
package cfg

import (
	"errors"
	"fmt"
	"runtime"

	"github.com/docker/docker-credential-helpers/client"
	"github.com/docker/docker-credential-helpers/credentials"
	"github.com/olebedev/config"
	"github.com/wtfutil/wtf/logger"
)

type SecretLoadParams struct {
	name         string
	globalConfig *config.Config
	service      string

	secret *string
}

// Load module secrets.
//
// The credential helpers impose this structure:
//
//	SERVICE is mapped to a SECRET and USERNAME
//
// Only SECRET is secret, SERVICE and USERNAME are not, so this
// API doesn't expose USERNAME.
//
// SERVICE was intended to be the URL of an API server, but
// for hosted services that do not have or need a configurable
// API server, its easier to just use the module name as the
// SERVICE:
//
//	   cfg.ModuleSecret(name, globalConfig, &settings.apiKey).Load()
//
//	The user will use the module name as the service, and the API key as
//	the secret, for example:
//
//	   % wtfutil save-secret circleci
//	   Secret: ...
//
// If a module (such as pihole, jenkins, or github) might have multiple
// instantiations each using a different API service (with its own unique
// API key), then the module should use the API URL to lookup the secret.
// For example, for github:
//
//	cfg.ModuleSecret(name, globalConfig, &settings.apiKey).
//	    Service(settings.baseURL).
//	    Load()
//
// The user will use the API URL as the service, and the API key as the
// secret, for example, with github configured as:
//
//	   -- config.yml
//	   mods:
//	     github:
//	       baseURL: "https://github.mycompany.com/api/v3"
//	       ...
//
//	the secret must be saved as:
//
//	   % wtfutil save-secret https://github.mycompany.com/api/v3
//	   Secret: ...
//
//	If baseURL is not set in the configuration it will be the modules
//	default, and the SERVICE will default to the module name, "github",
//	and the user must save the secret as:
//
//	   % wtfutil save-secret github
//	   Secret: ...
//
//	Ideally, the individual module documentation would describe the
//	SERVICE name to use to save the secret.
func ModuleSecret(name string, globalConfig *config.Config, secret *string) *SecretLoadParams {
	return &SecretLoadParams{
		name:         name,
		globalConfig: globalConfig,
		secret:       secret,
		service:      name, // Default the service to the module name
	}
}

func (slp *SecretLoadParams) Service(service string) *SecretLoadParams {
	if service != "" {
		slp.service = service
	}
	return slp
}

func (slp *SecretLoadParams) Load() {
	configureSecret(
		slp.globalConfig,
		slp.service,
		slp.secret,
	)
}

type Secret struct {
	Service  string
	Secret   string
	Username string
	Store    string
}

func configureSecret(
	globalConfig *config.Config,
	service string,
	secret *string,
) {
	if service == "" {
		return
	}

	if secret == nil {
		return
	}

	// Don't overwrite the secret if it was configured with yaml
	if *secret != "" {
		return
	}

	cred, err := FetchSecret(globalConfig, service)

	if err != nil {
		logger.Log(fmt.Sprintf("Loading secret failed: %s", err.Error()))
		return
	}

	if cred == nil {
		// No secret store configued.
		return
	}

	if secret != nil && *secret == "" {
		*secret = cred.Secret
	}
}

// Fetch secret for `service`. Service is customarily a URL, but can be any
// identifier uniquely used by wtf to identify the service, such as the name
// of the module.  nil is returned if the secretStore global property is not
// present or the secret is not found in that store.
func FetchSecret(globalConfig *config.Config, service string) (*Secret, error) {
	prog := newProgram(globalConfig)

	if prog == nil {
		// No secret store configured.
		return nil, nil
	}

	cred, err := client.Get(prog.runner, service)

	if err != nil {
		return nil, fmt.Errorf("get %v from %v: %w", service, prog.store, err)
	}

	return &Secret{
		Service:  cred.ServerURL,
		Secret:   cred.Secret,
		Username: cred.Username,
		Store:    prog.store,
	}, nil
}

func StoreSecret(globalConfig *config.Config, secret *Secret) error {
	prog := newProgram(globalConfig)

	if prog == nil {
		return errors.New("cannot store secrets: wtf.secretStore is not configured")
	}

	cred := &credentials.Credentials{
		ServerURL: secret.Service,
		Username:  secret.Username,
		Secret:    secret.Secret,
	}

	// docker-credential requires a username, but it isn't necessary for
	// all services. Use a default if a username was not set.
	if cred.Username == "" {
		cred.Username = "default"
	}

	err := client.Store(prog.runner, cred)

	if err != nil {
		return fmt.Errorf("store %v: %w", prog.store, err)
	}

	return nil
}

type program struct {
	store  string
	runner client.ProgramFunc
}

func newProgram(globalConfig *config.Config) *program {
	secretStore := globalConfig.UString("wtf.secretStore", "(none)")

	if secretStore == "(none)" {
		return nil
	}

	if secretStore == "" {
		switch runtime.GOOS {
		case "windows":
			secretStore = "winrt"
		case "darwin":
			secretStore = "osxkeychain"
		default:
			secretStore = "secretservice"
		}

	}

	return &program{
		secretStore,
		client.NewShellProgramFunc("docker-credential-" + secretStore),
	}
}


================================================
FILE: cfg/validatable.go
================================================
package cfg

// Validatable is implemented by any value that validates a configuration setting
type Validatable interface {
	Error() error
	HasError() bool
	String() string
	IntValue() int
}


================================================
FILE: cfg/validations.go
================================================
package cfg

// Validations represent a collection of config setting validations
type Validations struct {
	validations map[string]Validatable
}

// NewValidations creates and returns an instance of Validations
func NewValidations() *Validations {
	vals := &Validations{
		validations: make(map[string]Validatable),
	}

	return vals
}

func (vals *Validations) append(key string, posVal Validatable) {
	vals.validations[key] = posVal
}

func (vals *Validations) intValueFor(key string) int {
	val := vals.validations[key]
	if val != nil {
		return val.IntValue()
	}

	return 0
}


================================================
FILE: cfg/validations_test.go
================================================
package cfg

import (
	"testing"

	"github.com/stretchr/testify/assert"
)

var (
	vals = NewValidations()
)

func Test_intValueFor(t *testing.T) {
	vals.append("left", newPositionValidation("left", 3, nil))

	tests := []struct {
		name     string
		key      string
		expected int
	}{
		{
			name:     "with valid key",
			key:      "left",
			expected: 3,
		},
		{
			name:     "with invalid key",
			key:      "cat",
			expected: 0,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			assert.Equal(t, tt.expected, vals.intValueFor(tt.key))
		})
	}
}


================================================
FILE: checklist/checklist.go
================================================
package checklist

import (
	"time"
)

// Checklist is a module for creating generic checklist implementations
// See 'Todo' for an implementation example
type Checklist struct {
	Items []*ChecklistItem

	checkedIcon   string
	selected      int
	uncheckedIcon string
}

func NewChecklist(checkedIcon, uncheckedIcon string) Checklist {
	list := Checklist{
		checkedIcon:   checkedIcon,
		selected:      -1,
		uncheckedIcon: uncheckedIcon,
	}

	return list
}

/* -------------------- Exported Functions -------------------- */

// Add creates a new checklist item and adds it to the list
// The new one is at the start or end of the list, based on newPos
func (list *Checklist) Add(checked bool, date *time.Time, tags []string, text string, newPos ...string) {
	item := NewChecklistItem(
		checked,
		date,
		tags,
		text,
		list.checkedIcon,
		list.uncheckedIcon,
	)

	if len(newPos) == 0 || newPos[0] == "first" {
		list.Items = append([]*ChecklistItem{item}, list.Items...)
	} else if newPos[0] == "last" {
		list.Items = append(list.Items, []*ChecklistItem{item}...)
	}
}

// CheckedItems returns a slice of all the checked items
func (list *Checklist) CheckedItems() []*ChecklistItem {
	items := []*ChecklistItem{}

	for _, item := range list.Items {
		if item.Checked {
			items = append(items, item)
		}
	}

	return items
}

// Delete removes the selected item from the checklist
func (list *Checklist) Delete(selectedIndex int) {
	if selectedIndex >= 0 && selectedIndex < len(list.Items) {
		list.Items = append(list.Items[:selectedIndex], list.Items[selectedIndex+1:]...)
	}
}

// IsSelectable returns true if the checklist has selectable items, false if it does not
func (list *Checklist) IsSelectable() bool {
	return list.selected >= 0 && list.selected < len(list.Items)
}

// IsUnselectable returns true if the checklist has no selectable items, false if it does
func (list *Checklist) IsUnselectable() bool {
	return !list.IsSelectable()
}

// LongestLine returns the length of the longest checklist item's text
func (list *Checklist) LongestLine() int {
	maxLen := 0

	for _, item := range list.Items {
		if len(item.Text) > maxLen {
			maxLen = len(item.Text)
		}
	}

	return maxLen
}

// IndexByItem returns the index of a giving item if found, otherwise returns 0 with ok set to false
func (list *Checklist) IndexByItem(selectableItem *ChecklistItem) (index int, ok bool) {
	for idx, item := range list.Items {
		if item == selectableItem {
			return idx, true
		}
	}
	return 0, false
}

// UncheckedItems returns a slice of all the unchecked items
func (list *Checklist) UncheckedItems() []*ChecklistItem {
	items := []*ChecklistItem{}

	for _, item := range list.Items {
		if !item.Checked {
			items = append(items, item)
		}
	}

	return items
}

// Unselect removes the current select such that no item is selected
func (list *Checklist) Unselect() {
	list.selected = -1
}

/* -------------------- Sort Interface -------------------- */

func (list *Checklist) Len() int {
	return len(list.Items)
}

func (list *Checklist) Less(i, j int) bool {
	return list.Items[i].Text < list.Items[j].Text
}

func (list *Checklist) Swap(i, j int) {
	list.Items[i], list.Items[j] = list.Items[j], list.Items[i]
}


================================================
FILE: checklist/checklist_item.go
================================================
package checklist

import (
	"fmt"
	"time"
)

// ChecklistItem is a module for creating generic checklist implementations
// See 'Todo' for an implementation example
type ChecklistItem struct {
	Checked       bool
	CheckedIcon   string `yaml:"-"`
	Date          *time.Time
	Tags          []string
	Text          string
	UncheckedIcon string `yaml:"-"`
}

func NewChecklistItem(checked bool, date *time.Time, tags []string, text string, checkedIcon, uncheckedIcon string) *ChecklistItem {
	item := &ChecklistItem{
		Checked:       checked,
		CheckedIcon:   checkedIcon,
		Date:          date,
		Tags:          tags,
		Text:          text,
		UncheckedIcon: uncheckedIcon,
	}

	return item
}

// CheckMark returns the string used to indicate a ChecklistItem is checked or unchecked
func (item *ChecklistItem) CheckMark() string {
	item.ensureItemIcons()

	if item.Checked {
		return item.CheckedIcon
	}

	return item.UncheckedIcon
}

// EditText returns the content of the edit todo form, so includes formatted date and tags
func (item *ChecklistItem) EditText() string {
	datePrefix := ""
	if item.Date != nil {
		datePrefix = fmt.Sprintf("%d-%02d-%02d", item.Date.Year(), item.Date.Month(), item.Date.Day()) + " "
	}

	tagsPrefix := item.TagString()

	return datePrefix + tagsPrefix + item.Text
}

func (item *ChecklistItem) TagString() string {
	if len(item.Tags) == 0 {
		return ""
	}

	s := ""
	for _, tag := range item.Tags {
		s += "#" + tag + " "
	}

	return s
}

// Toggle changes the checked state of the ChecklistItem
// If checked, it is unchecked. If unchecked, it is checked
func (item *ChecklistItem) Toggle() {
	item.Checked = !item.Checked
}

/* -------------------- Unexported Functions -------------------- */

func (item *ChecklistItem) ensureItemIcons() {
	if item.CheckedIcon == "" {
		item.CheckedIcon = "x"
	}

	if item.UncheckedIcon == "" {
		item.UncheckedIcon = " "
	}
}


================================================
FILE: checklist/checklist_item_test.go
================================================
package checklist

import (
	"testing"

	"github.com/stretchr/testify/assert"
)

func testChecklistItem() *ChecklistItem {
	item := NewChecklistItem(
		false,
		nil,
		make([]string, 0),
		"test",
		"",
		"",
	)
	return item
}

func Test_CheckMark(t *testing.T) {
	item := testChecklistItem()
	assert.Equal(t, " ", item.CheckMark())

	item.Toggle()
	assert.Equal(t, "x", item.CheckMark())
}

func Test_Toggle(t *testing.T) {
	item := testChecklistItem()
	assert.Equal(t, false, item.Checked)

	item.Toggle()
	assert.Equal(t, true, item.Checked)

	item.Toggle()
	assert.Equal(t, false, item.Checked)
}


================================================
FILE: checklist/checklist_test.go
================================================
package checklist

import (
	"testing"

	"github.com/stretchr/testify/assert"
)

func Test_NewCheckist(t *testing.T) {
	cl := NewChecklist("o", "-")

	assert.IsType(t, Checklist{}, cl)
	assert.Equal(t, "o", cl.checkedIcon)
	assert.Equal(t, -1, cl.selected)
	assert.Equal(t, "-", cl.uncheckedIcon)
	assert.Equal(t, 0, len(cl.Items))
}

func Test_Add(t *testing.T) {
	cl := NewChecklist("o", "-")
	cl.Add(true, nil, make([]string, 0), "test item")

	assert.Equal(t, 1, len(cl.Items))
}

func Test_CheckedItems(t *testing.T) {
	tests := []struct {
		name        string
		expectedLen int
		checkedLen  int
		before      func(cl *Checklist)
	}{
		{
			name:        "with no items",
			expectedLen: 0,
			checkedLen:  0,
			before:      func(cl *Checklist) {},
		},
		{
			name:        "with no checked items",
			expectedLen: 1,
			checkedLen:  0,
			before: func(cl *Checklist) {
				cl.Add(false, nil, make([]string, 0), "unchecked item")
			},
		},
		{
			name:        "with one checked item",
			expectedLen: 2,
			checkedLen:  1,
			before: func(cl *Checklist) {
				cl.Add(false, nil, make([]string, 0), "unchecked item")
				cl.Add(true, nil, make([]string, 0), "checked item")
			},
		},
		{
			name:        "with multiple checked items",
			expectedLen: 3,
			checkedLen:  2,
			before: func(cl *Checklist) {
				cl.Add(false, nil, make([]string, 0), "unchecked item")
				cl.Add(true, nil, make([]string, 0), "checked item 11")
				cl.Add(true, nil, make([]string, 0), "checked item 2")
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			cl := NewChecklist("o", "-")
			tt.before(&cl)

			assert.Equal(t, tt.expectedLen, len(cl.Items))
			assert.Equal(t, tt.checkedLen, len(cl.CheckedItems()))
		})
	}
}

func Test_Delete(t *testing.T) {
	tests := []struct {
		name        string
		idx         int
		expectedLen int
	}{
		{
			name:        "with valid index",
			idx:         0,
			expectedLen: 0,
		},
		{
			name:        "with invalid index",
			idx:         2,
			expectedLen: 1,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			cl := NewChecklist("o", "-")

			cl.Add(true, nil, make([]string, 0), "test item")
			cl.Delete(tt.idx)

			assert.Equal(t, tt.expectedLen, len(cl.Items))
		})
	}
}

func Test_IsSelectable(t *testing.T) {
	tests := []struct {
		name     string
		selected int
		expected bool
	}{
		{
			name:     "nothing selected",
			selected: -1,
			expected: false,
		},
		{
			name:     "valid selection",
			selected: 1,
			expected: true,
		},
		{
			name:     "invalid selection",
			selected: 3,
			expected: false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			cl := NewChecklist("o", "-")
			cl.Add(true, nil, make([]string, 0), "test item 1")
			cl.Add(false, nil, make([]string, 0), "test item 2")

			cl.selected = tt.selected

			assert.Equal(t, tt.expected, cl.IsSelectable())
		})
	}
}

func Test_IsUnselectable(t *testing.T) {
	tests := []struct {
		name     string
		selected int
		expected bool
	}{
		{
			name:     "nothing selected",
			selected: -1,
			expected: true,
		},
		{
			name:     "valid selection",
			selected: 1,
			expected: false,
		},
		{
			name:     "invalid selection",
			selected: 3,
			expected: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			cl := NewChecklist("o", "-")
			cl.Add(true, nil, make([]string, 0), "test item 1")
			cl.Add(false, nil, make([]string, 0), "test item 2")

			cl.selected = tt.selected

			assert.Equal(t, tt.expected, cl.IsUnselectable())
		})
	}
}

func Test_LongestLine(t *testing.T) {
	tests := []struct {
		name        string
		expectedLen int
		before      func(cl *Checklist)
	}{
		{
			name:        "with no items",
			expectedLen: 0,
			before:      func(cl *Checklist) {},
		},
		{
			name:        "with different-length items",
			expectedLen: 12,
			before: func(cl *Checklist) {
				cl.Add(true, nil, make([]string, 0), "test item 1")
				cl.Add(false, nil, make([]string, 0), "test item 22")
			},
		},
		{
			name:        "with same-length items",
			expectedLen: 11,
			before: func(cl *Checklist) {
				cl.Add(true, nil, make([]string, 0), "test item 1")
				cl.Add(false, nil, make([]string, 0), "test item 2")
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			cl := NewChecklist("o", "-")
			tt.before(&cl)

			assert.Equal(t, tt.expectedLen, cl.LongestLine())
		})
	}
}

func Test_IndexByItem(t *testing.T) {
	cl := NewChecklist("o", "-")
	cl.Add(false, nil, make([]string, 0), "unchecked item")
	cl.Add(true, nil, make([]string, 0), "checked item")

	tests := []struct {
		name        string
		item        *ChecklistItem
		expectedIdx int
		expectedOk  bool
	}{
		{
			name:        "with nil",
			item:        nil,
			expectedIdx: 0,
			expectedOk:  false,
		},
		{
			name:        "with valid item",
			item:        cl.Items[1],
			expectedIdx: 1,
			expectedOk:  true,
		},
		{
			name:        "with valid item",
			item:        NewChecklistItem(false, nil, make([]string, 0), "invalid", "x", " "),
			expectedIdx: 0,
			expectedOk:  false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {

			idx, ok := cl.IndexByItem(tt.item)

			assert.Equal(t, tt.expectedIdx, idx)
			assert.Equal(t, tt.expectedOk, ok)
		})
	}
}

func Test_UncheckedItems(t *testing.T) {
	tests := []struct {
		name        string
		expectedLen int
		checkedLen  int
		before      func(cl *Checklist)
	}{
		{
			name:        "with no items",
			expectedLen: 0,
			checkedLen:  0,
			before:      func(cl *Checklist) {},
		},
		{
			name:        "with no unchecked items",
			expectedLen: 1,
			checkedLen:  0,
			before: func(cl *Checklist) {
				cl.Add(true, nil, make([]string, 0), "unchecked item")
			},
		},
		{
			name:        "with one unchecked item",
			expectedLen: 2,
			checkedLen:  1,
			before: func(cl *Checklist) {
				cl.Add(false, nil, make([]string, 0), "unchecked item")
				cl.Add(true, nil, make([]string, 0), "checked item")
			},
		},
		{
			name:        "with multiple unchecked items",
			expectedLen: 3,
			checkedLen:  2,
			before: func(cl *Checklist) {
				cl.Add(false, nil, make([]string, 0), "unchecked item")
				cl.Add(true, nil, make([]string, 0), "checked item 11")
				cl.Add(false, nil, make([]string, 0), "checked item 2")
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			cl := NewChecklist("o", "-")
			tt.before(&cl)

			assert.Equal(t, tt.expectedLen, len(cl.Items))
			assert.Equal(t, tt.checkedLen, len(cl.UncheckedItems()))
		})
	}
}

func Test_Unselect(t *testing.T) {
	cl := NewChecklist("o", "-")
	cl.Add(false, nil, make([]string, 0), "unchecked item")

	cl.selected = 0
	assert.Equal(t, 0, cl.selected)

	cl.Unselect()
	assert.Equal(t, -1, cl.selected)
}

/* -------------------- Sort Interface -------------------- */

func Test_Len(t *testing.T) {
	tests := []struct {
		name        string
		expectedLen int
		before      func(cl *Checklist)
	}{
		{
			name:        "with no items",
			expectedLen: 0,
			before:      func(cl *Checklist) {},
		},
		{
			name:        "with one item",
			expectedLen: 1,
			before: func(cl *Checklist) {
				cl.Add(false, nil, make([]string, 0), "unchecked item")
			},
		},
		{
			name:        "with multiple items",
			expectedLen: 3,
			before: func(cl *Checklist) {
				cl.Add(false, nil, make([]string, 0), "unchecked item")
				cl.Add(true, nil, make([]string, 0), "checked item 1")
				cl.Add(false, nil, make([]string, 0), "checked item 2")
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			cl := NewChecklist("o", "-")
			tt.before(&cl)

			assert.Equal(t, tt.expectedLen, cl.Len())
		})
	}
}

func Test_Less(t *testing.T) {
	tests := []struct {
		name     string
		first    string
		second   string
		expected bool
	}{
		{
			name:     "same",
			first:    "",
			second:   "",
			expected: false,
		},
		{
			name:     "last less",
			first:    "beta",
			second:   "alpha",
			expected: true,
		},
		{
			name:     "first less",
			first:    "alpha",
			second:   "beta",
			expected: false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			cl := NewChecklist("o", "-")
			cl.Add(false, nil, make([]string, 0), tt.first)
			cl.Add(false, nil, make([]string, 0), tt.second)

			assert.Equal(t, tt.expected, cl.Less(0, 1))
		})
	}
}

func Test_Swap(t *testing.T) {
	tests := []struct {
		name     string
		first    string
		second   string
		expected bool
	}{
		{
			name:   "same",
			first:  "",
			second: "",
		},
		{
			name:   "last less",
			first:  "alpha",
			second: "beta",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			cl := NewChecklist("o", "-")
			cl.Add(false, nil, make([]string, 0), tt.first)
			cl.Add(false, nil, make([]string, 0), tt.second)

			cl.Swap(0, 1)

			assert.Equal(t, tt.expected, cl.Items[0].Text == "beta")
			assert.Equal(t, tt.expected, cl.Items[1].Text == "alpha")
		})
	}
}


================================================
FILE: flags/flags.go
================================================
package flags

import (
	"fmt"
	"os"
	"path/filepath"
	"runtime/debug"
	"strings"

	"github.com/chzyer/readline"
	goFlags "github.com/jessevdk/go-flags"
	"github.com/olebedev/config"
	"github.com/wtfutil/wtf/cfg"
	"github.com/wtfutil/wtf/help"
)

// Flags is the container for command line flag data
type Flags struct {
	Config  string `short:"c" long:"config" optional:"no" description:"Path to config file"`
	Module  string `short:"m" long:"module" optional:"no" description:"Display info about a specific module, i.e.: 'wtfutil -m=todo'"`
	Profile bool   `short:"p" long:"profile" optional:"yes" description:"Profile application memory usage"`
	Version bool   `short:"v" long:"version" description:"Show version info"`
	// Work-around go-flags misfeatures. If any sub-command is defined
	// then `wtf` (no sub-commands, the common usage), is warned about.
	Opt struct {
		Cmd  string   `positional-arg-name:"command"`
		Args []string `positional-arg-name:"args"`
	} `positional-args:"yes"`

	hasCustom bool
}

var EXTRA = `
Commands:
  save-secret <service>
    service      Service URL or module name of secret.
  Save a secret into the secret store. The secret will be prompted for.
  Requires wtf.secretStore to be configured.  See individual modules for
  information on what service and secret means for their configuration,
  not all modules use secrets.
`

// NewFlags creates an instance of Flags
func NewFlags() *Flags {
	flags := Flags{}
	return &flags
}

/* -------------------- Exported Functions -------------------- */

// ConfigFilePath returns the path to the currently-loaded config file
func (flags *Flags) ConfigFilePath() string {
	return flags.Config
}

// RenderIf displays special-case information based on the flags passed
// in, if any flags were passed in
func (flags *Flags) RenderIf(config *config.Config) {
	if flags.HasModule() {
		help.Display(flags.Module, config)
		os.Exit(0)
	}

	if flags.HasVersion() {

		info, ok := debug.ReadBuildInfo()
		if !ok {
			os.Exit(1)
			return
		}

		var official bool
		var revision string
		version := info.Main.Version
		date := "unknown"

		// Check if this binary was built with git. If so, extract details.
		for _, setting := range info.Settings {
			switch setting.Key {
			case "vcs.revision":
				revision = setting.Value[0:12] // only need the 12 char hash
			case "vcs.time":
				date = setting.Value
			}
		}

		// if we're built with git...
		if revision != "" {
			if !strings.Contains(version, revision) {
				official = true
			}
		}

		if official {
			fmt.Printf("WTF %s (built: %s)\n", version, date)
		} else {
			fmt.Printf("WTF %s\nNote: This is an unofficial release.\n", version)
		}

		os.Exit(0)
	}

	if flags.Opt.Cmd == "" {
		return
	}

	switch cmd := flags.Opt.Cmd; cmd {
	case "save-secret":
		var service, secret string
		args := flags.Opt.Args

		if len(args) < 1 || args[0] == "" {
			fmt.Fprintf(os.Stderr, "save-secret: service required, see `%s --help`\n", os.Args[0])
			os.Exit(1)
		}

		service = args[0]

		if len(args) > 1 {
			fmt.Fprintf(os.Stderr, "save-secret: too many arguments, see `%s --help`\n", os.Args[0])
			os.Exit(1)
		}

		b, err := readline.Password("Secret: ")
		if err != nil {
			fmt.Fprintf(os.Stderr, "Error: %v\n", err)
			os.Exit(1)
		}
		secret = string(b)
		secret = strings.TrimSpace(secret)

		if secret == "" {
			fmt.Fprintf(os.Stderr, "save-secret: secret required, see `%s --help`\n", os.Args[0])
			os.Exit(1)
		}

		err = cfg.StoreSecret(config, &cfg.Secret{
			Service:  service,
			Secret:   secret,
			Username: "default",
		})

		if err != nil {
			fmt.Fprintf(os.Stderr, "Saving secret for service %q: %s\n", service, err.Error())
			os.Exit(1)
		}

		fmt.Printf("Saved secret for service %q\n", service)
		os.Exit(0)
	default:
		fmt.Fprintf(os.Stderr, "Command `%s` is not supported, try `%s --help`\n", cmd, os.Args[0])
		os.Exit(1)
	}
}

// HasCustomConfig returns TRUE if a config path was passed in, FALSE if one was not
func (flags *Flags) HasCustomConfig() bool {
	return flags.hasCustom
}

// HasModule returns TRUE if a module name was passed in, FALSE if one was not
func (flags *Flags) HasModule() bool {
	return len(flags.Module) > 0
}

// HasVersion returns TRUE if the version flag was passed in, FALSE if it was not
func (flags *Flags) HasVersion() bool {
	return flags.Version
}

// Parse parses the incoming flags
func (flags *Flags) Parse() {
	parser := goFlags.NewParser(flags, goFlags.Default)
	if _, err := parser.Parse(); err != nil {
		if flagsErr, ok := err.(*goFlags.Error); ok && flagsErr.Type == goFlags.ErrHelp {
			fmt.Println(EXTRA)
			os.Exit(0)
		}
	}

	// If we have a custom config, then we're done parsing parameters, we don't need to
	// generate the default value
	flags.hasCustom = (len(flags.Config) > 0)
	if flags.hasCustom {
		return
	}

	// If no config file is explicitly passed in as a param
	// then try the `WTF_CONFIG` environment variable
	// then fallback to the default config file to define the default flag value
	configDir, err := cfg.WtfConfigDir()
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		os.Exit(1)
	}
	envCfg := os.Getenv("WTF_CONFIG")
	if envCfg == "" {
		flags.Config = filepath.Join(configDir, "config.yml")
	} else {
		flags.Config = envCfg
	}
}


================================================
FILE: generator/settings.tpl
================================================
package {{(Lower .Name)}}

import (
	"github.com/olebedev/config"
	"github.com/wtfutil/wtf/cfg"
)

const (
	defaultFocusable = false
	defaultTitle     = "{{(.Name)}}"
)

// Settings defines the configuration properties for this module
type Settings struct {
	common *cfg.Common

    // Define your settings attributes here
}

// NewSettingsFromYAML creates a new settings instance from a YAML config block
func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
	settings := Settings{
        common: cfg.NewCommonSettingsFromModule(name, defaultTitle, defaultFocusable, ymlConfig, globalConfig),

        // Configure your settings attributes here. See http://github.com/olebedev/config for type details
	}

	return &settings
}

================================================
FILE: generator/textwidget.go
================================================
//go:build ignore

// This package takes care of generates for empty widgets. Each generator is named after the
// type of widget it generate, so textwidget.go will generate the skeleton for a new TextWidget
// using the textwidget.tpl template.
// The TextWidget generator needs one environment variable, called WTF_WIDGET_NAME, which will
// be the name of the TextWidget it generates. If the variable hasn't been set, the generator
// will use "NewTextWidget". On Linux and macOS the command can be run as
// 'WTF_WIDGET_NAME=MyNewWidget go generate -run=text'.
package main

import (
	"fmt"
	"os"
	"strings"
	"text/template"
)

const (
	defaultWidgetName = "NewTextWidget"
	widgetMaker       = "app/widget_maker.go"
)

func main() {
	widgetName, present := os.LookupEnv("WTF_WIDGET_NAME")
	if !present {
		widgetName = defaultWidgetName
	}

	data := struct {
		Name string
	}{
		widgetName,
	}

	createModuleDirectory(data)

	generateWidgetFile(data)
	generateSettingsFile(data)
	fmt.Println("Don't forget to register your module in file", widgetMaker)
}

/* -------------------- Unexported Functions -------------------- */

func createModuleDirectory(data struct{ Name string }) {
	err := os.MkdirAll(strings.ToLower(fmt.Sprintf("modules/%s", data.Name)), os.ModePerm)
	if err != nil {
		fmt.Println(err.Error())
	}
}

func generateWidgetFile(data struct{ Name string }) {
	tpl, _ := template.New("textwidget.tpl").Funcs(template.FuncMap{
		"Lower": strings.ToLower,
	}).ParseFiles("generator/textwidget.tpl")

	out, err := os.Create(fmt.Sprintf("modules/%s/widget.go", strings.ToLower(data.Name)))
	if err != nil {
		fmt.Println(err.Error())
	}
	defer out.Close()

	tpl.Execute(out, data)
}

func generateSettingsFile(data struct{ Name string }) {
	tpl, _ := template.New("settings.tpl").Funcs(template.FuncMap{
		"Lower": strings.ToLower,
	}).ParseFiles("generator/settings.tpl")

	out, err := os.Create(fmt.Sprintf("modules/%s/settings.go", strings.ToLower(data.Name)))
	if err != nil {
		fmt.Println(err.Error())
	}
	defer out.Close()

	tpl.Execute(out, data)
}


================================================
FILE: generator/textwidget.tpl
================================================
package {{(Lower .Name)}}

import (
	"github.com/rivo/tview"
	"github.com/wtfutil/wtf/view"
)

// Widget is the container for your module's data
type Widget struct {
	view.TextWidget

	settings *Settings
}

// NewWidget creates and returns an instance of Widget
func NewWidget(tviewApp *tview.Application, redrawChan chan bool, pages *tview.Pages, settings *Settings) *Widget {
	widget := Widget{
		TextWidget: view.NewTextWidget(tviewApp, redrawChan, pages, settings.common),

		settings: settings,
	}

	return &widget
}

/* -------------------- Exported Functions -------------------- */

// Refresh updates the onscreen contents of the widget
func (widget *Widget) Refresh() {

    // The last call should always be to the display function
    widget.display()
}

/* -------------------- Unexported Functions -------------------- */

func (widget *Widget) content() string {
	return "This is my widget"
}

func (widget *Widget) display() {
	widget.Redraw(func() (string, string, bool) {
		return widget.CommonSettings().Title, widget.content(), false
	})
}


================================================
FILE: go.mod
================================================
module github.com/wtfutil/wtf

go 1.26.1

require (
	bitbucket.org/mikehouston/asana-go v0.0.0-20201102222432-715318d0343a
	code.cloudfoundry.org/bytefmt v0.66.0
	github.com/Microsoft/go-winio v0.6.2 // indirect
	github.com/PagerDuty/go-pagerduty v1.8.0
	github.com/VictorAvelar/devto-api-go v1.0.0
	github.com/adlio/trello v1.12.0
	github.com/alecthomas/chroma v0.10.0
	github.com/andygrunwald/go-gerrit v1.1.1
	github.com/briandowns/openweathermap v0.21.1
	github.com/cenkalti/backoff v2.2.1+incompatible // indirect
	github.com/chzyer/readline v1.5.1
	github.com/creack/pty v1.1.24
	github.com/digitalocean/godo v1.175.0
	github.com/docker/docker v28.5.2+incompatible
	github.com/docker/docker-credential-helpers v0.9.5
	github.com/docker/go-connections v0.5.0 // indirect
	github.com/docker/go-units v0.5.0 // indirect
	github.com/dustin/go-humanize v1.0.1
	github.com/gdamore/tcell v1.4.1
	github.com/go-ole/go-ole v1.2.6 // indirect
	github.com/godbus/dbus v4.1.0+incompatible // indirect
	github.com/google/go-github/v32 v32.1.0
	github.com/jedib0t/go-pretty/v6 v6.7.8
	github.com/jessevdk/go-flags v1.6.1
	github.com/microsoft/azure-devops-go-api/azuredevops v1.0.0-b5
	github.com/mmcdole/gofeed v1.3.0
	github.com/olebedev/config v0.0.0-20190528211619-364964f3a8e4
	github.com/olekukonko/tablewriter v0.0.5
	github.com/ovh/cds v0.55.1
	github.com/pborman/uuid v1.2.0 // indirect
	github.com/piquette/finance-go v1.1.0
	github.com/pkg/errors v0.9.1
	github.com/pkg/profile v1.7.0
	github.com/radovskyb/watcher v1.0.7
	github.com/rivo/tview v0.42.0
	github.com/shirou/gopsutil v2.21.11+incompatible
	github.com/shopspring/decimal v1.3.1 // indirect
	github.com/shurcooL/githubv4 v0.0.0-20200802174311-f27d2ca7f6d5
	github.com/shurcooL/graphql v0.0.0-20181231061246-d48a9a75455f // indirect
	github.com/spf13/cobra v1.10.2 // indirect
	github.com/spf13/pflag v1.0.10 // indirect
	github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
	github.com/stretchr/testify v1.11.1
	github.com/wtfutil/spotigopher v0.0.0-20191127141047-7d8168fe103a
	github.com/zmb3/spotify v1.3.0
	github.com/zorkian/go-datadog-api v2.30.0+incompatible
	golang.org/x/oauth2 v0.35.0
	golang.org/x/sync v0.20.0
	golang.org/x/sys v0.42.0 // indirect
	golang.org/x/text v0.34.0
	google.golang.org/api v0.266.0
	gopkg.in/yaml.v2 v2.4.0
	gotest.tools v2.2.0+incompatible
	jaytaylor.com/html2text v0.0.0-20200412013138-3577fbdbcff7
	k8s.io/apimachinery v0.35.2
	k8s.io/client-go v0.35.2
)

require github.com/nicklaw5/helix/v2 v2.32.0

require (
	github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0
	github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1
	github.com/Azure/azure-sdk-for-go/sdk/monitor/azquery v1.2.0
	github.com/charmbracelet/bubbles v0.21.1
	github.com/gdamore/tcell/v2 v2.13.8
	github.com/gopherlibs/todoist v0.1.0
	github.com/hekmon/transmissionrpc/v2 v2.0.1
	github.com/logrusorgru/aurora/v4 v4.0.0
	github.com/muesli/reflow v0.3.0
	github.com/prometheus-community/pro-bing v0.8.0
	gitlab.com/gitlab-org/api/client-go v0.160.1
	gopkg.in/yaml.v3 v3.0.1
)

require (
	cloud.google.com/go/auth v0.18.1 // indirect
	cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
	cloud.google.com/go/compute/metadata v0.9.0 // indirect
	contrib.go.opencensus.io/exporter/jaeger v0.2.1 // indirect
	contrib.go.opencensus.io/exporter/prometheus v0.4.2 // indirect
	dario.cat/mergo v1.0.0 // indirect
	github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 // indirect
	github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 // indirect
	github.com/CycloneDX/cyclonedx-go v0.8.0 // indirect
	github.com/ProtonMail/go-crypto v1.1.6 // indirect
	github.com/PuerkitoBio/goquery v1.8.0 // indirect
	github.com/RackSec/srslog v0.0.0-20180709174129-a4725f04ec91 // indirect
	github.com/andybalholm/brotli v1.1.0 // indirect
	github.com/andybalholm/cascadia v1.3.1 // indirect
	github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230321174746-8dcc6526cfb1 // indirect
	github.com/aokoli/goutils v1.1.1 // indirect
	github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
	github.com/beorn7/perks v1.0.1 // indirect
	github.com/blang/semver v3.5.1+incompatible // indirect
	github.com/cespare/xxhash/v2 v2.3.0 // indirect
	github.com/charmbracelet/bubbletea v1.3.10 // indirect
	github.com/charmbracelet/colorprofile v0.4.1 // indirect
	github.com/charmbracelet/harmonica v0.2.0 // indirect
	github.com/charmbracelet/lipgloss v1.1.0 // indirect
	github.com/charmbracelet/x/ansi v0.11.5 // indirect
	github.com/charmbracelet/x/cellbuf v0.0.15 // indirect
	github.com/charmbracelet/x/term v0.2.2 // indirect
	github.com/clipperhouse/displaywidth v0.9.0 // indirect
	github.com/clipperhouse/stringish v0.1.1 // indirect
	github.com/clipperhouse/uax29/v2 v2.5.0 // indirect
	github.com/cloudflare/circl v1.6.1 // indirect
	github.com/containerd/errdefs v1.0.0 // indirect
	github.com/containerd/errdefs/pkg v0.3.0 // indirect
	github.com/cyphar/filepath-securejoin v0.4.1 // indirect
	github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
	github.com/distribution/reference v0.6.0 // indirect
	github.com/dlclark/regexp2 v1.4.0 // indirect
	github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect
	github.com/eapache/go-resiliency v1.3.0 // indirect
	github.com/emicklei/go-restful/v3 v3.12.2 // indirect
	github.com/emirpasic/gods v1.18.1 // indirect
	github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
	github.com/fatih/color v1.16.0 // indirect
	github.com/felixge/fgprof v0.9.5 // indirect
	github.com/felixge/httpsnoop v1.0.4 // indirect
	github.com/forPelevin/gomoji v1.1.8 // indirect
	github.com/fsamin/go-dump v1.0.9 // indirect
	github.com/fxamacker/cbor/v2 v2.9.0 // indirect
	github.com/gdamore/encoding v1.0.1 // indirect
	github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
	github.com/go-git/go-billy/v5 v5.6.2 // indirect
	github.com/go-git/go-git/v5 v5.16.5 // indirect
	github.com/go-gorp/gorp v2.0.0+incompatible // indirect
	github.com/go-kit/log v0.2.1 // indirect
	github.com/go-logfmt/logfmt v0.5.1 // indirect
	github.com/go-logr/logr v1.4.3 // indirect
	github.com/go-logr/stdr v1.2.2 // indirect
	github.com/go-openapi/jsonpointer v0.21.0 // indirect
	github.com/go-openapi/jsonreference v0.20.2 // indirect
	github.com/go-openapi/swag v0.23.0 // indirect
	github.com/go-sql-driver/mysql v1.9.3 // indirect
	github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
	github.com/golang-jwt/jwt/v4 v4.5.2 // indirect
	github.com/golang-jwt/jwt/v5 v5.3.0 // indirect
	github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
	github.com/golang/protobuf v1.5.4 // indirect
	github.com/golang/snappy v0.0.4 // indirect
	github.com/google/gnostic-models v0.7.0 // indirect
	github.com/google/go-cmp v0.7.0 // indirect
	github.com/google/go-querystring v1.1.0 // indirect
	github.com/google/pprof v0.0.0-20260302011040-a15ffb7f9dcc // indirect
	github.com/google/s2a-go v0.1.9 // indirect
	github.com/google/uuid v1.6.0 // indirect
	github.com/googleapis/enterprise-certificate-proxy v0.3.11 // indirect
	github.com/googleapis/gax-go/v2 v2.17.0 // indirect
	github.com/gookit/color v1.5.4 // indirect
	github.com/gorhill/cronexpr v0.0.0-20161205141322-d520615e531a // indirect
	github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect
	github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
	github.com/hashicorp/go-retryablehttp v0.7.8 // indirect
	github.com/hekmon/cunits/v2 v2.1.0 // indirect
	github.com/huandu/xstrings v1.4.0 // indirect
	github.com/iancoleman/orderedmap v0.3.0 // indirect
	github.com/imdario/mergo v0.3.13 // indirect
	github.com/inconshreveable/mousetrap v1.1.0 // indirect
	github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
	github.com/jfrog/archiver/v3 v3.6.0 // indirect
	github.com/jfrog/build-info-go v1.9.23 // indirect
	github.com/jfrog/gofrog v1.6.0 // indirect
	github.com/jfrog/jfrog-client-go v1.37.1 // indirect
	github.com/josharian/intern v1.0.0 // indirect
	github.com/json-iterator/go v1.1.12 // indirect
	github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
	github.com/kevinburke/ssh_config v1.2.0 // indirect
	github.com/klauspost/compress v1.17.4 // indirect
	github.com/klauspost/cpuid/v2 v2.2.3 // indirect
	github.com/klauspost/pgzip v1.2.6 // indirect
	github.com/kylelemons/godebug v1.1.0 // indirect
	github.com/lucasb-eyer/go-colorful v1.3.0 // indirect
	github.com/mailru/easyjson v0.7.7 // indirect
	github.com/maruel/panicparse/v2 v2.2.2 // indirect
	github.com/mattn/go-colorable v0.1.13 // indirect
	github.com/mattn/go-isatty v0.0.20 // indirect
	github.com/mattn/go-localereader v0.0.1 // indirect
	github.com/mattn/go-runewidth v0.0.19 // indirect
	github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
	github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
	github.com/minio/sha256-simd v1.0.1 // indirect
	github.com/mitchellh/hashstructure v0.0.0-20170609045927-2bca23e0e452 // indirect
	github.com/mitchellh/mapstructure v1.5.0 // indirect
	github.com/mmcdole/goxpp v1.1.1-0.20240225020742-a0c311522b23 // indirect
	github.com/moby/docker-image-spec v1.3.1 // indirect
	github.com/moby/sys/atomicwriter v0.1.0 // indirect
	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
	github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
	github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
	github.com/muesli/cancelreader v0.2.2 // indirect
	github.com/muesli/termenv v0.16.0 // indirect
	github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
	github.com/nwaples/rardecode v1.1.3 // indirect
	github.com/onsi/ginkgo v1.16.5 // indirect
	github.com/opencontainers/go-digest v1.0.0 // indirect
	github.com/opencontainers/image-spec v1.1.0-rc6 // indirect
	github.com/ovh/cds/sdk/interpolate v0.0.0-20190319104452-71125b036b25 // indirect
	github.com/pierrec/lz4/v4 v4.1.21 // indirect
	github.com/pjbgf/sha1cd v0.3.2 // indirect
	github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
	github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
	github.com/prometheus/client_golang v1.16.0 // indirect
	github.com/prometheus/client_model v0.4.0 // indirect
	github.com/prometheus/common v0.44.0 // indirect
	github.com/prometheus/procfs v0.10.1 // indirect
	github.com/prometheus/statsd_exporter v0.22.7 // indirect
	github.com/rivo/uniseg v0.4.7 // indirect
	github.com/rockbears/log v0.11.2 // indirect
	github.com/rockbears/yaml v0.4.0 // indirect
	github.com/rs/xid v1.2.1 // indirect
	github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
	github.com/sguiheux/go-coverage v0.0.0-20190710153556-287b082a7197 // indirect
	github.com/sguiheux/jsonschema v0.0.0-20240314085137-97ecc280683c // indirect
	github.com/sirupsen/logrus v1.9.3 // indirect
	github.com/skeema/knownhosts v1.3.1 // indirect
	github.com/spf13/afero v1.11.0 // indirect
	github.com/spf13/cast v1.6.0 // indirect
	github.com/tklauser/go-sysconf v0.3.10 // indirect
	github.com/tklauser/numcpus v0.4.0 // indirect
	github.com/uber/jaeger-client-go v2.25.0+incompatible // indirect
	github.com/ulikunitz/xz v0.5.11 // indirect
	github.com/x448/float16 v0.8.4 // indirect
	github.com/xanzy/ssh-agent v0.3.3 // indirect
	github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
	github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
	github.com/xeipuuv/gojsonschema v1.2.0 // indirect
	github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
	github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
	github.com/yusufpapurcu/wmi v1.2.2 // indirect
	go.opencensus.io v0.24.0 // indirect
	go.opentelemetry.io/auto/sdk v1.2.1 // indirect
	go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
	go.opentelemetry.io/otel v1.39.0 // indirect
	go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.36.0 // indirect
	go.opentelemetry.io/otel/metric v1.39.0 // indirect
	go.opentelemetry.io/otel/trace v1.39.0 // indirect
	go.uber.org/multierr v1.11.0 // indirect
	go.uber.org/zap v1.27.0 // indirect
	go.yaml.in/yaml/v2 v2.4.3 // indirect
	go.yaml.in/yaml/v3 v3.0.4 // indirect
	golang.org/x/crypto v0.48.0 // indirect
	golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
	golang.org/x/net v0.51.0 // indirect
	golang.org/x/term v0.40.0 // indirect
	golang.org/x/time v0.14.0 // indirect
	google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20 // indirect
	google.golang.org/grpc v1.78.0 // indirect
	google.golang.org/protobuf v1.36.11 // indirect
	gopkg.in/AlecAivazis/survey.v1 v1.7.1 // indirect
	gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect
	gopkg.in/inf.v0 v0.9.1 // indirect
	gopkg.in/warnings.v0 v0.1.2 // indirect
	gotest.tools/v3 v3.3.0 // indirect
	k8s.io/api v0.35.2 // indirect
	k8s.io/klog/v2 v2.130.1 // indirect
	k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 // indirect
	k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 // indirect
	sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect
	sigs.k8s.io/randfill v1.0.0 // indirect
	sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect
	sigs.k8s.io/yaml v1.6.0 // indirect
)


================================================
FILE: go.sum
================================================
bitbucket.org/mikehouston/asana-go v0.0.0-20201102222432-715318d0343a h1:qH51iOpTres3x2kNb0f2R3ggMpbYCyCvaRrsvdndhvY=
bitbucket.org/mikehouston/asana-go v0.0.0-20201102222432-715318d0343a/go.mod h1:HcP4iCG6i6uVAyX2X7yKOsjbzLFiTfX0EMT20CYn5Ig=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
cloud.google.com/go/auth v0.18.1 h1:IwTEx92GFUo2pJ6Qea0EU3zYvKnTAeRCODxfA/G5UWs=
cloud.google.com/go/auth v0.18.1/go.mod h1:GfTYoS9G3CWpRA3Va9doKN9mjPGRS+v41jmZAhBzbrA=
cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc=
cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs=
cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
code.cloudfoundry.org/bytefmt v0.66.0 h1:tSK2uf1Shxb5SIc7W9RE+FSKnwuFwzoEqkpwwVQx0SM=
code.cloudfoundry.org/bytefmt v0.66.0/go.mod h1:JrBuqsb9cQeqrVSdWvcDLjnxD6RXfAev2s8nwjskhhs=
contrib.go.opencensus.io/exporter/jaeger v0.2.1 h1:yGBYzYMewVL0yO9qqJv3Z5+IRhPdU7e9o/2oKpX4YvI=
contrib.go.opencensus.io/exporter/jaeger v0.2.1/go.mod h1:Y8IsLgdxqh1QxYxPC5IgXVmBaeLUeQFfBeBi9PbeZd0=
contrib.go.opencensus.io/exporter/prometheus v0.4.2 h1:sqfsYl5GIY/L570iT+l93ehxaWJs2/OwXtiWwew3oAg=
contrib.go.opencensus.io/exporter/prometheus v0.4.2/go.mod h1:dvEHbiKmgvbr5pjaF9fpw1KeYcjrnC1J8B+JKjsZyRQ=
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0 h1:fou+2+WFTib47nS+nz/ozhEBnvU96bKHy6LjRsY4E28=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0/go.mod h1:t76Ruy8AHvUAC8GfMWJMa0ElSbuIcO03NLpynfbgsPA=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 h1:Hk5QBxZQC1jb2Fwj6mpzme37xbCDdNTxU7O9eb5+LB4=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1/go.mod h1:IYus9qsFobWIc2YVwe/WPjcnyCkPKtnHAqUYeebc8z0=
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY=
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 h1:9iefClla7iYpfYWdzPCRDozdmndjTm8DXdpCzPajMgA=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2/go.mod h1:XtLgD3ZD34DAaVIIAyG3objl5DynM3CQ/vMcbBNJZGI=
github.com/Azure/azure-sdk-for-go/sdk/monitor/azquery v1.2.0 h1:s0SaQtHigowP0n3Kx4ieV94pNZAHlHhS+xjZyLCSVCQ=
github.com/Azure/azure-sdk-for-go/sdk/monitor/azquery v1.2.0/go.mod h1:oI5SPI1vpNJYfP9MPWXthq7jDfh9xTAuQVBKPOu7DPo=
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM=
github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE=
github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 h1:XRzhVemXdgvJqCH0sFfrBUTnUJSBrBf7++ypk+twtRs=
github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0/go.mod h1:HKpQxkWaGLJ+D/5H8QRpyQXA1eKjxkFlOMwck5+33Jk=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/CycloneDX/cyclonedx-go v0.8.0 h1:FyWVj6x6hoJrui5uRQdYZcSievw3Z32Z88uYzG/0D6M=
github.com/CycloneDX/cyclonedx-go v0.8.0/go.mod h1:K2bA+324+Og0X84fA8HhN2X066K7Bxz4rpMQ4ZhjtSk=
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/Netflix/go-expect v0.0.0-20180928190340-9d1f4485533b h1:sSQK05nvxs4UkgCJaxihteu+r+6ela3dNMm7NVmsS3c=
github.com/Netflix/go-expect v0.0.0-20180928190340-9d1f4485533b/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc=
github.com/PagerDuty/go-pagerduty v1.8.0 h1:MTFqTffIcAervB83U7Bx6HERzLbyaSPL/+oxH3zyluI=
github.com/PagerDuty/go-pagerduty v1.8.0/go.mod h1:nzIeAqyFSJAFkjWKvMzug0JtwDg+V+UoCWjFrfFH5mI=
github.com/ProtonMail/go-crypto v1.1.6 h1:ZcV+Ropw6Qn0AX9brlQLAUXfqLBc7Bl+f/DmNxpLfdw=
github.com/ProtonMail/go-crypto v1.1.6/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
github.com/PuerkitoBio/goquery v1.8.0 h1:PJTF7AmFCFKk1N6V6jmKfrNH9tV5pNE6lZMkG0gta/U=
github.com/PuerkitoBio/goquery v1.8.0/go.mod h1:ypIiRMtY7COPGk+I/YbZLbxsxn9g5ejnI2HSMtkjZvI=
github.com/RackSec/srslog v0.0.0-20180709174129-a4725f04ec91 h1:vX+gnvBc56EbWYrmlhYbFYRaeikAke1GL84N4BEYOFE=
github.com/RackSec/srslog v0.0.0-20180709174129-a4725f04ec91/go.mod h1:cDLGBht23g0XQdLjzn6xOGXDkLK182YfINAaZEQLCHQ=
github.com/VictorAvelar/devto-api-go v1.0.0 h1:oXmzye3xYvlgBX18vX4+v6LVbjoihgIokpeOpzeJzqU=
github.com/VictorAvelar/devto-api-go v1.0.0/go.mod h1:gX13cqzMdpo49qP8VtBR2uCnzW7d76LFrAVSX2eLifY=
github.com/adlio/trello v1.12.0 h1:JqOE2GFHQ9YtEviRRRSnicSxPbt4WFOxhqXzjMOw8lw=
github.com/adlio/trello v1.12.0/go.mod h1:I4Lti4jf2KxjTNgTqs5W3lLuE78QZZdYbbPnQQGwjOo=
github.com/alecthomas/chroma v0.10.0 h1:7XDcGkCQopCNKjZHfYrNLraA+M7e0fMiJ/Mfikbfjek=
github.com/alecthomas/chroma v0.10.0/go.mod h1:jtJATyUxlIORhUOFNA9NZDWGAQ8wpxQQqNSB4rjA/1s=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE=
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c=
github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA=
github.com/andygrunwald/go-gerrit v1.1.1 h1:U1Aw5WrwWIVc8PK+jMFmMGzGTYZFI8re8JS6DEGNYfA=
github.com/andygrunwald/go-gerrit v1.1.1/go.mod h1:SeP12EkHZxEVjuJ2HZET304NBtHGG2X6w2Gzd0QXAZw=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230321174746-8dcc6526cfb1 h1:X8MJ0fnN5FPdcGF5Ij2/OW+HgiJrRg3AfHAx1PJtIzM=
github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230321174746-8dcc6526cfb1/go.mod h1:pSwJ0fSY5KhvocuWSx4fz3BA8OrA1bQn+K1Eli3BRwM=
github.com/aokoli/goutils v1.1.0/go.mod h1:SijmP0QR8LtwsmDs8Yii5Z/S4trXFGFC2oO5g9DP+DQ=
github.com/aokoli/goutils v1.1.1 h1:/hA+Ywo3AxoDZY5ZMnkiEkUvkK4BPp927ax110KCqqg=
github.com/aokoli/goutils v1.1.1/go.mod h1:SijmP0QR8LtwsmDs8Yii5Z/S4trXFGFC2oO5g9DP+DQ=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/bradleyjkemp/cupaloy/v2 v2.8.0 h1:any4BmKE+jGIaMpnU8YgH/I2LPiLBufr6oMMlVBbn9M=
github.com/bradleyjkemp/cupaloy/v2 v2.8.0/go.mod h1:bm7JXdkRd4BHJk9HpwqAI8BoAY1lps46Enkdqw6aRX0=
github.com/briandowns/openweathermap v0.21.1 h1:TPbuixuF+aGJP1mpgTNny6eUkdbvj7gqODGXkwhss48=
github.com/briandowns/openweathermap v0.21.1/go.mod h1:0GLnknqicWxXnGi1IqoOaZIw+kIe5hkt+YM5WY3j8+0=
github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/cenkalti/backoff/v5 v5.0.2 h1:rIfFVxEf1QsI7E1ZHfp/B4DF/6QBAUhmgkxc0H7Zss8=
github.com/cenkalti/backoff/v5 v5.0.2/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/charmbracelet/bubbles v0.21.1 h1:nj0decPiixaZeL9diI4uzzQTkkz1kYY8+jgzCZXSmW0=
github.com/charmbracelet/bubbles v0.21.1/go.mod h1:HHvIYRCpbkCJw2yo0vNX1O5loCwSr9/mWS8GYSg50Sk=
github.com/charmbracelet/bubbletea v1.3.10 h1:otUDHWMMzQSB0Pkc87rm691KZ3SWa4KUlvF9nRvCICw=
github.com/charmbracelet/bubbletea v1.3.10/go.mod h1:ORQfo0fk8U+po9VaNvnV95UPWA1BitP1E0N6xJPlHr4=
github.com/charmbracelet/colorprofile v0.4.1 h1:a1lO03qTrSIRaK8c3JRxJDZOvhvIeSco3ej+ngLk1kk=
github.com/charmbracelet/colorprofile v0.4.1/go.mod h1:U1d9Dljmdf9DLegaJ0nGZNJvoXAhayhmidOdcBwAvKk=
github.com/charmbracelet/harmonica v0.2.0 h1:8NxJWRWg/bzKqqEaaeFNipOu77YR5t8aSwG4pgaUBiQ=
github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao=
github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY=
github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30=
github.com/charmbracelet/x/ansi v0.11.5 h1:NBWeBpj/lJPE3Q5l+Lusa4+mH6v7487OP8K0r1IhRg4=
github.com/charmbracelet/x/ansi v0.11.5/go.mod h1:2JNYLgQUsyqaiLovhU2Rv/pb8r6ydXKS3NIttu3VGZQ=
github.com/charmbracelet/x/cellbuf v0.0.15 h1:ur3pZy0o6z/R7EylET877CBxaiE1Sp1GMxoFPAIztPI=
github.com/charmbracelet/x/cellbuf v0.0.15/go.mod h1:J1YVbR7MUuEGIFPCaaZ96KDl5NoS0DAWkskup+mOY+Q=
github.com/charmbracelet/x/term v0.2.2 h1:xVRT/S2ZcKdhhOuSP4t5cLi5o+JxklsoEObBSgfgZRk=
github.com/charmbracelet/x/term v0.2.2/go.mod h1:kF8CY5RddLWrsgVwpw4kAa6TESp6EB5y3uxGLeCqzAI=
github.com/chromedp/cdproto v0.0.0-20230802225258-3cf4e6d46a89/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs=
github.com/chromedp/chromedp v0.9.2/go.mod h1:LkSXJKONWTCHAfQasKFUZI+mxqS4tZqhmtGzzhLsnLs=
github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM=
github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI=
github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04=
github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/clipperhouse/displaywidth v0.9.0 h1:Qb4KOhYwRiN3viMv1v/3cTBlz3AcAZX3+y9OLhMtAtA=
github.com/clipperhouse/displaywidth v0.9.0/go.mod h1:aCAAqTlh4GIVkhQnJpbL0T/WfcrJXHcj8C0yjYcjOZA=
github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs=
github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA=
github.com/clipperhouse/uax29/v2 v2.5.0 h1:x7T0T4eTHDONxFJsL94uKNKPHrclyFI0lm7+w94cO8U=
github.com/clipperhouse/uax29/v2 v2.5.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g=
github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=
github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=
github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE=
github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk=
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=
github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s=
github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/digitalocean/godo v1.175.0 h1:tpfwJFkBzpePxvvFazOn69TXctdxuFlOs7DMVXsI7oU=
github.com/digitalocean/godo v1.175.0/go.mod h1:xQsWpVCCbkDrWisHA72hPzPlnC+4W5w/McZY5ij9uvU=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/dlclark/regexp2 v1.4.0 h1:F1r
Download .txt
gitextract_iprghv20/

├── .editorconfig
├── .gitattributes
├── .github/
│   ├── FUNDING.yml
│   ├── ISSUE_TEMPLATE/
│   │   ├── Bug.md
│   │   ├── Feature.md
│   │   └── Support.md
│   ├── PULL_REQUEST_TEMPLATE/
│   │   ├── Improvement.md
│   │   └── Other.md
│   ├── PULL_REQUEST_TEMPLATE.md
│   ├── dependabot.yml
│   ├── stale.yml
│   └── workflows/
│       ├── codeql-analysis.yml
│       ├── golangci-lint.yml
│       ├── goreleaser.yml
│       ├── pr-checks.yml
│       └── staticcheck.yml
├── .gitignore
├── .gitmodules
├── .golangci.yml
├── .goreleaser.yml
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE.md
├── Makefile
├── README.md
├── SECURITY.md
├── _sample_configs/
│   ├── bargraph_config.yml
│   ├── dynamic_sizing.yml
│   ├── kubernetes_config.yml
│   ├── sample_config.yml
│   ├── small_config.yml
│   └── uniconfig.yml
├── app/
│   ├── app_manager.go
│   ├── display.go
│   ├── exit_message.go
│   ├── exit_message_test.go
│   ├── focus_tracker.go
│   ├── module_validator.go
│   ├── module_validator_test.go
│   ├── scheduler.go
│   ├── scheduler_test.go
│   ├── widget_maker.go
│   ├── widget_maker_test.go
│   └── wtf_app.go
├── cfg/
│   ├── common_settings.go
│   ├── common_settings_test.go
│   ├── config_files.go
│   ├── copy.go
│   ├── default_color_theme.go
│   ├── default_color_theme_test.go
│   ├── default_config_file.go
│   ├── error_messages.go
│   ├── parsers.go
│   ├── parsers_test.go
│   ├── position_settings.go
│   ├── position_validation.go
│   ├── position_validation_test.go
│   ├── secrets.go
│   ├── validatable.go
│   ├── validations.go
│   └── validations_test.go
├── checklist/
│   ├── checklist.go
│   ├── checklist_item.go
│   ├── checklist_item_test.go
│   └── checklist_test.go
├── flags/
│   └── flags.go
├── generator/
│   ├── settings.tpl
│   ├── textwidget.go
│   └── textwidget.tpl
├── go.mod
├── go.sum
├── help/
│   └── help.go
├── logger/
│   └── log.go
├── main.go
├── modules/
│   ├── airbrake/
│   │   ├── client.go
│   │   ├── group_info_table.go
│   │   ├── keyboard.go
│   │   ├── result_table.go
│   │   ├── settings.go
│   │   ├── util.go
│   │   └── widget.go
│   ├── asana/
│   │   ├── client.go
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── azuredevops/
│   │   ├── client.go
│   │   ├── example-conf.yml
│   │   ├── settings.go
│   │   └── widget.go
│   ├── azurelogs/
│   │   ├── config.go
│   │   ├── config_test.go
│   │   ├── query.go
│   │   ├── query_concurrent_test.go
│   │   ├── query_test.go
│   │   ├── session.go
│   │   ├── session_test.go
│   │   ├── settings.go
│   │   ├── settings_test.go
│   │   ├── widget.go
│   │   └── widget_test.go
│   ├── bamboohr/
│   │   ├── calendar.go
│   │   ├── client.go
│   │   ├── employee.go
│   │   ├── item.go
│   │   ├── request.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── bargraph/
│   │   ├── settings.go
│   │   └── widget.go
│   ├── buildkite/
│   │   ├── client.go
│   │   ├── keyboard.go
│   │   ├── pipelines_display_data.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── cds/
│   │   ├── favorites/
│   │   │   ├── display.go
│   │   │   ├── keyboard.go
│   │   │   ├── settings.go
│   │   │   └── widget.go
│   │   ├── queue/
│   │   │   ├── display.go
│   │   │   ├── keyboard.go
│   │   │   ├── settings.go
│   │   │   └── widget.go
│   │   └── status/
│   │       ├── display.go
│   │       ├── keyboard.go
│   │       ├── settings.go
│   │       └── widget.go
│   ├── circleci/
│   │   ├── build.go
│   │   ├── client.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── clocks/
│   │   ├── clock.go
│   │   ├── clock_collection.go
│   │   ├── display.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── cmdrunner/
│   │   ├── settings.go
│   │   └── widget.go
│   ├── cryptocurrency/
│   │   ├── bittrex/
│   │   │   ├── bittrex.go
│   │   │   ├── display.go
│   │   │   ├── settings.go
│   │   │   └── widget.go
│   │   ├── blockfolio/
│   │   │   ├── settings.go
│   │   │   └── widget.go
│   │   ├── cryptolive/
│   │   │   ├── price/
│   │   │   │   ├── price.go
│   │   │   │   ├── settings.go
│   │   │   │   └── widget.go
│   │   │   ├── settings.go
│   │   │   ├── toplist/
│   │   │   │   ├── display.go
│   │   │   │   ├── settings.go
│   │   │   │   ├── toplist.go
│   │   │   │   └── widget.go
│   │   │   └── widget.go
│   │   └── mempool/
│   │       ├── settings.go
│   │       └── widget.go
│   ├── datadog/
│   │   ├── client.go
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── devto/
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── digitalclock/
│   │   ├── clocks.go
│   │   ├── display.go
│   │   ├── fonts.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── digitalocean/
│   │   ├── display.go
│   │   ├── droplet.go
│   │   ├── droplet_properties_table.go
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── docker/
│   │   ├── client.go
│   │   ├── example-conf.yml
│   │   ├── settings.go
│   │   ├── utils.go
│   │   └── widget.go
│   ├── feedreader/
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   ├── widget.go
│   │   └── widget_test.go
│   ├── football/
│   │   ├── client.go
│   │   ├── settings.go
│   │   ├── types.go
│   │   ├── util.go
│   │   └── widget.go
│   ├── gcal/
│   │   ├── cal_event.go
│   │   ├── client.go
│   │   ├── display.go
│   │   ├── display_test.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── gerrit/
│   │   ├── display.go
│   │   ├── gerrit_repo.go
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── git/
│   │   ├── display.go
│   │   ├── git_repo.go
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   ├── variables.go
│   │   ├── variables_win.go
│   │   └── widget.go
│   ├── github/
│   │   ├── display.go
│   │   ├── github_repo.go
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── gitlab/
│   │   ├── display.go
│   │   ├── gitlab_project.go
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── gitlabtodo/
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── gitter/
│   │   ├── client.go
│   │   ├── gitter.go
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── googleanalytics/
│   │   ├── client.go
│   │   ├── display.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── grafana/
│   │   ├── client.go
│   │   ├── display.go
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── gspreadsheets/
│   │   ├── client.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── hackernews/
│   │   ├── client.go
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   ├── story.go
│   │   ├── story_test.go
│   │   └── widget.go
│   ├── healthchecks/
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── hibp/
│   │   ├── client.go
│   │   ├── hibp_breach.go
│   │   ├── hibp_status.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── ipaddresses/
│   │   ├── ipapi/
│   │   │   ├── settings.go
│   │   │   └── widget.go
│   │   └── ipinfo/
│   │       ├── settings.go
│   │       └── widget.go
│   ├── jenkins/
│   │   ├── client.go
│   │   ├── job.go
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   ├── view.go
│   │   └── widget.go
│   ├── jira/
│   │   ├── client.go
│   │   ├── client_test.go
│   │   ├── issues.go
│   │   ├── keyboard.go
│   │   ├── search_result.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── krisinformation/
│   │   ├── client.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── kubernetes/
│   │   ├── client.go
│   │   ├── settings.go
│   │   ├── widget.go
│   │   └── widget_test.go
│   ├── logger/
│   │   ├── settings.go
│   │   └── widget.go
│   ├── lunarphase/
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── mercurial/
│   │   ├── display.go
│   │   ├── hg_repo.go
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── nbascore/
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── newrelic/
│   │   ├── client/
│   │   │   ├── README.md
│   │   │   ├── alert_conditions.go
│   │   │   ├── alert_events.go
│   │   │   ├── application_deployments.go
│   │   │   ├── application_host_metrics.go
│   │   │   ├── application_hosts.go
│   │   │   ├── application_instance_metrics.go
│   │   │   ├── application_instances.go
│   │   │   ├── application_metrics.go
│   │   │   ├── applications.go
│   │   │   ├── array.go
│   │   │   ├── browser_applications.go
│   │   │   ├── component_metrics.go
│   │   │   ├── http_helper.go
│   │   │   ├── key_transactions.go
│   │   │   ├── legacy_alert_policies.go
│   │   │   ├── main.go
│   │   │   ├── metrics.go
│   │   │   ├── mobile_application_metrics.go
│   │   │   ├── mobile_applications.go
│   │   │   ├── notification_channels.go
│   │   │   ├── server_metrics.go
│   │   │   ├── servers.go
│   │   │   └── usages.go
│   │   ├── client.go
│   │   ├── display.go
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── nextbus/
│   │   ├── settings.go
│   │   └── widget.go
│   ├── opsgenie/
│   │   ├── client.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── pagerduty/
│   │   ├── client.go
│   │   ├── settings.go
│   │   ├── sort.go
│   │   └── widget.go
│   ├── pihole/
│   │   ├── client.go
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   ├── view.go
│   │   └── widget.go
│   ├── ping/
│   │   ├── settings.go
│   │   └── widget.go
│   ├── pivotal/
│   │   ├── client.go
│   │   ├── display.go
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   ├── structs.go
│   │   ├── view.go
│   │   └── widget.go
│   ├── pocket/
│   │   ├── client.go
│   │   ├── item_service.go
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── power/
│   │   ├── battery.go
│   │   ├── battery_freebsd.go
│   │   ├── battery_linux.go
│   │   ├── managed_device_test.go
│   │   ├── managed_devices.go
│   │   ├── settings.go
│   │   ├── source.go
│   │   ├── source_freebsd.go
│   │   ├── source_linux.go
│   │   └── widget.go
│   ├── progress/
│   │   ├── settings.go
│   │   └── widget.go
│   ├── resourceusage/
│   │   ├── settings.go
│   │   └── widget.go
│   ├── rollbar/
│   │   ├── client.go
│   │   ├── keyboard.go
│   │   ├── rollbar.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── security/
│   │   ├── dns.go
│   │   ├── firewall.go
│   │   ├── security_data.go
│   │   ├── settings.go
│   │   ├── users.go
│   │   ├── widget.go
│   │   └── wifi.go
│   ├── spacex/
│   │   ├── client.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── spotify/
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── spotifyweb/
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── status/
│   │   ├── settings.go
│   │   └── widget.go
│   ├── steam/
│   │   ├── client.go
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── stocks/
│   │   ├── finnhub/
│   │   │   ├── client.go
│   │   │   ├── quote.go
│   │   │   ├── settings.go
│   │   │   └── widget.go
│   │   └── yfinance/
│   │       ├── settings.go
│   │       ├── widget.go
│   │       └── yquote.go
│   ├── subreddit/
│   │   ├── api.go
│   │   ├── keyboard.go
│   │   ├── link.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── system/
│   │   ├── settings.go
│   │   ├── system_info.go
│   │   ├── system_info_windows.go
│   │   └── widget.go
│   ├── textfile/
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── todo/
│   │   ├── display.go
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── todo_plus/
│   │   ├── backend/
│   │   │   ├── backend.go
│   │   │   ├── project.go
│   │   │   ├── todoist.go
│   │   │   └── trello.go
│   │   ├── display.go
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── transmission/
│   │   ├── display.go
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── travisci/
│   │   ├── client.go
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   ├── travis.go
│   │   └── widget.go
│   ├── twitch/
│   │   ├── client.go
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── twitter/
│   │   ├── client.go
│   │   ├── keyboard.go
│   │   ├── request.go
│   │   ├── settings.go
│   │   ├── tweet.go
│   │   ├── user.go
│   │   └── widget.go
│   ├── twitterstats/
│   │   ├── client.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── unknown/
│   │   ├── settings.go
│   │   └── widget.go
│   ├── updown/
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── uptimekuma/
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── uptimerobot/
│   │   ├── keyboard.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── urlcheck/
│   │   ├── client.go
│   │   ├── client_test.go
│   │   ├── settings.go
│   │   ├── urlResult.go
│   │   ├── urlResult_test.go
│   │   ├── view.go
│   │   └── widget.go
│   ├── victorops/
│   │   ├── client.go
│   │   ├── oncallresponse.go
│   │   ├── oncallteam.go
│   │   ├── settings.go
│   │   └── widget.go
│   ├── weatherservices/
│   │   ├── arpansagovau/
│   │   │   ├── client.go
│   │   │   ├── settings.go
│   │   │   └── widget.go
│   │   ├── prettyweather/
│   │   │   ├── settings.go
│   │   │   └── widget.go
│   │   └── weather/
│   │       ├── display.go
│   │       ├── emoji.go
│   │       ├── keyboard.go
│   │       ├── settings.go
│   │       └── widget.go
│   └── zendesk/
│       ├── client.go
│       ├── keyboard.go
│       ├── settings.go
│       ├── tickets.go
│       └── widget.go
├── scripts/
│   └── check-uncommitted-vendor-files.sh
├── support/
│   └── github.go
├── utils/
│   ├── colors.go
│   ├── colors_test.go
│   ├── conversions.go
│   ├── conversions_test.go
│   ├── email_addresses.go
│   ├── email_addresses_test.go
│   ├── help_parser.go
│   ├── homedir.go
│   ├── homedir_test.go
│   ├── init.go
│   ├── init_test.go
│   ├── reflective.go
│   ├── sums.go
│   ├── sums_test.go
│   ├── text.go
│   ├── text_test.go
│   ├── utils.go
│   └── utils_test.go
├── view/
│   ├── bargraph.go
│   ├── bargraph_test.go
│   ├── base.go
│   ├── base_test.go
│   ├── billboard_modal.go
│   ├── info_table.go
│   ├── info_table_test.go
│   ├── keyboard_widget.go
│   ├── keyboard_widget_test.go
│   ├── multisource_widget.go
│   ├── scrollable_widget.go
│   ├── text_widget.go
│   └── text_widget_test.go
└── wtf/
    ├── colors.go
    ├── colors_test.go
    ├── datetime.go
    ├── datetime_test.go
    ├── enablable.go
    ├── numbers.go
    ├── numbers_test.go
    ├── schedulable.go
    ├── stoppable.go
    ├── terminal.go
    └── wtfable.go
Download .txt
Showing preview only (221K chars total). Download the full file or copy to clipboard to get everything.
SYMBOL INDEX (2585 symbols across 479 files)

FILE: app/app_manager.go
  type WtfAppManager (line 11) | type WtfAppManager struct
    method MakeNewWtfApp (line 27) | func (appMan *WtfAppManager) MakeNewWtfApp(config *config.Config, conf...
    method Add (line 36) | func (appMan *WtfAppManager) Add(wtfApp *WtfApp) {
    method Current (line 41) | func (appMan *WtfAppManager) Current() (*WtfApp, error) {
    method Next (line 51) | func (appMan *WtfAppManager) Next() (*WtfApp, error) {
    method Prev (line 63) | func (appMan *WtfAppManager) Prev() (*WtfApp, error) {
  function NewAppManager (line 18) | func NewAppManager() WtfAppManager {

FILE: app/display.go
  type Display (line 11) | type Display struct
    method add (line 37) | func (display *Display) add(widget wtf.Wtfable) {
    method build (line 54) | func (display *Display) build(widgets []wtf.Wtfable) *tview.Grid {
  function NewDisplay (line 17) | func NewDisplay(widgets []wtf.Wtfable, config *config.Config) *Display {

FILE: app/exit_message.go
  constant exitMessageHeader (line 12) | exitMessageHeader = `
  method DisplayExitMessage (line 24) | func (wtfApp *WtfApp) DisplayExitMessage() {
  method displayExitMsg (line 32) | func (wtfApp *WtfApp) displayExitMsg(exitMessageIsDisplayable bool) stri...
  function readDisplayableConfig (line 63) | func readDisplayableConfig(cfg *config.Config) bool {
  function readGitHubAPIKey (line 69) | func readGitHubAPIKey(cfg *config.Config) string {
  method contributorThankYouMessage (line 85) | func (wtfApp *WtfApp) contributorThankYouMessage() string {
  method sponsorThankYouMessage (line 92) | func (wtfApp *WtfApp) sponsorThankYouMessage() string {
  method supportRequestMessage (line 99) | func (wtfApp *WtfApp) supportRequestMessage() string {

FILE: app/exit_message_test.go
  function Test_displayExitMessage (line 11) | func Test_displayExitMessage(t *testing.T) {

FILE: app/focus_tracker.go
  type FocusState (line 13) | type FocusState
  constant widgetFocused (line 16) | widgetFocused FocusState = iota
  constant appBoardFocused (line 17) | appBoardFocused
  constant neverFocused (line 18) | neverFocused
  type FocusTracker (line 23) | type FocusTracker struct
    method FocusOn (line 51) | func (tracker *FocusTracker) FocusOn(char string) bool {
    method Next (line 79) | func (tracker *FocusTracker) Next() {
    method None (line 92) | func (tracker *FocusTracker) None() {
    method Prev (line 102) | func (tracker *FocusTracker) Prev() {
    method Refocus (line 115) | func (tracker *FocusTracker) Refocus() {
    method assignHotKeys (line 124) | func (tracker *FocusTracker) assignHotKeys() {
    method blur (line 165) | func (tracker *FocusTracker) blur(idx int) {
    method decrement (line 183) | func (tracker *FocusTracker) decrement() {
    method focus (line 191) | func (tracker *FocusTracker) focus(idx int) {
    method focusables (line 206) | func (tracker *FocusTracker) focusables() []wtf.Wtfable {
    method focusableAt (line 232) | func (tracker *FocusTracker) focusableAt(idx int) wtf.Wtfable {
    method focusState (line 240) | func (tracker *FocusTracker) focusState() FocusState {
    method increment (line 254) | func (tracker *FocusTracker) increment() {
    method useNavShortcuts (line 262) | func (tracker *FocusTracker) useNavShortcuts() bool {
  function NewFocusTracker (line 33) | func NewFocusTracker(tviewApp *tview.Application, widgets []wtf.Wtfable,...

FILE: app/module_validator.go
  type ModuleValidator (line 13) | type ModuleValidator struct
    method Validate (line 27) | func (val *ModuleValidator) Validate(widgets []wtf.Wtfable) {
  type widgetError (line 15) | type widgetError struct
    method errorMessages (line 61) | func (err widgetError) errorMessages() (messages []string) {
  function NewModuleValidator (line 21) | func NewModuleValidator() *ModuleValidator {
  function validate (line 43) | func validate(widgets []wtf.Wtfable) (widgetErrors []widgetError) {

FILE: app/module_validator_test.go
  constant valid (line 14) | valid = `
  constant invalid (line 26) | invalid = `
  function Test_NewModuleValidator (line 39) | func Test_NewModuleValidator(t *testing.T) {
  function Test_validate (line 43) | func Test_validate(t *testing.T) {

FILE: app/scheduler.go
  function Schedule (line 11) | func Schedule(widget wtf.Wtfable) {

FILE: app/scheduler_test.go
  constant configExample (line 11) | configExample = `
  constant new (line 23) | new = `
  function Test_RefreshInterval (line 36) | func Test_RefreshInterval(t *testing.T) {

FILE: app/widget_maker.go
  function MakeWidget (line 96) | func MakeWidget(
  function MakeWidgets (line 389) | func MakeWidgets(tviewApp *tview.Application, pages *tview.Pages, config...

FILE: app/widget_maker_test.go
  constant disabled (line 13) | disabled = `
  constant enabled (line 25) | enabled = `
  function Test_MakeWidget (line 38) | func Test_MakeWidget(t *testing.T) {

FILE: app/wtf_app.go
  type WtfApp (line 23) | type WtfApp struct
    method Exit (line 112) | func (wtfApp *WtfApp) Exit() {
    method Execute (line 120) | func (wtfApp *WtfApp) Execute() error {
    method Start (line 129) | func (wtfApp *WtfApp) Start() {
    method Stop (line 138) | func (wtfApp *WtfApp) Stop() {
    method stopAllWidgets (line 148) | func (wtfApp *WtfApp) stopAllWidgets() {
    method keyboardIntercept (line 154) | func (wtfApp *WtfApp) keyboardIntercept(event *tcell.EventKey) *tcell....
    method refreshAllWidgets (line 197) | func (wtfApp *WtfApp) refreshAllWidgets() {
    method scheduleWidgets (line 203) | func (wtfApp *WtfApp) scheduleWidgets() {
    method watchForConfigChanges (line 209) | func (wtfApp *WtfApp) watchForConfigChanges() {
  function NewWtfApp (line 43) | func NewWtfApp(tviewApp *tview.Application, config *config.Config, confi...
  function handleRedraws (line 92) | func handleRedraws(tviewApp *tview.Application, redrawChan chan bool) {

FILE: cfg/common_settings.go
  constant defaultLanguageTag (line 14) | defaultLanguageTag = "en-CA"
  type Module (line 17) | type Module struct
  type Sigils (line 22) | type Sigils struct
  type Common (line 34) | type Common struct
    method DefaultFocusedRowColor (line 123) | func (common *Common) DefaultFocusedRowColor() string {
    method DefaultRowColor (line 131) | func (common *Common) DefaultRowColor() string {
    method FocusChar (line 141) | func (common *Common) FocusChar() string {
    method LocalizedPrinter (line 156) | func (common *Common) LocalizedPrinter() (*message.Printer, error) {
    method RowColor (line 167) | func (common *Common) RowColor(idx int) string {
    method RightAlignFormat (line 182) | func (*Common) RightAlignFormat(width int) string {
    method PaginationMarker (line 189) | func (common *Common) PaginationMarker(length, pos, width int) string {
    method SetDocumentationPath (line 208) | func (common *Common) SetDocumentationPath(path string) {
    method Validations (line 214) | func (common *Common) Validations() []Validatable {
  function NewCommonSettingsFromModule (line 55) | func NewCommonSettingsFromModule(name, defaultTitle string, defaultFocus...

FILE: cfg/common_settings_test.go
  function Test_NewCommonSettingsFromModule (line 29) | func Test_NewCommonSettingsFromModule(t *testing.T) {
  function Test_DefaultFocusedRowColor (line 40) | func Test_DefaultFocusedRowColor(t *testing.T) {
  function Test_DefaultRowColor (line 44) | func Test_DefaultRowColor(t *testing.T) {
  function Test_FocusChar (line 48) | func Test_FocusChar(t *testing.T) {
  function Test_RowColor (line 93) | func Test_RowColor(t *testing.T) {
  function Test_RightAlignFormat (line 118) | func Test_RightAlignFormat(t *testing.T) {
  function Test_PaginationMarker (line 148) | func Test_PaginationMarker(t *testing.T) {
  function Test_Validations (line 193) | func Test_Validations(t *testing.T) {

FILE: cfg/config_files.go
  constant XdgConfigDir (line 15) | XdgConfigDir = "~/.config/"
  constant WtfConfigDirV1 (line 18) | WtfConfigDirV1 = "~/.wtf/"
  constant WtfConfigDirV2 (line 21) | WtfConfigDirV2 = "~/.config/wtf/"
  constant WtfConfigFile (line 24) | WtfConfigFile = "config.yml"
  function CreateFile (line 33) | func CreateFile(fileName string) (string, error) {
  function Initialize (line 59) | func Initialize(hasCustom bool) {
  function WtfConfigDir (line 75) | func WtfConfigDir() (string, error) {
  function LoadWtfConfigFile (line 91) | func LoadWtfConfigFile(filePath string) *config.Config {
  function chmodConfigFile (line 106) | func chmodConfigFile() {
  function createWtfConfigDir (line 124) | func createWtfConfigDir() {
  function createWtfConfigFile (line 138) | func createWtfConfigFile() {
  function expandHomeDir (line 159) | func expandHomeDir(path string) (string, error) {
  function home (line 182) | func home() (string, error) {
  function migrateOldConfig (line 196) | func migrateOldConfig() {

FILE: cfg/copy.go
  function Copy (line 14) | func Copy(src, dest string) error {
  function locationCopy (line 23) | func locationCopy(src, dest string, info os.FileInfo) error {
  function fileCopy (line 30) | func fileCopy(src, dest string, info os.FileInfo) error {
  function directoryCopy (line 52) | func directoryCopy(src, dest string, info os.FileInfo) error {

FILE: cfg/default_color_theme.go
  type BorderTheme (line 9) | type BorderTheme struct
  type CheckboxTheme (line 16) | type CheckboxTheme struct
  type RowTheme (line 21) | type RowTheme struct
  type TextTheme (line 33) | type TextTheme struct
  type WidgetTheme (line 41) | type WidgetTheme struct
  type ColorTheme (line 46) | type ColorTheme struct
  function NewDefaultColorTheme (line 55) | func NewDefaultColorTheme() ColorTheme {
  function NewDefaultColorConfig (line 95) | func NewDefaultColorConfig() (*config.Config, error) {

FILE: cfg/default_color_theme_test.go
  function Test_NewDefaultColorTheme (line 9) | func Test_NewDefaultColorTheme(t *testing.T) {
  function Test_NewDefaultColorConfig (line 17) | func Test_NewDefaultColorConfig(t *testing.T) {

FILE: cfg/default_config_file.go
  constant defaultConfigFile (line 3) | defaultConfigFile = `wtf:

FILE: cfg/error_messages.go
  function displayError (line 17) | func displayError(err error) {
  function displayDefaultConfigCreateError (line 21) | func displayDefaultConfigCreateError(err error) {
  function displayDefaultConfigWriteError (line 27) | func displayDefaultConfigWriteError(err error) {
  function displayWtfConfigDirCreateError (line 33) | func displayWtfConfigDirCreateError(err error) {
  function displayWtfConfigFileLoadError (line 39) | func displayWtfConfigFileLoadError(path string, err error) {

FILE: cfg/parsers.go
  function ParseAsMapOrList (line 12) | func ParseAsMapOrList(ymlConfig *config.Config, configKey string) []stri...
  function ParseTimeString (line 33) | func ParseTimeString(cfg *config.Config, configKey string, defaultValue ...

FILE: cfg/parsers_test.go
  function Test_ParseAsMapOrList (line 10) | func Test_ParseAsMapOrList(t *testing.T) {
  function Test_ParseTimeString (line 53) | func Test_ParseTimeString(t *testing.T) {

FILE: cfg/position_settings.go
  constant positionPath (line 8) | positionPath = "position"
  type PositionSettings (line 12) | type PositionSettings struct
  function NewPositionSettingsFromYAML (line 22) | func NewPositionSettingsFromYAML(moduleConfig *config.Config) PositionSe...

FILE: cfg/position_validation.go
  type positionValidation (line 33) | type positionValidation struct
    method Error (line 39) | func (posVal *positionValidation) Error() error {
    method HasError (line 43) | func (posVal *positionValidation) HasError() bool {
    method IntValue (line 47) | func (posVal *positionValidation) IntValue() int {
    method String (line 52) | func (posVal *positionValidation) String() string {
  function newPositionValidation (line 58) | func newPositionValidation(name string, intVal int, err error) *position...

FILE: cfg/position_validation_test.go
  function Test_Attributes (line 18) | func Test_Attributes(t *testing.T) {

FILE: cfg/secrets.go
  type SecretLoadParams (line 14) | type SecretLoadParams struct
    method Service (line 85) | func (slp *SecretLoadParams) Service(service string) *SecretLoadParams {
    method Load (line 92) | func (slp *SecretLoadParams) Load() {
  function ModuleSecret (line 76) | func ModuleSecret(name string, globalConfig *config.Config, secret *stri...
  type Secret (line 100) | type Secret struct
  function configureSecret (line 107) | func configureSecret(
  function FetchSecret (line 146) | func FetchSecret(globalConfig *config.Config, service string) (*Secret, ...
  function StoreSecret (line 168) | func StoreSecret(globalConfig *config.Config, secret *Secret) error {
  type program (line 196) | type program struct
  function newProgram (line 201) | func newProgram(globalConfig *config.Config) *program {

FILE: cfg/validatable.go
  type Validatable (line 4) | type Validatable interface

FILE: cfg/validations.go
  type Validations (line 4) | type Validations struct
    method append (line 17) | func (vals *Validations) append(key string, posVal Validatable) {
    method intValueFor (line 21) | func (vals *Validations) intValueFor(key string) int {
  function NewValidations (line 9) | func NewValidations() *Validations {

FILE: cfg/validations_test.go
  function Test_intValueFor (line 13) | func Test_intValueFor(t *testing.T) {

FILE: checklist/checklist.go
  type Checklist (line 9) | type Checklist struct
    method Add (line 31) | func (list *Checklist) Add(checked bool, date *time.Time, tags []strin...
    method CheckedItems (line 49) | func (list *Checklist) CheckedItems() []*ChecklistItem {
    method Delete (line 62) | func (list *Checklist) Delete(selectedIndex int) {
    method IsSelectable (line 69) | func (list *Checklist) IsSelectable() bool {
    method IsUnselectable (line 74) | func (list *Checklist) IsUnselectable() bool {
    method LongestLine (line 79) | func (list *Checklist) LongestLine() int {
    method IndexByItem (line 92) | func (list *Checklist) IndexByItem(selectableItem *ChecklistItem) (ind...
    method UncheckedItems (line 102) | func (list *Checklist) UncheckedItems() []*ChecklistItem {
    method Unselect (line 115) | func (list *Checklist) Unselect() {
    method Len (line 121) | func (list *Checklist) Len() int {
    method Less (line 125) | func (list *Checklist) Less(i, j int) bool {
    method Swap (line 129) | func (list *Checklist) Swap(i, j int) {
  function NewChecklist (line 17) | func NewChecklist(checkedIcon, uncheckedIcon string) Checklist {

FILE: checklist/checklist_item.go
  type ChecklistItem (line 10) | type ChecklistItem struct
    method CheckMark (line 33) | func (item *ChecklistItem) CheckMark() string {
    method EditText (line 44) | func (item *ChecklistItem) EditText() string {
    method TagString (line 55) | func (item *ChecklistItem) TagString() string {
    method Toggle (line 70) | func (item *ChecklistItem) Toggle() {
    method ensureItemIcons (line 76) | func (item *ChecklistItem) ensureItemIcons() {
  function NewChecklistItem (line 19) | func NewChecklistItem(checked bool, date *time.Time, tags []string, text...

FILE: checklist/checklist_item_test.go
  function testChecklistItem (line 9) | func testChecklistItem() *ChecklistItem {
  function Test_CheckMark (line 21) | func Test_CheckMark(t *testing.T) {
  function Test_Toggle (line 29) | func Test_Toggle(t *testing.T) {

FILE: checklist/checklist_test.go
  function Test_NewCheckist (line 9) | func Test_NewCheckist(t *testing.T) {
  function Test_Add (line 19) | func Test_Add(t *testing.T) {
  function Test_CheckedItems (line 26) | func Test_CheckedItems(t *testing.T) {
  function Test_Delete (line 79) | func Test_Delete(t *testing.T) {
  function Test_IsSelectable (line 109) | func Test_IsSelectable(t *testing.T) {
  function Test_IsUnselectable (line 145) | func Test_IsUnselectable(t *testing.T) {
  function Test_LongestLine (line 181) | func Test_LongestLine(t *testing.T) {
  function Test_IndexByItem (line 220) | func Test_IndexByItem(t *testing.T) {
  function Test_UncheckedItems (line 262) | func Test_UncheckedItems(t *testing.T) {
  function Test_Unselect (line 315) | func Test_Unselect(t *testing.T) {
  function Test_Len (line 328) | func Test_Len(t *testing.T) {
  function Test_Less (line 367) | func Test_Less(t *testing.T) {
  function Test_Swap (line 405) | func Test_Swap(t *testing.T) {

FILE: flags/flags.go
  type Flags (line 18) | type Flags struct
    method ConfigFilePath (line 52) | func (flags *Flags) ConfigFilePath() string {
    method RenderIf (line 58) | func (flags *Flags) RenderIf(config *config.Config) {
    method HasCustomConfig (line 157) | func (flags *Flags) HasCustomConfig() bool {
    method HasModule (line 162) | func (flags *Flags) HasModule() bool {
    method HasVersion (line 167) | func (flags *Flags) HasVersion() bool {
    method Parse (line 172) | func (flags *Flags) Parse() {
  function NewFlags (line 44) | func NewFlags() *Flags {

FILE: generator/textwidget.go
  constant defaultWidgetName (line 20) | defaultWidgetName = "NewTextWidget"
  constant widgetMaker (line 21) | widgetMaker       = "app/widget_maker.go"
  function main (line 24) | func main() {
  function createModuleDirectory (line 45) | func createModuleDirectory(data struct{ Name string }) {
  function generateWidgetFile (line 52) | func generateWidgetFile(data struct{ Name string }) {
  function generateSettingsFile (line 66) | func generateSettingsFile(data struct{ Name string }) {

FILE: help/help.go
  function Display (line 12) | func Display(moduleName string, cfg *config.Config) {
  function helpFor (line 20) | func helpFor(moduleName string, cfg *config.Config) string {

FILE: logger/log.go
  function Log (line 11) | func Log(msg string) {
  function LogFileMissing (line 26) | func LogFileMissing() bool {
  function LogFilePath (line 30) | func LogFilePath() string {

FILE: main.go
  function main (line 26) | func main() {

FILE: modules/airbrake/client.go
  function project (line 10) | func project(projectID int, authToken string) (*Project, error) {
  function groups (line 37) | func groups(projectID int, authToken string) ([]Group, error) {
  function resolveGroup (line 65) | func resolveGroup(projectID int64, groupID, authToken string) error {
  function muteGroup (line 87) | func muteGroup(projectID int64, groupID, authToken string) error {
  function unmuteGroup (line 109) | func unmuteGroup(projectID int64, groupID, authToken string) error {

FILE: modules/airbrake/group_info_table.go
  type groupInfoTable (line 10) | type groupInfoTable struct
    method buildPropertyMap (line 33) | func (propTable *groupInfoTable) buildPropertyMap() map[string]string {
    method render (line 51) | func (propTable *groupInfoTable) render() string {
  function newGroupInfoTable (line 19) | func newGroupInfoTable(g *Group) *groupInfoTable {

FILE: modules/airbrake/keyboard.go
  method initializeKeyboardControls (line 5) | func (widget *Widget) initializeKeyboardControls() {

FILE: modules/airbrake/result_table.go
  type resultTable (line 8) | type resultTable struct
    method render (line 27) | func (propTable *resultTable) render() string {
  function newResultTable (line 16) | func newResultTable(result, message string) *resultTable {

FILE: modules/airbrake/settings.go
  constant defaultFocusable (line 12) | defaultFocusable = true
  constant defaultTitle (line 13) | defaultTitle     = "Airbrake"
  type Settings (line 16) | type Settings struct
  function NewSettingsFromYAML (line 23) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...
  function getProjectID (line 36) | func getProjectID() int {

FILE: modules/airbrake/util.go
  function reverseString (line 3) | func reverseString(s string) string {

FILE: modules/airbrake/widget.go
  constant module (line 13) | module = "Airbrake"
  type ShowType (line 20) | type ShowType
  constant SHOW_TITLE (line 23) | SHOW_TITLE ShowType = iota
  constant SHOW_COMPARE (line 24) | SHOW_COMPARE
  type Widget (line 27) | type Widget struct
    method Refresh (line 134) | func (widget *Widget) Refresh() {
    method Render (line 166) | func (widget *Widget) Render() {
    method content (line 172) | func (widget *Widget) content() (string, string, bool) {
    method openGroup (line 218) | func (widget *Widget) openGroup() {
    method viewGroup (line 227) | func (widget *Widget) viewGroup() {
    method resolveGroup (line 253) | func (widget *Widget) resolveGroup() {
    method muteGroup (line 285) | func (widget *Widget) muteGroup() {
    method unmuteGroup (line 301) | func (widget *Widget) unmuteGroup() {
    method toggleDisplayText (line 317) | func (widget *Widget) toggleDisplayText() {
  type GroupJSON (line 41) | type GroupJSON struct
  type Group (line 45) | type Group struct
    method Link (line 62) | func (g *Group) Link() string {
    method Title (line 66) | func (g *Group) Title() string {
    method Type (line 70) | func (g *Group) Type() string {
    method Message (line 74) | func (g *Group) Message() string {
    method File (line 79) | func (g *Group) File() string {
  type GroupContext (line 57) | type GroupContext struct
  type Error (line 85) | type Error struct
  type StackFrame (line 91) | type StackFrame struct
  type ProjectJSON (line 97) | type ProjectJSON struct
  type Project (line 101) | type Project struct
  function rotateShowType (line 105) | func rotateShowType(showtype ShowType) ShowType {
  function NewWidget (line 116) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, pages ...

FILE: modules/asana/client.go
  function fetchTasksFromProject (line 11) | func fetchTasksFromProject(token, projectId, mode string) ([]*TaskItem, ...
  function fetchTasksFromProjectSections (line 36) | func fetchTasksFromProjectSections(token, projectId string, sections []s...
  function fetchTasksFromWorkspace (line 83) | func fetchTasksFromWorkspace(token, workspaceId, mode string) ([]*TaskIt...
  function toggleTaskCompletionById (line 110) | func toggleTaskCompletionById(token, taskId string) error {
  function processFetchedTasks (line 140) | func processFetchedTasks(client *asana.Client, fetchedTasks *[]*asana.Ta...
  function buildTaskItem (line 172) | func buildTaskItem(task *asana.Task, projectId string) *TaskItem {
  function getOtherUserEmail (line 204) | func getOtherUserEmail(client *asana.Client, uid string) (string, error) {
  function getCurrentUserId (line 221) | func getCurrentUserId(client *asana.Client, mode string) (string, error) {
  function findSection (line 233) | func findSection(client *asana.Client, project *asana.Project, sectionNa...
  function getTasksFromAsana (line 257) | func getTasksFromAsana(client *asana.Client, q *asana.TaskQuery) ([]*asa...

FILE: modules/asana/keyboard.go
  method initializeKeyboardControls (line 5) | func (widget *Widget) initializeKeyboardControls() {

FILE: modules/asana/settings.go
  constant defaultFocusable (line 10) | defaultFocusable = true
  constant defaultTitle (line 11) | defaultTitle     = "Asana"
  type Settings (line 14) | type Settings struct
  function NewSettingsFromYAML (line 34) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/asana/widget.go
  type TaskType (line 14) | type TaskType
  constant TASK_TYPE (line 17) | TASK_TYPE TaskType = iota
  constant TASK_SECTION (line 18) | TASK_SECTION
  constant TASK_BREAK (line 19) | TASK_BREAK
  type TaskItem (line 22) | type TaskItem struct
  type Widget (line 33) | type Widget struct
    method Refresh (line 60) | func (widget *Widget) Refresh() {
    method Render (line 84) | func (widget *Widget) Render() {
    method Fetch (line 88) | func (widget *Widget) Fetch(workspaceId, projectId, mode string, secti...
    method content (line 148) | func (widget *Widget) content() (string, string, bool) {
    method openTask (line 232) | func (widget *Widget) openTask() {
    method toggleTaskCompletion (line 243) | func (widget *Widget) toggleTaskCompletion() {
  function NewWidget (line 44) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, pages ...

FILE: modules/azuredevops/client.go
  method getBuildStats (line 11) | func (widget *Widget) getBuildStats() string {

FILE: modules/azuredevops/settings.go
  constant defaultFocus (line 11) | defaultFocus = false
  constant defaultTitle (line 12) | defaultTitle = "azuredevops"
  type Settings (line 16) | type Settings struct
  function NewSettingsFromYAML (line 27) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/azuredevops/widget.go
  type Widget (line 14) | type Widget struct
    method Refresh (line 45) | func (widget *Widget) Refresh() {
    method display (line 50) | func (widget *Widget) display() (string, string, bool) {
    method refreshDisplayBuffer (line 54) | func (widget *Widget) refreshDisplayBuffer() {
  function NewWidget (line 22) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, pages ...

FILE: modules/azurelogs/config.go
  type QueryFile (line 12) | type QueryFile struct
  function readQueryFile (line 21) | func readQueryFile(sess *Session, queryPath string) error {
  function readQueryFileContent (line 44) | func readQueryFileContent(filePath string) (QueryFile, error) {

FILE: modules/azurelogs/config_test.go
  function TestQueryFile_Structure (line 11) | func TestQueryFile_Structure(t *testing.T) {
  function TestReadQueryFileContent_ValidYAML (line 31) | func TestReadQueryFileContent_ValidYAML(t *testing.T) {
  function TestReadQueryFileContent_InvalidYAML (line 64) | func TestReadQueryFileContent_InvalidYAML(t *testing.T) {
  function TestReadQueryFileContent_NonexistentFile (line 85) | func TestReadQueryFileContent_NonexistentFile(t *testing.T) {
  function TestReadQueryFile_ValidYAMLFile (line 93) | func TestReadQueryFile_ValidYAMLFile(t *testing.T) {
  function TestReadQueryFile_NonYAMLFile (line 125) | func TestReadQueryFile_NonYAMLFile(t *testing.T) {
  function TestReadQueryFile_EmptyYAMLFile (line 144) | func TestReadQueryFile_EmptyYAMLFile(t *testing.T) {
  function TestReadQueryFile_PartialYAMLFile (line 165) | func TestReadQueryFile_PartialYAMLFile(t *testing.T) {
  function TestQueryFile_YAMLTags (line 191) | func TestQueryFile_YAMLTags(t *testing.T) {

FILE: modules/azurelogs/query.go
  type TableRow (line 20) | type TableRow
  type TableResp (line 23) | type TableResp struct
  function RunQuery (line 29) | func RunQuery(sess *Session) (*TableResp, error) {

FILE: modules/azurelogs/query_concurrent_test.go
  function TestLogQueryClients_ConcurrentAccess (line 12) | func TestLogQueryClients_ConcurrentAccess(t *testing.T) {
  function TestLogQueryClients_ConcurrentReadWrite (line 70) | func TestLogQueryClients_ConcurrentReadWrite(t *testing.T) {
  function TestLogQueryClients_RaceCondition (line 129) | func TestLogQueryClients_RaceCondition(t *testing.T) {

FILE: modules/azurelogs/query_test.go
  function createMockSession (line 11) | func createMockSession() *Session {
  function TestRunQuery_MissingWorkspaceID (line 23) | func TestRunQuery_MissingWorkspaceID(t *testing.T) {
  function TestRunQuery_MissingSubscriptionID (line 36) | func TestRunQuery_MissingSubscriptionID(t *testing.T) {
  function TestTableResp_Structure (line 47) | func TestTableResp_Structure(t *testing.T) {
  function TestTableRow_Operations (line 64) | func TestTableRow_Operations(t *testing.T) {
  function TestLogQueryClients_GlobalVariable (line 79) | func TestLogQueryClients_GlobalVariable(t *testing.T) {

FILE: modules/azurelogs/session.go
  constant envAzureClientID (line 12) | envAzureClientID     = "AZURE_CLIENT_ID"
  constant envAzureClientSecret (line 13) | envAzureClientSecret = "AZURE_CLIENT_SECRET"
  constant envAzureTenantID (line 14) | envAzureTenantID     = "AZURE_TENANT_ID"
  function Init (line 18) | func Init(queryPath *string) (*Session, error) {
  type Session (line 36) | type Session struct
  type AZClientSecretCredential (line 47) | type AZClientSecretCredential struct
  type AZSession (line 54) | type AZSession struct
  function InitializeAzureAuthentication (line 60) | func InitializeAzureAuthentication(sess *Session) error {
  function CreateLogsClient (line 92) | func CreateLogsClient(sess *Session, subscriptionID string) (*azquery.Lo...

FILE: modules/azurelogs/session_test.go
  function TestAZClientSecretCredential_Structure (line 10) | func TestAZClientSecretCredential_Structure(t *testing.T) {
  function TestAZSession_Structure (line 23) | func TestAZSession_Structure(t *testing.T) {
  function TestSession_Structure (line 38) | func TestSession_Structure(t *testing.T) {
  function TestInitializeAzureAuthentication_EnvironmentVariables (line 59) | func TestInitializeAzureAuthentication_EnvironmentVariables(t *testing.T) {
  function TestCreateLogsClient_NilCredentials (line 128) | func TestCreateLogsClient_NilCredentials(t *testing.T) {
  function TestInit_InvalidQueryPath (line 144) | func TestInit_InvalidQueryPath(t *testing.T) {
  function TestInit_NilQueryPath (line 155) | func TestInit_NilQueryPath(t *testing.T) {
  function TestEnvironmentConstants (line 172) | func TestEnvironmentConstants(t *testing.T) {

FILE: modules/azurelogs/settings.go
  constant defaultFocusable (line 10) | defaultFocusable = true
  constant defaultTitle (line 11) | defaultTitle     = "Azure Logs"
  type Settings (line 15) | type Settings struct
  function NewSettingsFromYAML (line 23) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/azurelogs/settings_test.go
  function TestSettings_Structure (line 12) | func TestSettings_Structure(t *testing.T) {
  function TestNewSettingsFromYAML (line 26) | func TestNewSettingsFromYAML(t *testing.T) {
  function TestDefaultConstants (line 81) | func TestDefaultConstants(t *testing.T) {
  function TestSettings_QueryfileField (line 87) | func TestSettings_QueryfileField(t *testing.T) {
  function TestNewSettingsFromYAML_Integration (line 106) | func TestNewSettingsFromYAML_Integration(t *testing.T) {
  function yamlFromMap (line 142) | func yamlFromMap(data map[string]interface{}) string {
  function interfaceToString (line 171) | func interfaceToString(v interface{}) string {

FILE: modules/azurelogs/widget.go
  constant defaultTableWidth (line 16) | defaultTableWidth  = 120
  constant minColumnWidth (line 17) | minColumnWidth     = 8
  constant maxColumnWidth (line 18) | maxColumnWidth     = 30
  constant maxDisplayRows (line 19) | maxDisplayRows     = 50
  constant truncateMarker (line 20) | truncateMarker     = "..."
  constant sampleRowsForWidth (line 21) | sampleRowsForWidth = 15
  type Widget (line 24) | type Widget struct
    method Refresh (line 47) | func (widget *Widget) Refresh() {
    method fetchDataAsync (line 63) | func (widget *Widget) fetchDataAsync() {
    method setError (line 91) | func (widget *Widget) setError(err error) {
    method renderTable (line 97) | func (widget *Widget) renderTable(title string) (string, string, bool) {
    method formatTableHeaders (line 121) | func (widget *Widget) formatTableHeaders(sb *strings.Builder, headers ...
    method formatTableSeparator (line 136) | func (widget *Widget) formatTableSeparator(sb *strings.Builder, header...
    method formatTableRows (line 147) | func (widget *Widget) formatTableRows(sb *strings.Builder, rows []Tabl...
    method content (line 248) | func (widget *Widget) content() (string, string, bool) {
  function NewWidget (line 34) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, _ *tvi...
  function calculateAdaptiveColumnWidths (line 181) | func calculateAdaptiveColumnWidths(tr *TableResp, availableWidth int) []...

FILE: modules/azurelogs/widget_test.go
  function TestNewWidget (line 13) | func TestNewWidget(t *testing.T) {
  function TestWidget_SetError (line 38) | func TestWidget_SetError(t *testing.T) {
  function TestWidget_RenderTable (line 49) | func TestWidget_RenderTable(t *testing.T) {
  function TestWidget_FormatTableHeaders (line 106) | func TestWidget_FormatTableHeaders(t *testing.T) {
  function TestWidget_FormatTableSeparator (line 123) | func TestWidget_FormatTableSeparator(t *testing.T) {
  function TestWidget_FormatTableRows (line 137) | func TestWidget_FormatTableRows(t *testing.T) {
  function TestWidget_FormatTableRows_WithTruncation (line 159) | func TestWidget_FormatTableRows_WithTruncation(t *testing.T) {
  function TestWidget_Content (line 178) | func TestWidget_Content(t *testing.T) {
  function TestCalculateAdaptiveColumnWidths (line 240) | func TestCalculateAdaptiveColumnWidths(t *testing.T) {
  function TestCalculateAdaptiveColumnWidths_Scaling (line 296) | func TestCalculateAdaptiveColumnWidths_Scaling(t *testing.T) {
  function createTestWidget (line 321) | func createTestWidget() *Widget {

FILE: modules/bamboohr/calendar.go
  type Calendar (line 3) | type Calendar struct
    method Holidays (line 9) | func (calendar *Calendar) Holidays() []Item {
    method ItemsByType (line 13) | func (calendar *Calendar) ItemsByType(itemType string) []Item {
    method TimeOffs (line 21) | func (calendar *Calendar) TimeOffs() []Item {
    method filteredItems (line 27) | func (calendar *Calendar) filteredItems(itemType string) []Item {

FILE: modules/bamboohr/client.go
  type Client (line 9) | type Client struct
    method Away (line 29) | func (client *Client) Away(itemType, startDate, endDate string) []Item {
    method getWhoIsAway (line 45) | func (client *Client) getWhoIsAway(startDate, endDate string) (cal Cal...
  function NewClient (line 16) | func NewClient(url string, apiKey string, subdomain string) *Client {

FILE: modules/bamboohr/employee.go
  type Employee (line 7) | type Employee struct

FILE: modules/bamboohr/item.go
  type Item (line 9) | type Item struct
    method String (line 17) | func (item *Item) String() string {
    method IsOneDay (line 23) | func (item *Item) IsOneDay() bool {
    method Name (line 27) | func (item *Item) Name() string {
    method PrettyStart (line 35) | func (item *Item) PrettyStart() string {
    method PrettyEnd (line 39) | func (item *Item) PrettyEnd() string {

FILE: modules/bamboohr/request.go
  function Request (line 8) | func Request(apiKey string, apiURL string) ([]byte, error) {
  function ParseBody (line 31) | func ParseBody(resp *http.Response) ([]byte, error) {

FILE: modules/bamboohr/settings.go
  constant defaultFocusable (line 11) | defaultFocusable = false
  constant defaultTitle (line 12) | defaultTitle     = "BambooHR"
  type Settings (line 15) | type Settings struct
  function NewSettingsFromYAML (line 22) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/bamboohr/widget.go
  constant apiURI (line 13) | apiURI = "https://api.bamboohr.com/api/gateway.php"
  type Widget (line 15) | type Widget struct
    method Refresh (line 34) | func (widget *Widget) Refresh() {
    method content (line 52) | func (widget *Widget) content() (string, string, bool) {
    method format (line 65) | func (widget *Widget) format(item Item) string {
  function NewWidget (line 22) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, settin...

FILE: modules/bargraph/settings.go
  constant defaultFocusable (line 9) | defaultFocusable = false
  constant defaultTitle (line 10) | defaultTitle     = "Bargraph"
  type Settings (line 13) | type Settings struct
  function NewSettingsFromYAML (line 17) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/bargraph/widget.go
  type Widget (line 17) | type Widget struct
    method Refresh (line 64) | func (widget *Widget) Refresh() {
  function NewWidget (line 24) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, settin...
  function MakeGraph (line 40) | func MakeGraph(widget *Widget) {

FILE: modules/buildkite/client.go
  type Pipeline (line 10) | type Pipeline struct
  type Build (line 14) | type Build struct
  method getBuilds (line 21) | func (widget *Widget) getBuilds() ([]Build, error) {
  method recentBuilds (line 38) | func (widget *Widget) recentBuilds(pipeline PipelineSettings) ([]Build, ...
  function branchesQuery (line 75) | func branchesQuery(branches []string) string {
  function mostRecentBuildForBranches (line 92) | func mostRecentBuildForBranches(builds []Build, branches []string) []Bui...

FILE: modules/buildkite/keyboard.go
  method initializeKeyboardControls (line 3) | func (widget *Widget) initializeKeyboardControls() {

FILE: modules/buildkite/pipelines_display_data.go
  type pipelinesDisplayData (line 9) | type pipelinesDisplayData struct
    method Content (line 14) | func (data *pipelinesDisplayData) Content() string {
  function newPipelinesDisplayData (line 29) | func newPipelinesDisplayData(builds []Build) pipelinesDisplayData {
  type ByBuild (line 62) | type ByBuild
    method Sort (line 64) | func (by ByBuild) Sort(builds []Build) {
  type buildSorter (line 72) | type buildSorter struct
    method Len (line 77) | func (bs *buildSorter) Len() int {
    method Swap (line 81) | func (bs *buildSorter) Swap(i, j int) {
    method Less (line 85) | func (bs *buildSorter) Less(i, j int) bool {
  function getLongestLength (line 89) | func getLongestLength(strs []string) int {
  function padRight (line 101) | func padRight(text string, length int) string {
  function buildColor (line 111) | func buildColor(state string) string {

FILE: modules/buildkite/settings.go
  constant defaultTitle (line 12) | defaultTitle     = "Buildkite"
  constant defaultFocusable (line 13) | defaultFocusable = true
  type PipelineSettings (line 17) | type PipelineSettings struct
  type Settings (line 23) | type Settings struct
  function NewSettingsFromYAML (line 32) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...
  function buildPipelineSettings (line 48) | func buildPipelineSettings(ymlConfig *config.Config) []PipelineSettings {

FILE: modules/buildkite/widget.go
  type Widget (line 10) | type Widget struct
    method Refresh (line 32) | func (widget *Widget) Refresh() {
    method display (line 49) | func (widget *Widget) display() {
    method content (line 53) | func (widget *Widget) content() (string, string, bool) {
  function NewWidget (line 18) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, pages ...

FILE: modules/cds/favorites/display.go
  method display (line 9) | func (widget *Widget) display() {
  method content (line 13) | func (widget *Widget) content() (string, string, bool) {
  method title (line 36) | func (widget *Widget) title(workflow *sdk.Workflow) string {
  method displayWorkflowRuns (line 44) | func (widget *Widget) displayWorkflowRuns(workflow *sdk.Workflow) string {
  function getStatusColor (line 76) | func getStatusColor(status string) string {

FILE: modules/cds/favorites/keyboard.go
  method initializeKeyboardControls (line 7) | func (widget *Widget) initializeKeyboardControls() {

FILE: modules/cds/favorites/settings.go
  constant defaultFocusable (line 12) | defaultFocusable = true
  constant defaultTitle (line 13) | defaultTitle     = "CDS Favorites"
  type Settings (line 17) | type Settings struct
  function NewSettingsFromYAML (line 27) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/cds/favorites/widget.go
  type Widget (line 15) | type Widget struct
    method SetItemCount (line 62) | func (widget *Widget) SetItemCount(items int) {
    method GetItemCount (line 67) | func (widget *Widget) GetItemCount() int {
    method GetSelected (line 72) | func (widget *Widget) GetSelected() int {
    method Next (line 80) | func (widget *Widget) Next() {
    method Prev (line 90) | func (widget *Widget) Prev() {
    method Unselect (line 100) | func (widget *Widget) Unselect() {
    method Refresh (line 107) | func (widget *Widget) Refresh() {
    method buildWorkflowsCollection (line 113) | func (widget *Widget) buildWorkflowsCollection() []sdk.Workflow {
    method currentCDSWorkflow (line 124) | func (widget *Widget) currentCDSWorkflow() *sdk.Workflow {
    method openWorkflow (line 137) | func (widget *Widget) openWorkflow() {
  function NewWidget (line 30) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, pages ...

FILE: modules/cds/queue/display.go
  method display (line 12) | func (widget *Widget) display() {
  method content (line 16) | func (widget *Widget) content() (string, string, bool) {
  method title (line 35) | func (widget *Widget) title(filter string) string {
  method displayQueue (line 44) | func (widget *Widget) displayQueue(filter string) string {
  method generateQueueJobLine (line 64) | func (widget *Widget) generateQueueJobLine(parameters []sdk.Parameter, e...
  function pad (line 100) | func pad(t string, size int) string {
  function getVarsInPbj (line 107) | func getVarsInPbj(key string, ps []sdk.Parameter) string {

FILE: modules/cds/queue/keyboard.go
  method initializeKeyboardControls (line 7) | func (widget *Widget) initializeKeyboardControls() {

FILE: modules/cds/queue/settings.go
  constant defaultFocusable (line 11) | defaultFocusable = true
  constant defaultTitle (line 12) | defaultTitle     = "CDS Queue"
  type Settings (line 16) | type Settings struct
  function NewSettingsFromYAML (line 25) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/cds/queue/widget.go
  type Widget (line 15) | type Widget struct
    method SetItemCount (line 62) | func (widget *Widget) SetItemCount(items int) {
    method GetItemCount (line 67) | func (widget *Widget) GetItemCount() int {
    method GetSelected (line 72) | func (widget *Widget) GetSelected() int {
    method Next (line 80) | func (widget *Widget) Next() {
    method Prev (line 90) | func (widget *Widget) Prev() {
    method Unselect (line 100) | func (widget *Widget) Unselect() {
    method Refresh (line 107) | func (widget *Widget) Refresh() {
    method currentFilter (line 113) | func (widget *Widget) currentFilter() string {
    method openWorkflow (line 126) | func (widget *Widget) openWorkflow() {
  function NewWidget (line 30) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, pages ...

FILE: modules/cds/status/display.go
  method display (line 10) | func (widget *Widget) display() {
  method content (line 14) | func (widget *Widget) content() (string, string, bool) {
  method displayStatus (line 27) | func (widget *Widget) displayStatus() string {

FILE: modules/cds/status/keyboard.go
  method initializeKeyboardControls (line 7) | func (widget *Widget) initializeKeyboardControls() {

FILE: modules/cds/status/settings.go
  constant defaultFocusable (line 11) | defaultFocusable = true
  constant defaultTitle (line 12) | defaultTitle     = "CDS Status"
  type Settings (line 16) | type Settings struct
  function NewSettingsFromYAML (line 25) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/cds/status/widget.go
  type Widget (line 15) | type Widget struct
    method SetItemCount (line 62) | func (widget *Widget) SetItemCount(items int) {
    method GetItemCount (line 67) | func (widget *Widget) GetItemCount() int {
    method GetSelected (line 72) | func (widget *Widget) GetSelected() int {
    method Next (line 80) | func (widget *Widget) Next() {
    method Prev (line 90) | func (widget *Widget) Prev() {
    method Unselect (line 100) | func (widget *Widget) Unselect() {
    method Refresh (line 107) | func (widget *Widget) Refresh() {
    method openWorkflow (line 113) | func (widget *Widget) openWorkflow() {
  function NewWidget (line 30) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, pages ...

FILE: modules/circleci/build.go
  type Build (line 3) | type Build struct

FILE: modules/circleci/client.go
  type Client (line 13) | type Client struct
    method BuildsFor (line 25) | func (client *Client) BuildsFor() ([]*Build, error) {
    method circleRequest (line 47) | func (client *Client) circleRequest(path string) ([]byte, error) {
  function NewClient (line 17) | func NewClient(apiKey string) *Client {

FILE: modules/circleci/settings.go
  constant defaultFocusable (line 11) | defaultFocusable = false
  constant defaultTitle (line 12) | defaultTitle     = "CircleCI"
  type Settings (line 15) | type Settings struct
  function NewSettingsFromYAML (line 22) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/circleci/widget.go
  type Widget (line 10) | type Widget struct
    method Refresh (line 30) | func (widget *Widget) Refresh() {
    method content (line 40) | func (widget *Widget) content() (string, string, bool) {
  function NewWidget (line 17) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, settin...
  function buildColor (line 69) | func buildColor(build *Build) string {

FILE: modules/clocks/clock.go
  type Clock (line 8) | type Clock struct
    method Date (line 30) | func (clock *Clock) Date(dateFormat string) string {
    method LocalTime (line 34) | func (clock *Clock) LocalTime() time.Time {
    method ToLocal (line 38) | func (clock *Clock) ToLocal(t time.Time) time.Time {
    method Time (line 42) | func (clock *Clock) Time(timeFormat string) string {
  function NewClock (line 13) | func NewClock(label string, timeLoc *time.Location) Clock {
  function BuildClock (line 22) | func BuildClock(label string, location string) (clock Clock, err error) {
  function sanitizeLocation (line 46) | func sanitizeLocation(locStr string) string {

FILE: modules/clocks/clock_collection.go
  type ClockCollection (line 8) | type ClockCollection struct
    method Sorted (line 12) | func (clocks *ClockCollection) Sorted(sortOrder string) []Clock {
    method SortedAlphabetically (line 28) | func (clocks *ClockCollection) SortedAlphabetically() {
    method SortedChronologically (line 37) | func (clocks *ClockCollection) SortedChronologically() {
    method SortedReverseChronologically (line 47) | func (clocks *ClockCollection) SortedReverseChronologically() {

FILE: modules/clocks/display.go
  method display (line 5) | func (widget *Widget) display(clocks []Clock, dateFormat string, timeFor...

FILE: modules/clocks/settings.go
  constant defaultFocusable (line 10) | defaultFocusable = false
  constant defaultTitle (line 11) | defaultTitle     = "Clocks"
  type Settings (line 15) | type Settings struct
  function NewSettingsFromYAML (line 25) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...
  function buildLocations (line 38) | func buildLocations(ymlConfig *config.Config) []Clock {

FILE: modules/clocks/widget.go
  type Widget (line 8) | type Widget struct
    method Refresh (line 34) | func (widget *Widget) Refresh() {
    method buildClockCollection (line 41) | func (widget *Widget) buildClockCollection() ClockCollection {
  function NewWidget (line 17) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, settin...

FILE: modules/cmdrunner/settings.go
  constant defaultFocusable (line 10) | defaultFocusable = true
  constant defaultTitle (line 11) | defaultTitle     = "CmdRunner"
  type Settings (line 15) | type Settings struct
  function NewSettingsFromYAML (line 32) | func NewSettingsFromYAML(name string, moduleConfig *config.Config, globa...

FILE: modules/cmdrunner/widget.go
  type Widget (line 22) | type Widget struct
    method Refresh (line 56) | func (widget *Widget) Refresh() {
    method String (line 68) | func (widget *Widget) String() string {
    method Write (line 78) | func (widget *Widget) Write(p []byte) (n int, err error) {
    method countLines (line 97) | func (widget *Widget) countLines() int {
    method drainLines (line 102) | func (widget *Widget) drainLines(n int) error {
    method environment (line 113) | func (widget *Widget) environment() []string {
    method handleError (line 188) | func (widget *Widget) handleError(err error) {
    method content (line 207) | func (widget *Widget) content() (string, string, bool) {
    method resetBuffer (line 221) | func (widget *Widget) resetBuffer() {
  function NewWidget (line 34) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, settin...
  function runCommandLoop (line 123) | func runCommandLoop(widget *Widget) {
  function runCommand (line 145) | func runCommand(widget *Widget, cmd *exec.Cmd) error {
  function runCommandPty (line 150) | func runCommandPty(widget *Widget, cmd *exec.Cmd) error {
  function redrawLoop (line 197) | func redrawLoop(widget *Widget) {

FILE: modules/cryptocurrency/bittrex/bittrex.go
  type summaryList (line 3) | type summaryList struct
    method addSummaryItem (line 43) | func (list *summaryList) addSummaryItem(name, displayName string, mark...
  type bCurrency (line 8) | type bCurrency struct
  type mCurrency (line 15) | type mCurrency struct
  type summaryInfo (line 20) | type summaryInfo struct
  type summaryResponse (line 29) | type summaryResponse struct

FILE: modules/cryptocurrency/bittrex/display.go
  method display (line 9) | func (widget *Widget) display() {
  method content (line 14) | func (widget *Widget) content() (string, string, bool) {
  function formatableText (line 73) | func formatableText(key, value string) string {

FILE: modules/cryptocurrency/bittrex/settings.go
  constant defaultFocusable (line 9) | defaultFocusable = false
  constant defaultTitle (line 10) | defaultTitle     = "Bittrex"
  type colors (line 13) | type colors struct
  type currency (line 25) | type currency struct
  type summary (line 30) | type summary struct
  type Settings (line 34) | type Settings struct
  function NewSettingsFromYAML (line 41) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/cryptocurrency/bittrex/widget.go
  constant baseURL (line 15) | baseURL = "https://bittrex.com/api/v1.1/public/getmarketsummary"
  type Widget (line 24) | type Widget struct
    method setSummaryList (line 48) | func (widget *Widget) setSummaryList() {
    method makeSummaryMarketList (line 55) | func (widget *Widget) makeSummaryMarketList(market []interface{}) []*m...
    method Refresh (line 82) | func (widget *Widget) Refresh() {
    method updateSummary (line 90) | func (widget *Widget) updateSummary() {
  function NewWidget (line 32) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, settin...
  function makeMarketCurrency (line 65) | func makeMarketCurrency(name string) *mCurrency {
  function makeRequest (line 151) | func makeRequest(baseName, marketName string) *http.Request {

FILE: modules/cryptocurrency/blockfolio/settings.go
  constant defaultFocusable (line 9) | defaultFocusable = false
  constant defaultTitle (line 10) | defaultTitle     = "Blockfolio"
  type colors (line 13) | type colors struct
  type Settings (line 19) | type Settings struct
  function NewSettingsFromYAML (line 28) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/cryptocurrency/blockfolio/widget.go
  type Widget (line 14) | type Widget struct
    method Refresh (line 34) | func (widget *Widget) Refresh() {
    method content (line 40) | func (widget *Widget) content() (string, string, bool) {
  function NewWidget (line 21) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, settin...
  constant magic (line 92) | magic = "edtopjhgn2345piuty89whqejfiobh89-2q453"
  type Position (line 94) | type Position struct
  type AllPositionsResponse (line 102) | type AllPositionsResponse struct
  function MakeApiRequest (line 106) | func MakeApiRequest(token string, method string) ([]byte, error) {
  function GetAllPositions (line 126) | func GetAllPositions(token string) (*AllPositionsResponse, error) {
  function Fetch (line 138) | func Fetch(token string) (*AllPositionsResponse, error) {

FILE: modules/cryptocurrency/cryptolive/price/price.go
  type list (line 3) | type list struct
    method addItem (line 22) | func (l *list) addItem(name string, displayName string, to []*toCurren...
  type fromCurrency (line 7) | type fromCurrency struct
  type toCurrency (line 13) | type toCurrency struct
  type cResponse (line 18) | type cResponse

FILE: modules/cryptocurrency/cryptolive/price/settings.go
  constant defaultFocusable (line 9) | defaultFocusable = false
  constant defaultTitle (line 10) | defaultTitle     = "CryptoLive"
  type colors (line 13) | type colors struct
  type currency (line 35) | type currency struct
  type Settings (line 40) | type Settings struct
  function NewSettingsFromYAML (line 47) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/cryptocurrency/cryptolive/price/widget.go
  type Widget (line 15) | type Widget struct
    method setList (line 35) | func (widget *Widget) setList() {
    method Refresh (line 47) | func (widget *Widget) Refresh(wg *sync.WaitGroup) {
    method display (line 61) | func (widget *Widget) display() {
    method getToList (line 87) | func (widget *Widget) getToList(symbol string) []*toCurrency {
    method updateCurrencies (line 100) | func (widget *Widget) updateCurrencies() {
  function NewWidget (line 25) | func NewWidget(settings *Settings) *Widget {
  function makeRequest (line 134) | func makeRequest(currency *fromCurrency) *http.Request {
  function setPrices (line 146) | func setPrices(response *cResponse, currencry *fromCurrency) {

FILE: modules/cryptocurrency/cryptolive/settings.go
  constant defaultFocusable (line 11) | defaultFocusable = false
  constant defaultTitle (line 12) | defaultTitle     = "CryptolLive"
  type colors (line 15) | type colors struct
  type Settings (line 37) | type Settings struct
  function NewSettingsFromYAML (line 49) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/cryptocurrency/cryptolive/toplist/display.go
  method display (line 5) | func (widget *Widget) display() {
  method makeToListText (line 21) | func (widget *Widget) makeToListText(toList []*tCurrency) string {
  method makeToText (line 30) | func (widget *Widget) makeToText(toCurrency *tCurrency) string {
  method makeInfoText (line 45) | func (widget *Widget) makeInfoText(info tInfo) string {

FILE: modules/cryptocurrency/cryptolive/toplist/settings.go
  constant defaultFocusable (line 9) | defaultFocusable = false
  constant defaultTitle (line 10) | defaultTitle     = "CryptoLive"
  type colors (line 13) | type colors struct
  type currency (line 35) | type currency struct
  type Settings (line 41) | type Settings struct
  function NewSettingsFromYAML (line 48) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/cryptocurrency/cryptolive/toplist/toplist.go
  type cList (line 3) | type cList struct
    method addItem (line 34) | func (list *cList) addItem(name, displayName string, limit int, to []*...
  type fCurrency (line 7) | type fCurrency struct
  type tCurrency (line 13) | type tCurrency struct
  type tInfo (line 18) | type tInfo struct
  type responseInterface (line 23) | type responseInterface struct

FILE: modules/cryptocurrency/cryptolive/toplist/widget.go
  type Widget (line 15) | type Widget struct
    method setList (line 36) | func (widget *Widget) setList() {
    method makeToList (line 43) | func (widget *Widget) makeToList(symbol string, limit int) (list []*tC...
    method Refresh (line 57) | func (widget *Widget) Refresh(wg *sync.WaitGroup) {
    method updateData (line 69) | func (widget *Widget) updateData() {
  function NewWidget (line 25) | func NewWidget(settings *Settings) *Widget {
  function makeRequest (line 106) | func makeRequest(fsym, tsym string, limit int) *http.Request {

FILE: modules/cryptocurrency/cryptolive/widget.go
  type Widget (line 14) | type Widget struct
    method Refresh (line 41) | func (widget *Widget) Refresh() {
    method content (line 54) | func (widget *Widget) content() (string, string, bool) {
  function NewWidget (line 23) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, settin...

FILE: modules/cryptocurrency/mempool/settings.go
  constant defaultFocusable (line 9) | defaultFocusable = false
  constant defaultTitle (line 10) | defaultTitle     = "mempool"
  type Settings (line 14) | type Settings struct
  function NewSettingsFromYAML (line 21) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/cryptocurrency/mempool/widget.go
  type Widget (line 14) | type Widget struct
    method Refresh (line 39) | func (widget *Widget) Refresh() {
    method content (line 46) | func (widget *Widget) content() string {
    method display (line 75) | func (widget *Widget) display() {
  function NewWidget (line 20) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, pages ...
  type feeStruct (line 29) | type feeStruct struct
  function getBTCTxFees (line 50) | func getBTCTxFees() string {

FILE: modules/datadog/client.go
  method Monitors (line 9) | func (widget *Widget) Monitors() ([]datadog.Monitor, error) {

FILE: modules/datadog/keyboard.go
  method initializeKeyboardControls (line 7) | func (widget *Widget) initializeKeyboardControls() {

FILE: modules/datadog/settings.go
  constant defaultFocusable (line 11) | defaultFocusable = true
  constant defaultTitle (line 12) | defaultTitle     = "DataDog"
  type Settings (line 15) | type Settings struct
  function NewSettingsFromYAML (line 23) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/datadog/widget.go
  type Widget (line 12) | type Widget struct
    method Refresh (line 35) | func (widget *Widget) Refresh() {
    method Render (line 60) | func (widget *Widget) Render() {
    method content (line 66) | func (widget *Widget) content() (string, string, bool) {
    method openItem (line 102) | func (widget *Widget) openItem() {
  function NewWidget (line 20) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, pages ...

FILE: modules/devto/keyboard.go
  method initializeKeyboardControls (line 5) | func (widget *Widget) initializeKeyboardControls() {

FILE: modules/devto/settings.go
  constant defaultFocusable (line 10) | defaultFocusable = true
  constant defaultTitle (line 11) | defaultTitle     = "dev.to | News Feed"
  type Settings (line 15) | type Settings struct
  function NewSettingsFromYAML (line 25) | func NewSettingsFromYAML(name string, yamlConfig *config.Config, globalC...

FILE: modules/devto/widget.go
  type Widget (line 14) | type Widget struct
    method Refresh (line 36) | func (widget *Widget) Refresh() {
    method Render (line 79) | func (widget *Widget) Render() {
    method content (line 85) | func (widget *Widget) content() (string, string, bool) {
    method openStory (line 113) | func (widget *Widget) openStory() {
  function NewWidget (line 22) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, pages ...

FILE: modules/digitalclock/clocks.go
  constant AM (line 10) | AM = "A"
  constant PM (line 13) | PM = "P"
  constant minRowsForBorder (line 14) | minRowsForBorder = 3
  function intStrConv (line 17) | func intStrConv(val int) string {
  function getHourMinute (line 27) | func getHourMinute(hourFormat string) string {
  function getColon (line 48) | func getColon() string {
  function getDate (line 55) | func getDate(dateFormat string, withDatePrefix bool) string {
  function getUTC (line 62) | func getUTC() string {
  function getEpoch (line 66) | func getEpoch() string {
  function renderClock (line 71) | func renderClock(widgetSettings Settings) (string, bool) {

FILE: modules/digitalclock/display.go
  function mergeLines (line 5) | func mergeLines(outString []string) string {
  function renderWidget (line 9) | func renderWidget(widgetSettings Settings) string {
  method display (line 34) | func (widget *Widget) display() {

FILE: modules/digitalclock/fonts.go
  type ClockFontInterface (line 9) | type ClockFontInterface interface
  type ClockFont (line 15) | type ClockFont struct
    method get (line 34) | func (font *ClockFont) get(char string) []string {
  function fontsJoin (line 21) | func fontsJoin(fontCharArray [][]string, rows int, color string) string {
  function getDigitalFont (line 38) | func getDigitalFont() ClockFont {
  function getBigFont (line 60) | func getBigFont() ClockFont {
  function getBoldFont (line 82) | func getBoldFont() ClockFont {
  function getFont (line 105) | func getFont(widgetSettings Settings) ClockFont {

FILE: modules/digitalclock/settings.go
  constant defaultFocusable (line 9) | defaultFocusable = false
  constant defaultTitle (line 10) | defaultTitle     = "Clocks"
  type Settings (line 14) | type Settings struct
  function NewSettingsFromYAML (line 30) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/digitalclock/widget.go
  type Widget (line 9) | type Widget struct
    method Refresh (line 32) | func (widget *Widget) Refresh() {
  function NewWidget (line 16) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, settin...

FILE: modules/digitalocean/display.go
  constant maxColWidth (line 9) | maxColWidth = 12
  method content (line 11) | func (widget *Widget) content() (string, string, bool) {
  method display (line 65) | func (widget *Widget) display() {

FILE: modules/digitalocean/droplet.go
  type Droplet (line 11) | type Droplet struct
    method StringValueForProperty (line 30) | func (drop *Droplet) StringValueForProperty(propName string) (string, ...
  function NewDroplet (line 19) | func NewDroplet(doDroplet godo.Droplet) *Droplet {

FILE: modules/digitalocean/droplet_properties_table.go
  type dropletPropertiesTable (line 12) | type dropletPropertiesTable struct
    method buildPropertyMap (line 39) | func (propTable *dropletPropertiesTable) buildPropertyMap() map[string...
    method render (line 68) | func (propTable *dropletPropertiesTable) render() string {
  function newDropletPropertiesTable (line 22) | func newDropletPropertiesTable(droplet *Droplet) *dropletPropertiesTable {

FILE: modules/digitalocean/keyboard.go
  method initializeKeyboardControls (line 5) | func (widget *Widget) initializeKeyboardControls() {

FILE: modules/digitalocean/settings.go
  constant defaultFocusable (line 13) | defaultFocusable = true
  constant defaultTitle (line 14) | defaultTitle     = "DigitalOcean"
  type Settings (line 26) | type Settings struct
  function NewSettingsFromYAML (line 35) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/digitalocean/widget.go
  type tokenSource (line 17) | type tokenSource struct
    method Token (line 22) | func (t *tokenSource) Token() (*oauth2.Token, error) {
  type Widget (line 32) | type Widget struct
    method Fetch (line 68) | func (widget *Widget) Fetch() error {
    method Next (line 79) | func (widget *Widget) Next() {
    method Prev (line 84) | func (widget *Widget) Prev() {
    method Refresh (line 89) | func (widget *Widget) Refresh() {
    method Unselect (line 103) | func (widget *Widget) Unselect() {
    method createClient (line 111) | func (widget *Widget) createClient() {
    method currentDroplet (line 122) | func (widget *Widget) currentDroplet() *Droplet {
    method dropletsFetch (line 135) | func (widget *Widget) dropletsFetch() ([]*Droplet, error) {
    method dropletDestroy (line 169) | func (widget *Widget) dropletDestroy() {
    method dropletEnabledPrivateNetworking (line 185) | func (widget *Widget) dropletEnabledPrivateNetworking() {
    method dropletRemoveSelected (line 200) | func (widget *Widget) dropletRemoveSelected() {
    method dropletRestart (line 209) | func (widget *Widget) dropletRestart() {
    method dropletShutDown (line 223) | func (widget *Widget) dropletShutDown() {
    method showInfo (line 239) | func (widget *Widget) showInfo() {
  function NewWidget (line 45) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, pages ...

FILE: modules/docker/client.go
  method getSystemInfo (line 15) | func (widget *Widget) getSystemInfo() string {
  method getContainerStates (line 111) | func (widget *Widget) getContainerStates() string {

FILE: modules/docker/settings.go
  constant defaultFocusable (line 9) | defaultFocusable = false
  constant defaultTitle (line 10) | defaultTitle     = "docker"
  type Settings (line 14) | type Settings struct
  function NewSettingsFromYAML (line 21) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/docker/utils.go
  function padSlice (line 10) | func padSlice(padLeft bool, slice interface{}, getter func(i int) string...

FILE: modules/docker/widget.go
  type Widget (line 12) | type Widget struct
    method Refresh (line 41) | func (widget *Widget) Refresh() {
    method display (line 48) | func (widget *Widget) display() (string, string, bool) {
    method refreshDisplayBuffer (line 52) | func (widget *Widget) refreshDisplayBuffer() {
  function NewWidget (line 19) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, pages ...

FILE: modules/feedreader/keyboard.go
  method initializeKeyboardControls (line 5) | func (widget *Widget) initializeKeyboardControls() {

FILE: modules/feedreader/settings.go
  constant defaultFocusable (line 10) | defaultFocusable = true
  constant defaultTitle (line 11) | defaultTitle     = "Feed Reader"
  type colors (line 14) | type colors struct
  type auth (line 20) | type auth struct
  type Settings (line 26) | type Settings struct
  function NewSettingsFromYAML (line 42) | func NewSettingsFromYAML(name string, ymlConfig, globalConfig *config.Co...

FILE: modules/feedreader/widget.go
  type ShowType (line 19) | type ShowType
  constant SHOW_TITLE (line 22) | SHOW_TITLE ShowType = iota
  constant SHOW_LINK (line 23) | SHOW_LINK
  constant SHOW_CONTENT (line 24) | SHOW_CONTENT
  type FeedItem (line 28) | type FeedItem struct
  type Widget (line 35) | type Widget struct
    method Fetch (line 96) | func (widget *Widget) Fetch(feedURLs []string) ([]*FeedItem, error) {
    method Refresh (line 114) | func (widget *Widget) Refresh() {
    method Render (line 130) | func (widget *Widget) Render() {
    method fetchForFeed (line 136) | func (widget *Widget) fetchForFeed(feedURL string) ([]*FeedItem, error) {
    method content (line 177) | func (widget *Widget) content() (string, string, bool) {
    method getShowText (line 214) | func (widget *Widget) getShowText(feedItem *FeedItem, rowColor string)...
    method sort (line 246) | func (widget *Widget) sort(feedItems []*FeedItem) []*FeedItem {
    method openStory (line 256) | func (widget *Widget) openStory() {
    method toggleDisplayText (line 267) | func (widget *Widget) toggleDisplayText() {
  function rotateShowType (line 45) | func rotateShowType(showtype ShowType) ShowType {
  function NewWidget (line 59) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, pages ...

FILE: modules/feedreader/widget_test.go
  function Test_getShowText (line 10) | func Test_getShowText(t *testing.T) {

FILE: modules/football/client.go
  type leagueInfo (line 12) | type leagueInfo struct
  type Client (line 17) | type Client struct
    method footballRequest (line 29) | func (client *Client) footballRequest(path string, id int) (*http.Resp...
  function NewClient (line 21) | func NewClient(apiKey string) *Client {

FILE: modules/football/settings.go
  constant defaultFocusable (line 11) | defaultFocusable = true
  constant defaultTitle (line 12) | defaultTitle     = "football"
  type Settings (line 15) | type Settings struct
  function NewSettingsFromYAML (line 26) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/football/types.go
  type Team (line 3) | type Team struct
  type LeagueStandings (line 7) | type LeagueStandings struct
  type Table (line 13) | type Table struct
  type LeagueFixtuers (line 24) | type LeagueFixtuers struct
  type Matches (line 28) | type Matches struct
  type Score (line 37) | type Score struct
  type ScoreByTime (line 43) | type ScoreByTime struct

FILE: modules/football/util.go
  function createTable (line 12) | func createTable(header []string, buf *bytes.Buffer) *tablewriter.Table {
  function parseDateString (line 27) | func parseDateString(d string) string {
  function getDateString (line 32) | func getDateString(offset int) string {

FILE: modules/football/widget.go
  type Widget (line 31) | type Widget struct
    method Refresh (line 63) | func (widget *Widget) Refresh() {
    method content (line 67) | func (widget *Widget) content() (string, string, bool) {
    method GetStandings (line 91) | func (widget *Widget) GetStandings(leagueId int) string {
    method GetMatches (line 130) | func (widget *Widget) GetMatches(leagueId int) string {
    method markFavorite (line 191) | func (widget *Widget) markFavorite(m *Matches) {
  function NewWidget (line 39) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, pages ...
  function getLeague (line 81) | func getLeague(league string) (leagueInfo, error) {

FILE: modules/gcal/cal_event.go
  type CalEvent (line 11) | type CalEvent struct
    method AllDay (line 25) | func (calEvent *CalEvent) AllDay() bool {
    method ConflictsWith (line 29) | func (calEvent *CalEvent) ConflictsWith(otherEvents []*CalEvent) bool {
    method Now (line 46) | func (calEvent *CalEvent) Now() bool {
    method Past (line 50) | func (calEvent *CalEvent) Past() bool {
    method ResponseFor (line 59) | func (calEvent *CalEvent) ResponseFor(email string) string {
    method End (line 71) | func (calEvent *CalEvent) End() time.Time {
    method Start (line 86) | func (calEvent *CalEvent) Start() time.Time {
    method Timestamp (line 101) | func (calEvent *CalEvent) Timestamp(hourFormat string, showEndTime boo...
  function NewCalEvent (line 15) | func NewCalEvent(event *calendar.Event) *CalEvent {

FILE: modules/gcal/client.go
  method Fetch (line 31) | func (widget *Widget) Fetch() ([]*CalEvent, error) {
  function fromMidnight (line 98) | func fromMidnight() time.Time {
  function getClient (line 105) | func getClient(ctx context.Context, config *oauth2.Config, name string) ...
  function isAuthenticated (line 118) | func isAuthenticated(name string) bool {
  method authenticate (line 127) | func (widget *Widget) authenticate() {
  function getTokenFromWeb (line 143) | func getTokenFromWeb(config *oauth2.Config) *oauth2.Token {
  function tokenCacheFile (line 162) | func tokenCacheFile(name string) (string, error) {
  function tokenFromFile (line 182) | func tokenFromFile(file string) (*oauth2.Token, error) {
  function saveToken (line 196) | func saveToken(file string, token *oauth2.Token) {
  method getCalendarIdList (line 210) | func (widget *Widget) getCalendarIdList(srv *calendar.Service) ([]string...

FILE: modules/gcal/display.go
  method display (line 12) | func (widget *Widget) display() {
  method content (line 16) | func (widget *Widget) content() (string, string, bool) {
  method dayDivider (line 75) | func (widget *Widget) dayDivider(event, prevEvent *CalEvent) string {
  method descriptionColor (line 100) | func (widget *Widget) descriptionColor(calEvent *CalEvent) string {
  method eventTimeColor (line 108) | func (widget *Widget) eventTimeColor() string {
  method eventSummary (line 112) | func (widget *Widget) eventSummary(calEvent *CalEvent, conflict bool) st...
  method timeUntil (line 132) | func (widget *Widget) timeUntil(calEvent *CalEvent) string {
  method titleColor (line 165) | func (widget *Widget) titleColor(calEvent *CalEvent) string {
  method location (line 188) | func (widget *Widget) location(calEvent *CalEvent) string {
  method responseIcon (line 204) | func (widget *Widget) responseIcon(calEvent *CalEvent) string {
  method removeDeclined (line 225) | func (widget *Widget) removeDeclined(events []*CalEvent) []*CalEvent {

FILE: modules/gcal/display_test.go
  function Test_display_content (line 11) | func Test_display_content(t *testing.T) {

FILE: modules/gcal/settings.go
  constant defaultFocusable (line 9) | defaultFocusable = true
  constant defaultTitle (line 10) | defaultTitle     = "Calendar"
  type colors (line 13) | type colors struct
  type Settings (line 24) | type Settings struct
  function NewSettingsFromYAML (line 45) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/gcal/widget.go
  type Widget (line 8) | type Widget struct
    method Disable (line 30) | func (widget *Widget) Disable() {
    method Refresh (line 34) | func (widget *Widget) Refresh() {
    method fetchAndDisplayEvents (line 46) | func (widget *Widget) fetchAndDisplayEvents() {
  function NewWidget (line 17) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, settin...

FILE: modules/gerrit/display.go
  method display (line 7) | func (widget *Widget) display() {
  method content (line 11) | func (widget *Widget) content() (string, string, bool) {
  method displayMyIncomingReviews (line 38) | func (widget *Widget) displayMyIncomingReviews(project *GerritProject) s...
  method displayMyOutgoingReviews (line 51) | func (widget *Widget) displayMyOutgoingReviews(project *GerritProject) s...
  method displayStats (line 64) | func (widget *Widget) displayStats(project *GerritProject) string {
  method rowColor (line 73) | func (widget *Widget) rowColor(idx int) string {
  method title (line 81) | func (widget *Widget) title(project *GerritProject) string {

FILE: modules/gerrit/gerrit_repo.go
  type GerritProject (line 9) | type GerritProject struct
    method Refresh (line 29) | func (project *GerritProject) Refresh(username string) {
    method countReviews (line 40) | func (project *GerritProject) countReviews(changes *[]glb.ChangeInfo) ...
    method myOutgoingReviews (line 51) | func (project *GerritProject) myOutgoingReviews(changes *[]glb.ChangeI...
    method myIncomingReviews (line 70) | func (project *GerritProject) myIncomingReviews(changes *[]glb.ChangeI...
    method loadChanges (line 90) | func (project *GerritProject) loadChanges() (*[]glb.ChangeInfo, error) {
  function NewGerritProject (line 19) | func NewGerritProject(path string, gerrit *glb.Client) *GerritProject {

FILE: modules/gerrit/keyboard.go
  method initializeKeyboardControls (line 7) | func (widget *Widget) initializeKeyboardControls() {

FILE: modules/gerrit/settings.go
  constant defaultFocusable (line 11) | defaultFocusable = true
  constant defaultTitle (line 12) | defaultTitle     = "Gerrit"
  type colors (line 15) | type colors struct
  type Settings (line 22) | type Settings struct
  function NewSettingsFromYAML (line 33) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/gerrit/widget.go
  type Widget (line 16) | type Widget struct
    method Refresh (line 51) | func (widget *Widget) Refresh() {
    method nextProject (line 93) | func (widget *Widget) nextProject() {
    method prevProject (line 103) | func (widget *Widget) prevProject() {
    method nextReview (line 112) | func (widget *Widget) nextReview() {
    method prevReview (line 122) | func (widget *Widget) prevReview() {
    method openReview (line 132) | func (widget *Widget) openReview() {
    method unselect (line 146) | func (widget *Widget) unselect() {
    method buildProjectCollection (line 151) | func (widget *Widget) buildProjectCollection(projectData []interface{}...
    method currentGerritProject (line 162) | func (widget *Widget) currentGerritProject() *GerritProject {
  function NewWidget (line 33) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, _ *tvi...

FILE: modules/git/display.go
  method display (line 9) | func (widget *Widget) display() {
  method content (line 13) | func (widget *Widget) content() (string, string, bool) {
  method formatChanges (line 61) | func (widget *Widget) formatChanges(data []string) string {
  method formatChange (line 75) | func (widget *Widget) formatChange(line string) string {
  method formatCommits (line 98) | func (widget *Widget) formatCommits(data []string) string {
  method formatCommit (line 108) | func (widget *Widget) formatCommit(line string) string {

FILE: modules/git/git_repo.go
  type GitRepo (line 11) | type GitRepo struct
    method branch (line 32) | func (repo *GitRepo) branch() string {
    method changedFiles (line 41) | func (repo *GitRepo) changedFiles() []string {
    method commits (line 52) | func (repo *GitRepo) commits(commitCount int, commitFormat, dateFormat...
    method repository (line 67) | func (repo *GitRepo) repository() string {
    method pull (line 74) | func (repo *GitRepo) pull() string {
    method checkout (line 81) | func (repo *GitRepo) checkout(branch string) string {
    method gitDir (line 88) | func (repo *GitRepo) gitDir() string {
    method workTree (line 92) | func (repo *GitRepo) workTree() string {
  function NewGitRepo (line 19) | func NewGitRepo(repoPath string, commitCount int, commitFormat, dateForm...

FILE: modules/git/keyboard.go
  method initializeKeyboardControls (line 5) | func (widget *Widget) initializeKeyboardControls() {

FILE: modules/git/settings.go
  constant defaultFocusable (line 10) | defaultFocusable = true
  constant defaultTitle (line 11) | defaultTitle     = "Git"
  type Settings (line 14) | type Settings struct
  function NewSettingsFromYAML (line 28) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...
  method ConfigText (line 51) | func (widget *Widget) ConfigText() string {

FILE: modules/git/variables.go
  constant __go_cmd (line 6) | __go_cmd = "git"

FILE: modules/git/variables_win.go
  constant __go_cmd (line 6) | __go_cmd = "git.exe"

FILE: modules/git/widget.go
  constant modalHeight (line 15) | modalHeight = 7
  constant modalWidth (line 16) | modalWidth  = 80
  constant offscreen (line 17) | offscreen   = -1000
  type Widget (line 20) | type Widget struct
    method Checkout (line 50) | func (widget *Widget) Checkout() {
    method Pull (line 67) | func (widget *Widget) Pull() {
    method Refresh (line 74) | func (widget *Widget) Refresh() {
    method addCheckoutButton (line 84) | func (widget *Widget) addCheckoutButton(form *tview.Form, fctn func()) {
    method addButtons (line 88) | func (widget *Widget) addButtons(form *tview.Form, checkoutFctn func()) {
    method addCancelButton (line 93) | func (widget *Widget) addCancelButton(form *tview.Form) {
    method modalFocus (line 104) | func (widget *Widget) modalFocus(form *tview.Form) {
    method modalForm (line 110) | func (widget *Widget) modalForm(lbl, text string) *tview.Form {
    method modalFrame (line 120) | func (widget *Widget) modalFrame(form *tview.Form) *tview.Frame {
    method currentData (line 138) | func (widget *Widget) currentData() *GitRepo {
    method gitRepos (line 150) | func (widget *Widget) gitRepos(repoPaths []string) []*GitRepo {
    method findGitRepositories (line 172) | func (widget *Widget) findGitRepositories(repositories []*GitRepo, dir...
  function NewWidget (line 31) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, pages ...

FILE: modules/github/display.go
  method display (line 9) | func (widget *Widget) display() {
  method content (line 13) | func (widget *Widget) content() (string, string, bool) {
  method displayMyPullRequests (line 57) | func (widget *Widget) displayMyPullRequests(repo *Repo, username string)...
  method displayCustomQuery (line 80) | func (widget *Widget) displayCustomQuery(repo *Repo, filter string, perP...
  method displayMyReviewRequests (line 107) | func (widget *Widget) displayMyReviewRequests(repo *Repo, username strin...
  method displayStats (line 124) | func (widget *Widget) displayStats(repo *Repo) string {
  method title (line 140) | func (widget *Widget) title(repo *Repo) string {
  method mergeString (line 156) | func (widget *Widget) mergeString(pr *ghb.PullRequest) string {

FILE: modules/github/github_repo.go
  constant pullRequestsPath (line 14) | pullRequestsPath = "/pulls"
  constant issuesPath (line 15) | issuesPath       = "/issues"
  type Repo (line 19) | type Repo struct
    method Open (line 46) | func (repo *Repo) Open() {
    method OpenPulls (line 51) | func (repo *Repo) OpenPulls() {
    method OpenIssues (line 56) | func (repo *Repo) OpenIssues() {
    method Refresh (line 61) | func (repo *Repo) Refresh() {
    method IssueCount (line 76) | func (repo *Repo) IssueCount() int {
    method PullRequestCount (line 87) | func (repo *Repo) PullRequestCount() int {
    method StarCount (line 92) | func (repo *Repo) StarCount() int {
    method isGitHubEnterprise (line 102) | func (repo *Repo) isGitHubEnterprise() bool {
    method oauthClient (line 112) | func (repo *Repo) oauthClient() *http.Client {
    method githubClient (line 120) | func (repo *Repo) githubClient() (*ghb.Client, error) {
    method myPullRequests (line 131) | func (repo *Repo) myPullRequests(username string, showStatus bool) []*...
    method individualPRs (line 152) | func (repo *Repo) individualPRs(prs []*ghb.PullRequest) []*ghb.PullReq...
    method myReviewRequests (line 173) | func (repo *Repo) myReviewRequests(username string) []*ghb.PullRequest {
    method customIssueQuery (line 187) | func (repo *Repo) customIssueQuery(filter string, perPage int) *ghb.Is...
    method loadPullRequests (line 202) | func (repo *Repo) loadPullRequests() ([]*ghb.PullRequest, error) {
    method loadRemoteRepository (line 220) | func (repo *Repo) loadRemoteRepository() (*ghb.Repository, error) {
  function NewGithubRepo (line 32) | func NewGithubRepo(name, owner, apiKey, baseURL, uploadURL string) *Repo {

FILE: modules/github/keyboard.go
  method initializeKeyboardControls (line 7) | func (widget *Widget) initializeKeyboardControls() {

FILE: modules/github/settings.go
  constant defaultFocusable (line 11) | defaultFocusable = true
  constant defaultTitle (line 12) | defaultTitle     = "GitHub"
  type Settings (line 16) | type Settings struct
  type customQuery (line 31) | type customQuery struct
  function NewSettingsFromYAML (line 38) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...
  function parseCustomQueries (line 62) | func parseCustomQueries(ymlConfig *config.Config) []customQuery {

FILE: modules/github/widget.go
  type Widget (line 13) | type Widget struct
    method SetItemCount (line 51) | func (widget *Widget) SetItemCount(items int) {
    method GetItemCount (line 56) | func (widget *Widget) GetItemCount() int {
    method GetSelected (line 61) | func (widget *Widget) GetSelected() int {
    method Next (line 69) | func (widget *Widget) Next() {
    method Prev (line 79) | func (widget *Widget) Prev() {
    method Unselect (line 89) | func (widget *Widget) Unselect() {
    method Refresh (line 96) | func (widget *Widget) Refresh() {
    method buildRepoCollection (line 106) | func (widget *Widget) buildRepoCollection(repoData []string) []*Repo {
    method currentGithubRepo (line 126) | func (widget *Widget) currentGithubRepo() *Repo {
    method openPr (line 138) | func (widget *Widget) openPr() {
    method openRepo (line 146) | func (widget *Widget) openRepo() {
    method openPulls (line 154) | func (widget *Widget) openPulls() {
    method openIssues (line 162) | func (widget *Widget) openIssues() {
  function NewWidget (line 26) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, pages ...

FILE: modules/gitlab/display.go
  method display (line 9) | func (widget *Widget) display() {
  method displayError (line 13) | func (widget *Widget) displayError() {
  method contentError (line 17) | func (widget *Widget) contentError() (string, string, bool) {
  method content (line 28) | func (widget *Widget) content() (string, string, bool) {
  method displayMyMergeRequests (line 61) | func (widget *Widget) displayMyMergeRequests(project *GitlabProject, use...
  method displayMyAssignedMergeRequests (line 66) | func (widget *Widget) displayMyAssignedMergeRequests(project *GitlabProj...
  method displayMyAssignedIssues (line 71) | func (widget *Widget) displayMyAssignedIssues(project *GitlabProject, us...
  method displayMyIssues (line 76) | func (widget *Widget) displayMyIssues(project *GitlabProject, username s...
  method renderMergeRequests (line 81) | func (widget *Widget) renderMergeRequests(mrs []*glab.BasicMergeRequest)...
  method renderIssues (line 101) | func (widget *Widget) renderIssues(issues []*glab.Issue) string {
  method displayStats (line 121) | func (widget *Widget) displayStats(project *GitlabProject) string {
  method title (line 132) | func (widget *Widget) title(project *GitlabProject) string {

FILE: modules/gitlab/gitlab_project.go
  type context (line 7) | type context struct
  function newContext (line 12) | func newContext(settings *Settings) (*context, error) {
  type GitlabProject (line 30) | type GitlabProject struct
    method Refresh (line 52) | func (project *GitlabProject) Refresh() {
    method IssueCount (line 63) | func (project *GitlabProject) IssueCount() int {
    method MergeRequestCount (line 71) | func (project *GitlabProject) MergeRequestCount() int {
    method StarCount (line 75) | func (project *GitlabProject) StarCount() int {
    method myMergeRequests (line 86) | func (project *GitlabProject) myMergeRequests() []*glab.BasicMergeRequ...
    method myAssignedMergeRequests (line 92) | func (project *GitlabProject) myAssignedMergeRequests() []*glab.BasicM...
    method myAssignedIssues (line 97) | func (project *GitlabProject) myAssignedIssues() []*glab.Issue {
    method myIssues (line 102) | func (project *GitlabProject) myIssues() []*glab.Issue {
    method loadMergeRequests (line 106) | func (project *GitlabProject) loadMergeRequests() ([]*glab.BasicMergeR...
    method loadAssignedMergeRequests (line 121) | func (project *GitlabProject) loadAssignedMergeRequests() ([]*glab.Bas...
    method loadAuthoredMergeRequests (line 137) | func (project *GitlabProject) loadAuthoredMergeRequests() ([]*glab.Bas...
    method loadAssignedIssues (line 153) | func (project *GitlabProject) loadAssignedIssues() ([]*glab.Issue, err...
    method loadAuthoredIssues (line 169) | func (project *GitlabProject) loadAuthoredIssues() ([]*glab.Issue, int...
    method loadRemoteProject (line 185) | func (project *GitlabProject) loadRemoteProject() (*glab.Project, erro...
  function NewGitlabProject (line 42) | func NewGitlabProject(context *context, projectPath string) *GitlabProje...

FILE: modules/gitlab/keyboard.go
  method initializeKeyboardControls (line 7) | func (widget *Widget) initializeKeyboardControls() {

FILE: modules/gitlab/settings.go
  constant defaultFocusable (line 11) | defaultFocusable = true
  constant defaultTitle (line 12) | defaultTitle     = "GitLab"
  type Settings (line 16) | type Settings struct
  function NewSettingsFromYAML (line 26) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/gitlab/widget.go
  type ContentItem (line 11) | type ContentItem struct
  type Widget (line 16) | type Widget struct
    method Refresh (line 58) | func (widget *Widget) Refresh() {
    method SetItemCount (line 72) | func (widget *Widget) SetItemCount(items int) {
    method GetItemCount (line 77) | func (widget *Widget) GetItemCount() int {
    method GetSelected (line 82) | func (widget *Widget) GetSelected() int {
    method Next (line 90) | func (widget *Widget) Next() {
    method Prev (line 100) | func (widget *Widget) Prev() {
    method Unselect (line 110) | func (widget *Widget) Unselect() {
    method buildProjectCollection (line 118) | func (widget *Widget) buildProjectCollection(context *context, project...
    method currentGitlabProject (line 129) | func (widget *Widget) currentGitlabProject() *GitlabProject {
    method openItemInBrowser (line 141) | func (widget *Widget) openItemInBrowser() {
    method openRepo (line 165) | func (widget *Widget) openRepo() {
    method openPulls (line 174) | func (widget *Widget) openPulls() {
    method openIssues (line 183) | func (widget *Widget) openIssues() {
  function NewWidget (line 32) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, pages ...

FILE: modules/gitlabtodo/keyboard.go
  method initializeKeyboardControls (line 5) | func (widget *Widget) initializeKeyboardControls() {

FILE: modules/gitlabtodo/settings.go
  constant defaultFocusable (line 11) | defaultFocusable = true
  constant defaultTitle (line 12) | defaultTitle     = "GitLab Todos"
  type Settings (line 15) | type Settings struct
  function NewSettingsFromYAML (line 24) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/gitlabtodo/widget.go
  type Widget (line 13) | type Widget struct
    method Refresh (line 39) | func (widget *Widget) Refresh() {
    method Render (line 53) | func (widget *Widget) Render() {
    method content (line 59) | func (widget *Widget) content() (string, string, bool) {
    method getTodos (line 75) | func (widget *Widget) getTodos() ([]*glab.Todo, error) {
    method trimTodoBody (line 87) | func (widget *Widget) trimTodoBody(body string) string {
    method contentFrom (line 100) | func (widget *Widget) contentFrom(todos []*glab.Todo) string {
    method markAsDone (line 121) | func (widget *Widget) markAsDone() {
    method openTodo (line 132) | func (widget *Widget) openTodo() {
  function NewWidget (line 22) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, pages ...

FILE: modules/gitter/client.go
  function GetMessages (line 11) | func GetMessages(roomId string, numberOfMessages int, apiToken string) (...
  function GetRoom (line 27) | func GetRoom(roomUri, apiToken string) (*Room, error) {
  function apiRequest (line 55) | func apiRequest(path, apiToken string) (*http.Response, error) {

FILE: modules/gitter/gitter.go
  type Rooms (line 5) | type Rooms struct
  type Room (line 9) | type Room struct
  type User (line 15) | type User struct
  type Message (line 21) | type Message struct

FILE: modules/gitter/keyboard.go
  method initializeKeyboardControls (line 5) | func (widget *Widget) initializeKeyboardControls() {

FILE: modules/gitter/settings.go
  constant defaultFocusable (line 11) | defaultFocusable = true
  constant defaultTitle (line 12) | defaultTitle     = "Gitter"
  type Settings (line 15) | type Settings struct
  function NewSettingsFromYAML (line 23) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/gitter/widget.go
  type Widget (line 12) | type Widget struct
    method Refresh (line 35) | func (widget *Widget) Refresh() {
    method display (line 65) | func (widget *Widget) display() {
    method content (line 69) | func (widget *Widget) content() (string, string, bool) {
  function NewWidget (line 20) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, pages ...

FILE: modules/googleanalytics/client.go
  type websiteReport (line 19) | type websiteReport struct
  method fetch (line 25) | func (widget *Widget) fetch() []websiteReport {
  function buildNetClient (line 50) | func buildNetClient(secretPath string) *http.Client {
  function makeReportServiceV3 (line 64) | func makeReportServiceV3(secretPath string) (*gaV3.Service, error) {
  function makeReportServiceV4 (line 75) | func makeReportServiceV4(secretPath string) (*gaV4.Service, error) {
  function getReports (line 85) | func getReports(
  function getLiveCount (line 128) | func getLiveCount(service *gaV3.Service, viewID string) *gaV3.RealtimeDa...

FILE: modules/googleanalytics/display.go
  method createTable (line 9) | func (widget *Widget) createTable(websiteReports []websiteReport) string {
  method createHeader (line 72) | func (widget *Widget) createHeader() string {

FILE: modules/googleanalytics/settings.go
  constant defaultFocusable (line 9) | defaultFocusable = false
  constant defaultTitle (line 10) | defaultTitle     = "Google Analytics"
  type Settings (line 13) | type Settings struct
  function NewSettingsFromYAML (line 22) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/googleanalytics/widget.go
  type Widget (line 8) | type Widget struct
    method Refresh (line 24) | func (widget *Widget) Refresh() {
  function NewWidget (line 14) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, settin...

FILE: modules/grafana/client.go
  type AlertState (line 14) | type AlertState
    method MarshalJSON (line 41) | func (s AlertState) MarshalJSON() ([]byte, error) {
    method UnmarshalJSON (line 49) | func (s *AlertState) UnmarshalJSON(b []byte) error {
  constant Alerting (line 17) | Alerting AlertState = iota
  constant Pending (line 18) | Pending
  constant NoData (line 19) | NoData
  constant Paused (line 20) | Paused
  constant Ok (line 21) | Ok
  type Alert (line 60) | type Alert struct
  type Client (line 66) | type Client struct
    method Alerts (line 78) | func (client *Client) Alerts() ([]Alert, error) {
  function NewClient (line 71) | func NewClient(settings *Settings) *Client {

FILE: modules/grafana/display.go
  method content (line 5) | func (widget *Widget) content() (string, string, bool) {
  function stateColor (line 26) | func stateColor(state AlertState) string {
  function stateToEmoji (line 43) | func stateToEmoji(state AlertState) string {

FILE: modules/grafana/keyboard.go
  method initializeKeyboardControls (line 5) | func (widget *Widget) initializeKeyboardControls() {

FILE: modules/grafana/settings.go
  constant defaultFocusable (line 13) | defaultFocusable = true
  constant defaultTitle (line 14) | defaultTitle     = "Grafana"
  type Settings (line 17) | type Settings struct
  function NewSettingsFromYAML (line 24) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/grafana/widget.go
  type Widget (line 12) | type Widget struct
    method Refresh (line 41) | func (widget *Widget) Refresh() {
    method GetSelected (line 55) | func (widget *Widget) GetSelected() int {
    method Next (line 63) | func (widget *Widget) Next() {
    method Prev (line 73) | func (widget *Widget) Prev() {
    method Unselect (line 83) | func (widget *Widget) Unselect() {
    method openAlert (line 91) | func (widget *Widget) openAlert() {
  function NewWidget (line 23) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, _ *tvi...

FILE: modules/gspreadsheets/client.go
  method Fetch (line 28) | func (widget *Widget) Fetch() ([]*sheets.ValueRange, error) {
  function getClient (line 70) | func getClient(ctx context.Context, config *oauth2.Config) *http.Client {
  function getTokenFromWeb (line 85) | func getTokenFromWeb(config *oauth2.Config) *oauth2.Token {
  function tokenCacheFile (line 104) | func tokenCacheFile() (string, error) {
  function tokenFromFile (line 121) | func tokenFromFile(file string) (*oauth2.Token, error) {
  function saveToken (line 134) | func saveToken(file string, token *oauth2.Token) {

FILE: modules/gspreadsheets/settings.go
  constant defaultFocusable (line 9) | defaultFocusable = false
  constant defaultTitle (line 10) | defaultTitle     = "Google Spreadsheets"
  type colors (line 13) | type colors struct
  type Settings (line 17) | type Settings struct
  function NewSettingsFromYAML (line 27) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/gspreadsheets/widget.go
  type Widget (line 12) | type Widget struct
    method Refresh (line 32) | func (widget *Widget) Refresh() {
    method content (line 42) | func (widget *Widget) content() (string, string, bool) {
  function NewWidget (line 20) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, settin...

FILE: modules/hackernews/client.go
  function GetStories (line 14) | func GetStories(storyType string) ([]int, error) {
  function GetStory (line 33) | func GetStory(id int) (Story, error) {
  function apiRequest (line 55) | func apiRequest(path string) ([]byte, error) {

FILE: modules/hackernews/keyboard.go
  method initializeKeyboardControls (line 5) | func (widget *Widget) initializeKeyboardControls() {

FILE: modules/hackernews/settings.go
  constant defaultFocusable (line 9) | defaultFocusable = true
  constant defaultTitle (line 10) | defaultTitle     = "HackerNews"
  type Settings (line 13) | type Settings struct
  function NewSettingsFromYAML (line 20) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/hackernews/story.go
  constant hnStoryPath (line 6) | hnStoryPath = "https://news.ycombinator.com/item?id="
  type Story (line 10) | type Story struct
    method CommentLink (line 23) | func (story *Story) CommentLink() string {
    method Link (line 29) | func (story *Story) Link() string {

FILE: modules/hackernews/story_test.go
  function Test_CommentLink (line 9) | func Test_CommentLink(t *testing.T) {
  function Test_Link (line 17) | func Test_Link(t *testing.T) {

FILE: modules/hackernews/widget.go
  type Widget (line 13) | type Widget struct
    method Refresh (line 36) | func (widget *Widget) Refresh() {
    method Render (line 62) | func (widget *Widget) Render() {
    method content (line 68) | func (widget *Widget) content() (string, string, bool) {
    method openComments (line 97) | func (widget *Widget) openComments() {
    method openStory (line 104) | func (widget *Widget) openStory() {
    method selectedStory (line 111) | func (widget *Widget) selectedStory() *Story {
  function NewWidget (line 21) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, pages ...

FILE: modules/healthchecks/keyboard.go
  method initializeKeyboardControls (line 3) | func (widget *Widget) initializeKeyboardControls() {

FILE: modules/healthchecks/settings.go
  constant defaultFocusable (line 12) | defaultFocusable = true
  constant defaultTitle (line 13) | defaultTitle     = "Healthchecks.io"
  type Settings (line 16) | type Settings struct
  function NewSettingsFromYAML (line 24) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/healthchecks/widget.go
  constant userAgent (line 15) | userAgent = "WTFUtil"
  type Widget (line 18) | type Widget struct
    method Refresh (line 63) | func (widget *Widget) Refresh() {
    method Render (line 76) | func (widget *Widget) Render() {
    method content (line 82) | func (widget *Widget) content() (string, string, bool) {
    method contentFrom (line 105) | func (widget *Widget) contentFrom(checks []Checks) string {
    method getExistingChecks (line 156) | func (widget *Widget) getExistingChecks() ([]Checks, error) {
  type Health (line 25) | type Health struct
  type Checks (line 29) | type Checks struct
  function NewWidget (line 49) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, pages ...
  function timeSincePing (line 134) | func timeSincePing(ts time.Time) string {
  function makeURL (line 139) | func makeURL(baseurl string, path string, tags []string) (string, error) {

FILE: modules/hibp/client.go
  constant apiURL (line 13) | apiURL            = "https://haveibeenpwned.com/api/v3/breachedaccount/"
  constant clientTimeoutSecs (line 14) | clientTimeoutSecs = 2
  constant userAgent (line 15) | userAgent         = "WTFUtil"
  type hibpError (line 18) | type hibpError struct
  method fullURL (line 23) | func (widget *Widget) fullURL(account string, truncated bool) string {
  method fetchForAccount (line 32) | func (widget *Widget) fetchForAccount(account string, since string) (*St...
  method parseResponseBody (line 74) | func (widget *Widget) parseResponseBody(account string, body []byte) (*S...
  method filterBreaches (line 94) | func (widget *Widget) filterBreaches(breaches []Breach) []Breach {
  method validateHTTPResponse (line 126) | func (widget *Widget) validateHTTPResponse(responseCode int, body []byte...

FILE: modules/hibp/hibp_breach.go
  type Breach (line 6) | type Breach struct
    method BreachDate (line 12) | func (br *Breach) BreachDate() (time.Time, error) {

FILE: modules/hibp/hibp_status.go
  type Status (line 4) | type Status struct
    method HasBeenCompromised (line 21) | func (stat *Status) HasBeenCompromised() bool {
    method Len (line 26) | func (stat *Status) Len() int {
  function NewStatus (line 10) | func NewStatus(acct string, breaches []Breach) *Status {

FILE: modules/hibp/settings.go
  constant defaultFocusable (line 13) | defaultFocusable   = false
  constant defaultTitle (line 14) | defaultTitle       = "HIBP"
  constant minRefreshInterval (line 15) | minRefreshInterval = 6 * time.Hour
  type colors (line 18) | type colors struct
  type Settings (line 24) | type Settings struct
    method HasSince (line 58) | func (sett *Settings) HasSince() bool {
    method SinceDate (line 68) | func (sett *Settings) SinceDate() (time.Time, error) {
  function NewSettingsFromYAML (line 34) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/hibp/widget.go
  type Widget (line 11) | type Widget struct
    method Fetch (line 33) | func (widget *Widget) Fetch(accounts []string) ([]*Status, error) {
    method Refresh (line 49) | func (widget *Widget) Refresh() {
    method content (line 65) | func (widget *Widget) content() (string, string, bool) {
    method sinceDateForTitle (line 89) | func (widget *Widget) sinceDateForTitle() string {
  function NewWidget (line 20) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, settin...

FILE: modules/ipaddresses/ipapi/settings.go
  constant defaultFocusable (line 9) | defaultFocusable = false
  constant defaultTitle (line 10) | defaultTitle     = "IP API"
  type colors (line 13) | type colors struct
  type Settings (line 18) | type Settings struct
  function NewSettingsFromYAML (line 24) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/ipaddresses/ipapi/widget.go
  type Widget (line 18) | type Widget struct
    method Refresh (line 82) | func (widget *Widget) Refresh() {
    method ipinfo (line 89) | func (widget *Widget) ipinfo() {
    method setResult (line 113) | func (widget *Widget) setResult(info *ipinfo) {
  type ipinfo (line 25) | type ipinfo struct
  function NewWidget (line 69) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, settin...
  function formatableText (line 164) | func formatableText(key, value string) string {

FILE: modules/ipaddresses/ipinfo/settings.go
  constant defaultFocusable (line 11) | defaultFocusable                 = false
  constant defaultTitle (line 12) | defaultTitle                     = "IPInfo"
  constant ipV4 (line 13) | ipV4             protocolVersion = "v4"
  constant ipV6 (line 14) | ipV6             protocolVersion = "v6"
  constant auto (line 15) | auto             protocolVersion = "auto"
  type protocolVersion (line 18) | type protocolVersion
    method String (line 20) | func (pv protocolVersion) String() string {
  function newProtocolVersion (line 31) | func newProtocolVersion(str string) (protocolVersion, error) {
  type Settings (line 44) | type Settings struct
  function NewSettingsFromYAML (line 51) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/ipaddresses/ipinfo/widget.go
  type Widget (line 16) | type Widget struct
    method Refresh (line 46) | func (widget *Widget) Refresh() {
    method ipinfo (line 53) | func (widget *Widget) ipinfo() {
    method setResult (line 90) | func (widget *Widget) setResult(info *ipinfo) {
  type ipinfo (line 23) | type ipinfo struct
  function NewWidget (line 34) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, settin...
  function formatableText (line 123) | func formatableText(key, value string) string {
  function getMyIP (line 131) | func getMyIP(version protocolVersion) (ip net.IP, v6 bool) {
  method toNetwork (line 148) | func (pv protocolVersion) toNetwork() string {

FILE: modules/jenkins/client.go
  method Create (line 13) | func (widget *Widget) Create(jenkinsURL string, username string, apiKey ...
  function ensureLastSlash (line 66) | func ensureLastSlash(url string) string {

FILE: modules/jenkins/job.go
  type Job (line 3) | type Job struct

FILE: modules/jenkins/keyboard.go
  method initializeKeyboardControls (line 5) | func (widget *Widget) initializeKeyboardControls() {

FILE: modules/jenkins/settings.go
  constant defaultFocusable (line 11) | defaultFocusable = true
  constant defaultTitle (line 12) | defaultTitle     = "Jenkins"
  type Settings (line 15) | type Settings struct
  function NewSettingsFromYAML (line 26) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/jenkins/view.go
  type View (line 3) | type View struct

FILE: modules/jenkins/widget.go
  type Widget (line 12) | type Widget struct
    method Refresh (line 35) | func (widget *Widget) Refresh() {
    method Render (line 59) | func (widget *Widget) Render() {
    method content (line 63) | func (widget *Widget) content() (string, string, bool) {
    method jobColor (line 90) | func (widget *Widget) jobColor(job Job) string {
    method openJob (line 102) | func (widget *Widget) openJob() {
  function NewWidget (line 20) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, pages ...

FILE: modules/jira/client.go
  type UserIDCache (line 19) | type UserIDCache struct
  type UserIDCacheMap (line 25) | type UserIDCacheMap struct
    method Get (line 59) | func (c *UserIDCacheMap) Get(username string) (string, bool) {
    method Set (line 83) | func (c *UserIDCacheMap) Set(username, accountID string, duration time...
    method Clear (line 94) | func (c *UserIDCacheMap) Clear() {
  type JQLConversionRequest (line 36) | type JQLConversionRequest struct
  type JQLConversionResponse (line 41) | type JQLConversionResponse struct
  type ConvertedQuery (line 46) | type ConvertedQuery struct
  type UserMessage (line 53) | type UserMessage struct
  method ConvertJQLWithUsername (line 107) | func (widget *Widget) ConvertJQLWithUsername(username string) (string, e...
  function extractAccountIDFromJQL (line 159) | func extractAccountIDFromJQL(jql string) string {
  method IssuesFor (line 178) | func (widget *Widget) IssuesFor(username string, projects []string, jql ...
  method searchWithNewAPI (line 211) | func (widget *Widget) searchWithNewAPI(jql string) (*SearchResult, error) {
  method getIssueByID (line 262) | func (widget *Widget) getIssueByID(issueID string) (*Issue, error) {
  function buildJql (line 279) | func buildJql(key string, value string) string {
  method jiraRequest (line 285) | func (widget *Widget) jiraRequest(path string) ([]byte, error) {
  method jiraPostRequest (line 326) | func (widget *Widget) jiraPostRequest(path string, data []byte) ([]byte,...
  function getProjectQuery (line 369) | func getProjectQuery(projects []string) string {

FILE: modules/jira/client_test.go
  function TestUserIDCacheMap_SetAndGet (line 13) | func TestUserIDCacheMap_SetAndGet(t *testing.T) {
  function TestUserIDCacheMap_GetNonExistent (line 31) | func TestUserIDCacheMap_GetNonExistent(t *testing.T) {
  function TestUserIDCacheMap_GetExpired (line 42) | func TestUserIDCacheMap_GetExpired(t *testing.T) {
  function TestUserIDCacheMap_Clear (line 64) | func TestUserIDCacheMap_Clear(t *testing.T) {
  function TestExtractAccountIDFromJQL (line 87) | func TestExtractAccountIDFromJQL(t *testing.T) {
  function TestConvertJQLWithUsername_CacheHit (line 128) | func TestConvertJQLWithUsername_CacheHit(t *testing.T) {
  function TestConvertJQLWithUsername_APICalls (line 149) | func TestConvertJQLWithUsername_APICalls(t *testing.T) {
  function TestConvertJQLWithUsername_APIError (line 197) | func TestConvertJQLWithUsername_APIError(t *testing.T) {
  function TestConvertJQLWithUsername_EmptyResponse (line 224) | func TestConvertJQLWithUsername_EmptyResponse(t *testing.T) {
  function TestConvertJQLWithUsername_InvalidAccountID (line 255) | func TestConvertJQLWithUsername_InvalidAccountID(t *testing.T) {

FILE: modules/jira/issues.go
  type Issue (line 3) | type Issue struct
  type IssueFields (line 12) | type IssueFields struct
  type IssueType (line 19) | type IssueType struct
  type IssueStatus (line 28) | type IssueStatus struct

FILE: modules/jira/keyboard.go
  method initializeKeyboardControls (line 7) | func (widget *Widget) initializeKeyboardControls() {

FILE: modules/jira/search_result.go
  type SearchResult (line 3) | type SearchResult struct

FILE: modules/jira/settings.go
  constant defaultFocusable (line 11) | defaultFocusable = true
  constant defaultTitle (line 12) | defaultTitle     = "Jira"
  type colors (line 15) | type colors struct
  type Settings (line 22) | type Settings struct
    method arrayifyProjects (line 64) | func (settings *Settings) arrayifyProjects(ymlConfig *config.Config) [...
  function NewSettingsFromYAML (line 36) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/jira/widget.go
  type Widget (line 11) | type Widget struct
    method Refresh (line 34) | func (widget *Widget) Refresh() {
    method Render (line 53) | func (widget *Widget) Render() {
    method openItem (line 59) | func (widget *Widget) openItem() {
    method content (line 70) | func (widget *Widget) content() (string, string, bool) {
    method issueTypeColor (line 138) | func (*Widget) issueTypeColor(issue *Issue) string {
  function NewWidget (line 19) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, pages ...
  constant MaxIssueTypeLength (line 67) | MaxIssueTypeLength = 7
  constant MaxStatusNameLength (line 68) | MaxStatusNameLength = 14
  function getLongestColumnLengths (line 106) | func getLongestColumnLengths(issues []Issue) (int, int, int) {
  function trimToMaxLength (line 151) | func trimToMaxLength(text string, maxLength int) string {

FILE: modules/krisinformation/client.go
  constant krisinformationAPI (line 16) | krisinformationAPI = "https://api.krisinformation.se/v2/feed?format=json"
  type Krisinformation (line 19) | type Krisinformation
  type Client (line 45) | type Client struct
    method getKrisinformation (line 81) | func (c *Client) getKrisinformation() (items []Item, err error) {
  type Item (line 54) | type Item struct
  function NewClient (line 65) | func NewClient(latitude, longitude float64, radius int, county string, c...
  function hsin (line 166) | func hsin(theta float64) float64 {
  function DistanceInMeters (line 179) | func DistanceInMeters(lat1, lon1, lat2, lon2 float64) float64 {

FILE: modules/krisinformation/settings.go
  constant defaultFocusable (line 9) | defaultFocusable = false
  constant defaultTitle (line 10) | defaultTitle     = "Krisinformation"
  constant defaultRadius (line 11) | defaultRadius    = -1
  constant defaultCountry (line 12) | defaultCountry   = true
  constant defaultCounty (line 13) | defaultCounty    = ""
  constant defaultMaxItems (line 14) | defaultMaxItems  = -1
  constant defaultMaxAge (line 15) | defaultMaxAge    = 720
  type Settings (line 19) | type Settings struct
  function NewSettingsFromYAML (line 31) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/krisinformation/widget.go
  type Widget (line 12) | type Widget struct
    method Refresh (line 41) | func (widget *Widget) Refresh() {
    method content (line 51) | func (widget *Widget) content() (string, string, bool) {
    method display (line 82) | func (widget *Widget) display() {
  function NewWidget (line 22) | func NewWidget(app *tview.Application, redrawChan chan bool, settings *S...
  function handleError (line 88) | func handleError(widget *Widget, err error) {

FILE: modules/kubernetes/client.go
  type clientInstance (line 10) | type clientInstance struct
  method getInstance (line 15) | func (widget *Widget) getInstance() (*clientInstance, error) {
  method getKubeClient (line 27) | func (widget *Widget) getKubeClient() (kubernetes.Interface, error) {

FILE: modules/kubernetes/settings.go
  constant defaultFocusable (line 10) | defaultFocusable = false
  constant defaultTitle (line 11) | defaultTitle     = "Kubernetes"
  type Settings (line 14) | type Settings struct
  function NewSettingsFromYAML (line 24) | func NewSettingsFromYAML(name string, moduleConfig *config.Config, globa...

FILE: modules/kubernetes/widget.go
  type Widget (line 16) | type Widget struct
    method Refresh (line 49) | func (widget *Widget) Refresh() {
    method generateTitle (line 105) | func (widget *Widget) generateTitle() string {
  function NewWidget (line 31) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, settin...
  method getPods (line 124) | func (client *clientInstance) getPods(namespaces []string) ([]string, er...
  method getDeployments (line 160) | func (client *clientInstance) getDeployments(namespaces []string) ([]str...
  method getNodes (line 194) | func (client *clientInstance) getNodes() ([]string, error) {

FILE: modules/kubernetes/widget_test.go
  function Test_generateTitle (line 9) | func Test_generateTitle(t *testing.T) {

FILE: modules/logger/settings.go
  constant defaultFocusable (line 9) | defaultFocusable = true
  constant defaultTitle (line 10) | defaultTitle     = "Logger"
  type Settings (line 13) | type Settings struct
  function NewSettingsFromYAML (line 17) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/logger/widget.go
  constant maxBufferSize (line 14) | maxBufferSize int64 = 1024
  type Widget (line 17) | type Widget struct
    method Refresh (line 36) | func (widget *Widget) Refresh() {
    method content (line 42) | func (widget *Widget) content() (string, string, bool) {
    method tailFile (line 66) | func (widget *Widget) tailFile() []string {
  function NewWidget (line 24) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, settin...

FILE: modules/lunarphase/keyboard.go
  method initializeKeyboardControls (line 5) | func (widget *Widget) initializeKeyboardControls() {

FILE: modules/lunarphase/settings.go
  constant defaultFocusable (line 9) | defaultFocusable = true
  constant defaultTitle (line 10) | defaultTitle     = "Phase of the Moon"
  constant dateFormat (line 11) | dateFormat       = "2006-01-02"
  constant phaseFormat (line 12) | phaseFormat      = "01-02-2006"
  type Settings (line 15) | type Settings struct
  function NewSettingsFromYAML (line 22) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/lunarphase/widget.go
  type Widget (line 15) | type Widget struct
    method Refresh (line 47) | func (widget *Widget) Refresh() {
    method RefreshTitle (line 67) | func (widget *Widget) RefreshTitle() {
    method lunarPhase (line 80) | func (widget *Widget) lunarPhase() {
    method NextDay (line 113) | func (widget *Widget) NextDay() {
    method NextWeek (line 120) | func (widget *Widget) NextWeek() {
    method PrevDay (line 127) | func (widget *Widget) PrevDay() {
    method Today (line 134) | func (widget *Widget) Today() {
    method PrevWeek (line 140) | func (widget *Widget) PrevWeek() {
    method setDay (line 146) | func (widget *Widget) setDay(ts time.Time) {
    method OpenMoonPhase (line 153) | func (widget *Widget) OpenMoonPhase() {
    method DisableWidget (line 159) | func (widget *Widget) DisableWidget() {
  function NewWidget (line 28) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, pages ...

FILE: modules/mercurial/display.go
  method display (line 9) | func (widget *Widget) display() {
  method content (line 13) | func (widget *Widget) content() (string, string, bool) {
  method formatChanges (line 37) | func (widget *Widget) formatChanges(data []string) string {
  method formatChange (line 51) | func (widget *Widget) formatChange(line string) string {
  method formatCommits (line 74) | func (widget *Widget) formatCommits(data []string) string {
  method formatCommit (line 84) | func (widget *Widget) formatCommit(line string) string {

FILE: modules/mercurial/hg_repo.go
  type MercurialRepo (line 13) | type MercurialRepo struct
    method branch (line 36) | func (repo *MercurialRepo) branch() string {
    method bookmark (line 45) | func (repo *MercurialRepo) bookmark() string {
    method changedFiles (line 53) | func (repo *MercurialRepo) changedFiles() []string {
    method commits (line 64) | func (repo *MercurialRepo) commits(commitCount int, commitFormat strin...
    method pull (line 78) | func (repo *MercurialRepo) pull() string {
    method checkout (line 85) | func (repo *MercurialRepo) checkout(branch string) string {
    method repoPath (line 92) | func (repo *MercurialRepo) repoPath() string {
  function NewMercurialRepo (line 22) | func NewMercurialRepo(repoPath string, commitCount int, commitFormat str...

FILE: modules/mercurial/keyboard.go
  method initializeKeyboardControls (line 5) | func (widget *Widget) initializeKeyboardControls() {

FILE: modules/mercurial/settings.go
  constant defaultFocusable (line 9) | defaultFocusable = true
  constant defaultTitle (line 10) | defaultTitle     = "Mercurial"
  type Settings (line 13) | type Settings struct
  function NewSettingsFromYAML (line 21) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/mercurial/widget.go
  constant modalHeight (line 11) | modalHeight = 7
  constant modalWidth (line 12) | modalWidth  = 80
  constant offscreen (line 13) | offscreen   = -1000
  type Widget (line 17) | type Widget struct
    method Checkout (line 47) | func (widget *Widget) Checkout() {
    method Pull (line 66) | func (widget *Widget) Pull() {
    method Refresh (line 72) | func (widget *Widget) Refresh() {
    method addCheckoutButton (line 82) | func (widget *Widget) addCheckoutButton(form *tview.Form, fctn func()) {
    method addButtons (line 86) | func (widget *Widget) addButtons(form *tview.Form, checkoutFctn func()) {
    method addCancelButton (line 91) | func (widget *Widget) addCancelButton(form *tview.Form) {
    method modalFocus (line 102) | func (widget *Widget) modalFocus(form *tview.Form) {
    method modalForm (line 108) | func (widget *Widget) modalForm(lbl, text string) *tview.Form {
    method modalFrame (line 118) | func (widget *Widget) modalFrame(form *tview.Form) *tview.Frame {
    method currentData (line 136) | func (widget *Widget) currentData() *MercurialRepo {
    method mercurialRepos (line 148) | func (widget *Widget) mercurialRepos(repoPaths []string) []*MercurialR...
  function NewWidget (line 28) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, pages ...

FILE: modules/nbascore/keyboard.go
  method initializeKeyboardControls (line 5) | func (widget *Widget) initializeKeyboardControls() {
  method center (line 17) | func (widget *Widget) center() {
  method next (line 22) | func (widget *Widget) next() {
  method prev (line 27) | func (widget *Widget) prev() {

FILE: modules/nbascore/settings.go
  constant defaultFocusable (line 9) | defaultFocusable = true
  constant defaultTitle (line 10) | defaultTitle     = "NBA Score"
  type Settings (line 13) | type Settings struct
  function NewSettingsFromYAML (line 17) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/nbascore/widget.go
  type Widget (line 19) | type Widget struct
    method Refresh (line 41) | func (widget *Widget) Refresh() {
    method nbascore (line 45) | func (widget *Widget) nbascore() (string, string, bool) {
  function NewWidget (line 27) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, pages ...

FILE: modules/newrelic/client.go
  type Client2 (line 7) | type Client2 struct
    method Application (line 20) | func (client *Client2) Application() (*nr.Application, error) {
    method Deployments (line 30) | func (client *Client2) Deployments() ([]nr.ApplicationDeployment, erro...
  function NewClient (line 12) | func NewClient(apiKey string, applicationId int) *Client2 {

FILE: modules/newrelic/client/alert_conditions.go
  type AlertCondition (line 4) | type AlertCondition struct
  type AlertConditionTerm (line 17) | type AlertConditionTerm struct
  type AlertUserDefined (line 26) | type AlertUserDefined struct
  type AlertConditionOptions (line 32) | type AlertConditionOptions struct
    method String (line 37) | func (o *AlertConditionOptions) String() string {
  method GetAlertConditions (line 49) | func (c *Client) GetAlertConditions(policy int, options *AlertConditionO...

FILE: modules/newrelic/client/alert_events.go
  type AlertEvent (line 4) | type AlertEvent struct
  type AlertEventFilter (line 19) | type AlertEventFilter struct
  type AlertEventOptions (line 30) | type AlertEventOptions struct
    method String (line 35) | func (o *AlertEventOptions) String() string {
  method GetAlertEvents (line 51) | func (c *Client) GetAlertEvents(options *AlertEventOptions) ([]AlertEven...

FILE: modules/newrelic/client/application_deployments.go
  type ApplicationDeploymentLinks (line 10) | type ApplicationDeploymentLinks struct
  type ApplicationDeploymentOptions (line 16) | type ApplicationDeploymentOptions struct
    method String (line 46) | func (o *ApplicationDeploymentOptions) String() string {
  type ApplicationDeployment (line 22) | type ApplicationDeployment struct
  method GetApplicationDeployments (line 34) | func (c *Client) GetApplicationDeployments(id int, opt *ApplicationDeplo...

FILE: modules/newrelic/client/application_host_metrics.go
  method GetApplicationHostMetrics (line 10) | func (c *Client) GetApplicationHostMetrics(appID, hostID int, options *M...
  method GetApplicationHostMetricData (line 26) | func (c *Client) GetApplicationHostMetricData(appID, hostID int, names [...

FILE: modules/newrelic/client/application_hosts.go
  type ApplicationHostSummary (line 8) | type ApplicationHostSummary struct
  type ApplicationHostEndUserSummary (line 18) | type ApplicationHostEndUserSummary struct
  type ApplicationHostLinks (line 25) | type ApplicationHostLinks struct
  type ApplicationHost (line 32) | type ApplicationHost struct
  type ApplicationHostsFilter (line 45) | type ApplicationHostsFilter struct
  type ApplicationHostsOptions (line 52) | type ApplicationHostsOptions struct
    method String (line 85) | func (o *ApplicationHostsOptions) String() string {
  method GetApplicationHosts (line 59) | func (c *Client) GetApplicationHosts(id int, options *ApplicationHostsOp...
  method GetApplicationHost (line 73) | func (c *Client) GetApplicationHost(appID, hostID int) (*ApplicationHost...

FILE: modules/newrelic/client/application_instance_metrics.go
  method GetApplicationInstanceMetrics (line 10) | func (c *Client) GetApplicationInstanceMetrics(appID, instanceID int, op...
  method GetApplicationInstanceMetricData (line 26) | func (c *Client) GetApplicationInstanceMetricData(appID, instanceID int,...

FILE: modules/newrelic/client/application_instances.go
  type ApplicationInstanceSummary (line 8) | type ApplicationInstanceSummary struct
  type ApplicationInstanceEndUserSummary (line 18) | type ApplicationInstanceEndUserSummary struct
  type ApplicationInstanceLinks (line 25) | type ApplicationInstanceLinks struct
  type ApplicationInstance (line 32) | type ApplicationInstance struct
  type ApplicationInstancesFilter (line 46) | type ApplicationInstancesFilter struct
  type ApplicationInstancesOptions (line 53) | type ApplicationInstancesOptions struct
    method String (line 85) | func (o *ApplicationInstancesOptions) String() string {
  method GetApplicationInstances (line 60) | func (c *Client) GetApplicationInstances(appID int, options *Application...
  method GetApplicationInstance (line 74) | func (c *Client) GetApplicationInstance(appID, instanceID int) (*Applica...

FILE: modules/newrelic/client/application_metrics.go
  method GetApplicationMetrics (line 10) | func (c *Client) GetApplicationMetrics(id int, options *MetricsOptions) ...
  method GetApplicationMetricData (line 25) | func (c *Client) GetApplicationMetricData(id int, names []string, option...

FILE: modules/newrelic/client/applications.go
  type ApplicationSummary (line 9) | type ApplicationSummary struct
  type EndUserSummary (line 21) | type EndUserSummary struct
  type Settings (line 29) | type Settings struct
  type Links (line 37) | type Links struct
  type Application (line 45) | type Application struct
  type ApplicationFilter (line 60) | type ApplicationFilter struct
  type ApplicationOptions (line 69) | type ApplicationOptions struct
    method String (line 74) | func (o *ApplicationOptions) String() string {
  method GetApplications (line 89) | func (c *Client) GetApplications(options *ApplicationOptions) ([]Applica...
  method GetApplication (line 101) | func (c *Client) GetApplication(id int) (*Application, error) {

FILE: modules/newrelic/client/array.go
  type Array (line 9) | type Array struct

FILE: modules/newrelic/client/browser_applications.go
  type BrowserApplicationsFilter (line 5) | type BrowserApplicationsFilter struct
  type BrowserApplicationsOptions (line 12) | type BrowserApplicationsOptions struct
    method String (line 39) | func (o *BrowserApplicationsOptions) String() string {
  type BrowserApplication (line 18) | type BrowserApplication struct
  method GetBrowserApplications (line 27) | func (c *Client) GetBrowserApplications(opt *BrowserApplicationsOptions)...

FILE: modules/newrelic/client/component_metrics.go
  method GetComponentMetrics (line 9) | func (c *Client) GetComponentMetrics(id int, options *MetricsOptions) ([...
  method GetComponentMetricData (line 23) | func (c *Client) GetComponentMetricData(id int, names []string, options ...

FILE: modules/newrelic/client/http_helper.go
  method doGet (line 14) | func (c *Client) doGet(path string, params fmt.Stringer, out interface{}...
  method doRequest (line 29) | func (c *Client) doRequest(req *http.Request, out interface{}) error {
  function encodeGetParams (line 52) | func encodeGetParams(params map[string]interface{}) string {

FILE: modules/newrelic/client/key_transactions.go
  type KeyTransactionsFilter (line 9) | type KeyTransactionsFilter struct
  type KeyTransactionsOptions (line 15) | type KeyTransactionsOptions struct
    method String (line 67) | func (o *KeyTransactionsOptions) String() string {
  type KeyTransactionLinks (line 22) | type KeyTransactionLinks struct
  type KeyTransaction (line 27) | type KeyTransaction struct
  method GetKeyTransactions (line 41) | func (c *Client) GetKeyTransactions(opt *KeyTransactionsOptions) ([]KeyT...
  method GetKeyTransaction (line 55) | func (c *Client) GetKeyTransaction(id int) (*KeyTransaction, error) {

FILE: modules/newrelic/client/legacy_alert_policies.go
  type LegacyAlertPolicyLinks (line 8) | type LegacyAlertPolicyLinks struct
  type LegacyAlertPolicyCondition (line 14) | type LegacyAlertPolicyCondition struct
  type LegacyAlertPolicy (line 24) | type LegacyAlertPolicy struct
  type LegacyAlertPolicyFilter (line 34) | type LegacyAlertPolicyFilter struct
  type LegacyAlertPolicyOptions (line 40) | type LegacyAlertPolicyOptions struct
    method String (line 45) | func (o *LegacyAlertPolicyOptions) String() string {
  method GetLegacyAlertPolicy (line 56) | func (c *Client) GetLegacyAlertPolicy(id int) (*LegacyAlertPolicy, error) {
  method GetLegacyAlertPolicies (line 69) | func (c *Client) GetLegacyAlertPolicies(options *LegacyAlertPolicyOption...

FILE: modules/newrelic/client/main.go
  constant defaultAPIURL (line 19) | defaultAPIURL = "https://api.newrelic.com/v2/"
  constant defaultTimeout (line 21) | defaultTimeout = 5 * time.Second
  type Client (line 25) | type Client struct
  function NewWithHTTPClient (line 33) | func NewWithHTTPClient(apiKey string, client *http.Client) *Client {
  function NewClient (line 46) | func NewClient(apiKey string) *Client {

FILE: modules/newrelic/client/metrics.go
  type Metric (line 8) | type Metric struct
  type MetricsOptions (line 15) | type MetricsOptions struct
    method String (line 55) | func (o *MetricsOptions) String() string {
  type MetricTimeslice (line 21) | type MetricTimeslice struct
  type MetricData (line 28) | type MetricData struct
  type MetricDataOptions (line 35) | type MetricDataOptions struct
    method String (line 65) | func (o *MetricDataOptions) String() string {
  type MetricDataResponse (line 47) | type MetricDataResponse struct
  type MetricClient (line 82) | type MetricClient struct
    method GetMetrics (line 96) | func (mc *MetricClient) GetMetrics(path string, options *MetricsOption...
    method GetMetricData (line 112) | func (mc *MetricClient) GetMetricData(path string, names []string, opt...
  function NewMetricClient (line 87) | func NewMetricClient(newRelicClient *Client) *MetricClient {

FILE: modules/newrelic/client/mobile_application_metrics.go
  method GetMobileApplicationMetrics (line 10) | func (c *Client) GetMobileApplicationMetrics(id int, options *MetricsOpt...
  method GetMobileApplicationMetricData (line 25) | func (c *Client) GetMobileApplicationMetricData(id int, names []string, ...

FILE: modules/newrelic/client/mobile_applications.go
  type MobileApplicationSummary (line 8) | type MobileApplicationSummary struct
  type MobileApplicationCrashSummary (line 20) | type MobileApplicationCrashSummary struct
  type MobileApplication (line 28) | type MobileApplication struct
  method GetMobileApplications (line 38) | func (c *Client) GetMobileApplications() ([]MobileApplication, error) {
  method GetMobileApplication (line 51) | func (c *Client) GetMobileApplication(id int) (*MobileApplication, error) {

FILE: modules/newrelic/client/notification_channels.go
  type NotificationChannelLinks (line 8) | type NotificationChannelLinks struct
  type NotificationChannel (line 14) | type NotificationChannel struct
  type NotificationChannelsFilter (line 32) | type NotificationChannelsFilter struct
  type NotificationChannelsOptions (line 39) | type NotificationChannelsOptions struct
    method String (line 44) | func (o *NotificationChannelsOptions) String() string {
  method GetNotificationChannel (line 56) | func (c *Client) GetNotificationChannel(id int) (*NotificationChannel, e...
  method GetNotificationChannels (line 69) | func (c *Client) GetNotificationChannels(options *NotificationChannelsOp...

FILE: modules/newrelic/client/server_metrics.go
  method GetServerMetrics (line 9) | func (c *Client) GetServerMetrics(id int, options *MetricsOptions) ([]Me...
  method GetServerMetricData (line 23) | func (c *Client) GetServerMetricData(id int, names []string, options *Me...

FILE: modules/newrelic/client/servers.go
  type ServersFilter (line 9) | type ServersFilter struct
  type ServersOptions (line 18) | type ServersOptions struct
    method String (line 80) | func (o *ServersOptions) String() string {
  type ServerSummary (line 24) | type ServerSummary struct
  type ServerLinks (line 36) | type ServerLinks struct
  type Server (line 41) | type Server struct
  method GetServers (line 55) | func (c *Client) GetServers(opt *ServersOptions) ([]Server, error) {
  method GetServer (line 68) | func (c *Client) GetServer(id int) (*Server, error) {

FILE: modules/newrelic/client/usages.go
  type Usage (line 8) | type Usage struct
  type UsageData (line 16) | type UsageData struct
  type usageParams (line 24) | type usageParams struct
    method String (line 30) | func (o *usageParams) String() string {
  method GetUsages (line 39) | func (c *Client) GetUsages(product string, start, end time.Time, include...

FILE: modules/newrelic/display.go
  method content (line 11) | func (widget *Widget) content() (string, string, bool) {
  method contentFrom (line 37) | func (widget *Widget) contentFrom(deploys []nr.ApplicationDeployment) st...

FILE: modules/newrelic/keyboard.go
  method initializeKeyboardControls (line 5) | func (widget *Widget) initializeKeyboardControls() {

FILE: modules/newrelic/settings.go
  constant defaultFocusable (line 11) | defaultFocusable = true
  constant defaultTitle (line 12) | defaultTitle     = "NewRelic"
  type Settings (line 15) | type Settings struct
  function NewSettingsFromYAML (line 23) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/newrelic/widget.go
  type Widget (line 11) | type Widget struct
    method Refresh (line 45) | func (widget *Widget) Refresh() {
    method currentData (line 53) | func (widget *Widget) currentData() *Client2 {
  function NewWidget (line 20) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, pages ...

FILE: modules/nextbus/settings.go
  constant defaultFocusable (line 9) | defaultFocusable = false
  constant defaultTitle (line 10) | defaultTitle     = "nextbus"
  type Settings (line 14) | type Settings struct
  function NewSettingsFromYAML (line 23) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/nextbus/widget.go
  type Widget (line 16) | type Widget struct
    method Refresh (line 35) | func (widget *Widget) Refresh() {
    method content (line 42) | func (widget *Widget) content() string {
    method display (line 137) | func (widget *Widget) display() {
  function NewWidget (line 23) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, pages ...
  type AutoGenerated (line 46) | type AutoGenerated struct
  type Prediction (line 51) | type Prediction struct
  type Direction (line 64) | type Direction struct
  type Predictions (line 69) | type Predictions struct
  function getNextBus (line 78) | func getNextBus(agency string, route string, stopID string) string {
  function strTimeToInt (line 130) | func strTimeToInt(sourceMinutes string, sourceSeconds string) string {

FILE: modules/opsgenie/client.go
  type OnCallResponse (line 9) | type OnCallResponse struct
  type OnCallData (line 16) | type OnCallData struct
  type Parent (line 21) | type Parent struct
  method Fetch (line 34) | func (widget *Widget) Fetch(scheduleIdentifierType string, schedules []s...
  function opsGenieRequest (line 54) | func opsGenieRequest(url string, apiKey string) (*OnCallResponse, error) {

FILE: modules/opsgenie/settings.go
  constant defaultFocusable (line 11) | defaultFocusable = false
  constant defaultTitle (line 12) | defaultTitle     = "OpsGenie"
  type Settings (line 15) | type Settings struct
    method arrayifySchedules (line 44) | func (settings *Settings) arrayifySchedules(ymlConfig *config.Config) ...
  function NewSettingsFromYAML (line 25) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/opsgenie/widget.go
  type Widget (line 12) | type Widget struct
    method Refresh (line 30) | func (widget *Widget) Refresh() {
    method content (line 36) | func (widget *Widget) content() (string, string, bool) {
    method cleanScheduleName (line 70) | func (widget *Widget) cleanScheduleName(schedule string) string {
  function NewWidget (line 18) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, settin...

FILE: modules/pagerduty/client.go
  constant queryTimeFmt (line 11) | queryTimeFmt = "2006-01-02T15:04:05Z07:00"
  function GetOnCalls (line 15) | func GetOnCalls(apiKey string, scheduleIDs []string) ([]pagerduty.OnCall...
  function GetIncidents (line 45) | func GetIncidents(apiKey string, teamIDs []string, userIDs []string) ([]...

FILE: modules/pagerduty/settings.go
  constant defaultFocusable (line 11) | defaultFocusable = false
  constant defaultTitle (line 12) | defaultTitle     = "PagerDuty"
  type Settings (line 16) | type Settings struct
  function NewSettingsFromYAML (line 31) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/pagerduty/sort.go
  type ByEscalationLevel (line 5) | type ByEscalationLevel
    method Len (line 7) | func (s ByEscalationLevel) Len() int      { return len(s) }
    method Swap (line 8) | func (s ByEscalationLevel) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
    method Less (line 10) | func (s ByEscalationLevel) Less(i, j int) bool {

FILE: modules/pagerduty/widget.go
  constant onCallTimeAPILayout (line 15) | onCallTimeAPILayout     = "2006-01-02T15:04:05Z"
  constant onCallTimeDisplayLayout (line 16) | onCallTimeDisplayLayout = "Jan 2, 2006"
  type Widget (line 19) | type Widget struct
    method Refresh (line 38) | func (widget *Widget) Refresh() {
    method contentFrom (line 76) | func (widget *Widget) contentFrom(onCalls []pagerduty.OnCall, incident...
    method onCallEndSummary (line 160) | func (widget *Widget) onCallEndSummary(onCall *pagerduty.OnCall) string {
    method userSummary (line 178) | func (widget *Widget) userSummary(onCall *pagerduty.OnCall) string {
  function NewWidget (line 26) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, settin...

FILE: modules/pihole/client.go
  type Status (line 15) | type Status struct
  function getStatus (line 33) | func getStatus(c http.Client, apiURL string) (status Status, err error) {
  type FlexInt (line 86) | type FlexInt
    method UnmarshalJSON (line 88) | func (fi *FlexInt) UnmarshalJSON(b []byte) error {
  type TopItems (line 109) | type TopItems struct
  function getTopItems (line 114) | func getTopItems(c http.Client, settings *Settings) (ti TopItems, err er...
  type TopClients (line 167) | type TopClients struct
  function parseError (line 172) | func parseError(err error) string {
  function getTopClients (line 182) | func getTopClients(c http.Client, settings *Settings) (tc TopClients, er...
  type QueryTypes (line 236) | type QueryTypes struct
  function getQueryTypes (line 240) | func getQueryTypes(c http.Client, settings *Settings) (qt QueryTypes, er...
  function checkServer (line 294) | func checkServer(c http.Client, apiURL string) error {
  method adblockSwitch (line 350) | func (widget *Widget) adblockSwitch(action string) {
  function getClient (line 376) | func getClient() http.Client {

FILE: modules/pihole/keyboard.go
  method initializeKeyboardControls (line 3) | func (widget *Widget) initializeKeyboardControls() {

FILE: modules/pihole/settings.go
  constant defaultFocusable (line 9) | defaultFocusable = true
  constant defaultTitle (line 10) | defaultTitle     = "Pi-hole"
  type Settings (line 13) | type Settings struct
  function NewSettingsFromYAML (line 26) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/pihole/view.go
  function getSummaryView (line 15) | func getSummaryView(c http.Client, settings *Settings) string {
  function getTopItemsView (line 52) | func getTopItemsView(c http.Client, settings *Settings) string {
  function getTopClientsView (line 98) | func getTopClientsView(c http.Client, settings *Settings) string {
  function shorten (line 154) | func shorten(s string, limit int) string {
  function createTable (line 162) | func createTable(header []string, buf io.Writer) *tablewriter.Table {
  function sortMapByIntVal (line 185) | func sortMapByIntVal(m map[string]int) (sorted [][]string) {
  function sortMapByFloatVal (line 207) | func sortMapByFloatVal(m map[string]float32) (sorted [][]string) {

FILE: modules/pihole/widget.go
  type Widget (line 11) | type Widget struct
    method Refresh (line 36) | func (widget *Widget) Refresh() {
    method content (line 46) | func (widget *Widget) content() (string, string, bool) {
    method disable (line 74) | func (widget *Widget) disable() {
    method enable (line 78) | func (widget *Widget) enable() {
  function NewWidget (line 19) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, _ *tvi...

FILE: modules/ping/settings.go
  constant defaultFocusable (line 11) | defaultFocusable = false
  constant defaultTitle (line 12) | defaultTitle     = "Pings"
  type Host (line 15) | type Host struct
  type Settings (line 21) | type Settings struct
  function NewSettingsFromYAML (line 26) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...
  function buildhosts (line 35) | func buildhosts(ymlConfig *config.Config) []Host {

FILE: modules/ping/widget.go
  type Widget (line 16) | type Widget struct
    method doPings (line 37) | func (widget *Widget) doPings() {
    method Refresh (line 67) | func (widget *Widget) Refresh() {
    method content (line 75) | func (widget *Widget) content() string {
    method display (line 98) | func (widget *Widget) display() {
  function NewWidget (line 24) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, settin...

FILE: modules/pivotal/client.go
  type Resource (line 11) | type Resource struct
  type PivotalClient (line 16) | type PivotalClient struct
    method apiv5 (line 43) | func (pivotal *PivotalClient) apiv5(resource string) (*Resource, error) {
    method getCurrentUser (line 82) | func (pivotal *PivotalClient) getCurrentUser() (*User, error) {
    method searchStories (line 96) | func (pivotal *PivotalClient) searchStories(filter string) (*PivotalTr...
  type Error (line 23) | type Error struct
  function NewPivotalClient (line 29) | func NewPivotalClient(token string, projectId string) *PivotalClient {

FILE: modules/pivotal/display.go
  constant hasPullFailIcon (line 12) | hasPullFailIcon = '💥'
  constant hasPullIcon (line 13) | hasPullIcon     = "🌱"
  method display (line 27) | func (widget *Widget) display() {
  method content (line 32) | func (widget *Widget) content() (string, string, bool) {
  function getStatusIcon (line 68) | func getStatusIcon(story *Story) string {
  function getPullStatusIcon (line 77) | func getPullStatusIcon(story *Story) string {
  function getShowText (line 87) | func getShowText(story *Story) string {

FILE: modules/pivotal/keyboard.go
  method initializeKeyboardControls (line 7) | func (widget *Widget) initializeKeyboardControls() {

FILE: modules/pivotal/settings.go
  constant defaultFocusable (line 11) | defaultFocusable = true
  constant defaultTitle (line 12) | defaultTitle     = "Pivotal"
  type customQuery (line 15) | type customQuery struct
  type Settings (line 22) | type Settings struct
  function NewSettingsFromYAML (line 32) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...
  function parseCustomQueries (line 49) | func parseCustomQueries(ymlConfig *config.Config) []customQuery {

FILE: modules/pivotal/structs.go
  type User (line 7) | type User struct
  type Story (line 11) | type Story struct
  type PivotalTrackerResponse (line 44) | type PivotalTrackerResponse struct
  type StoryResponse (line 50) | type StoryResponse struct
  type EpicResponse (line 58) | type EpicResponse struct
  type Epic (line 64) | type Epic struct
  type Label (line 77) | type Label struct
  type StoryLabel (line 88) | type StoryLabel struct
  type StoryPullRequest (line 97) | type StoryPullRequest struct
  type StoryTask (line 112) | type StoryTask struct
  type StoryBranch (line 123) | type StoryBranch struct
  type StoryComment (line 133) | type StoryComment struct
  type StoryReview (line 145) | type StoryReview struct
  type StoryProject (line 156) | type StoryProject struct

FILE: modules/pivotal/view.go
  type PivotalSource (line 8) | type PivotalSource struct
    method loadStories (line 30) | func (source *PivotalSource) loadStories() {
    method Open (line 44) | func (source *PivotalSource) Open() {
    method OpenPulls (line 56) | func (source *PivotalSource) OpenPulls() {
    method getItemCount (line 70) | func (source *PivotalSource) getItemCount() int {
    method setItemCount (line 76) | func (source *PivotalSource) setItemCount(count int) {
  function NewPivotalSource (line 19) | func NewPivotalSource(name string, filter string, client *PivotalClient,...

FILE: modules/pivotal/widget.go
  type Widget (line 9) | type Widget struct
    method loadSources (line 46) | func (widget *Widget) loadSources() {
    method buildPivotalSources (line 54) | func (widget *Widget) buildPivotalSources() []*PivotalSource {
    method CurrentSource (line 77) | func (widget *Widget) CurrentSource() *PivotalSource {
    method Refresh (line 86) | func (widget *Widget) Refresh() {
    method Open (line 96) | func (widget *Widget) Open() {
    method OpenPulls (line 99) | func (widget *Widget) OpenPulls() {
  function NewWidget (line 20) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, pages ...

FILE: modules/pocket/client.go
  type Client (line 12) | type Client struct
    method request (line 71) | func (*Client) request(req request, result interface{}) error {
    method ObtainRequestToken (line 122) | func (client *Client) ObtainRequestToken() (code string, err error) {
    method CreateAuthLink (line 147) | func (client *Client) CreateAuthLink(requestToken string) string {
    method GetAccessToken (line 162) | func (client *Client) GetAccessToken(requestToken string) (accessToken...
    method GetLinks (line 210) | func (client *Client) GetLinks(state LinkState) (response ItemLists, e...
    method ModifyLink (line 241) | func (client *Client) ModifyLink(action Action, itemID string) (ok boo...
  function NewClient (line 20) | func NewClient(consumerKey, redirectURL string) *Client {
  type Item (line 30) | type Item struct
  type ItemLists (line 57) | type ItemLists struct
  type request (line 64) | type request struct
  type obtainRequestTokenRequest (line 116) | type obtainRequestTokenRequest struct
  type accessTokenRequest (line 151) | type accessTokenRequest struct
  type accessTokenResponse (line 157) | type accessTokenResponse struct
  type LinkState (line 200) | type LinkState
  constant Read (line 204) | Read LinkState = "read"
  constant Unread (line 206) | Unread LinkState = "unread"
  type Action (line 226) | type Action
  constant Archive (line 230) | Archive Action = "archive"
  constant ReAdd (line 232) | ReAdd Action = "readd"
  type actionParams (line 235) | type actionParams struct

FILE: modules/pocket/item_service.go
  type sortByTimeAdded (line 5) | type sortByTimeAdded
    method Len (line 7) | func (a sortByTimeAdded) Len() int           { return len(a) }
    method Swap (line 8) | func (a sortByTimeAdded) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
    method Less (line 9) | func (a sortByTimeAdded) Less(i, j int) bool { return a[i].TimeAdded >...
  function orderItemResponseByKey (line 11) | func orderItemResponseByKey(response ItemLists) []Item {

FILE: modules/pocket/keyboard.go
  method initializeKeyboardControls (line 5) | func (widget *Widget) initializeKeyboardControls() {

FILE: modules/pocket/settings.go
  constant defaultFocusable (line 9) | defaultFocusable = true
  constant defaultTitle (line 10) | defaultTitle     = "Pocket"
  type Settings (line 13) | type Settings struct
  function NewSettingsFromYAML (line 21) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/pocket/widget.go
  type Widget (line 15) | type Widget struct
    method Render (line 44) | func (widget *Widget) Render() {
    method Refresh (line 49) | func (widget *Widget) Refresh() {
    method authorizeWorkFlow (line 127) | func (widget *Widget) authorizeWorkFlow() (string, string, bool) {
    method toggleView (line 171) | func (widget *Widget) toggleView() {
    method openLink (line 176) | func (widget *Widget) openLink() {
    method toggleLink (line 184) | func (widget *Widget) toggleLink() {
    method formatItem (line 202) | func (widget *Widget) formatItem(item Item, isSelected bool) string {
    method content (line 214) | func (widget *Widget) content() (string, string, bool) {
  function NewWidget (line 24) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, _ *tvi...
  type pocketMetaData (line 75) | type pocketMetaData struct
  function writeMetaDataToDisk (line 79) | func writeMetaDataToDisk(metaData pocketMetaData) error {
  function readMetaDataFromDisk (line 98) | func readMetaDataFromDisk() (pocketMetaData, error) {

FILE: modules/power/battery.go
  constant timeRegExp (line 16) | timeRegExp = "^(?:\\d|[01]\\d|2[0-3]):[0-5]\\d"
  type Battery (line 19) | type Battery struct
    method Refresh (line 39) | func (battery *Battery) Refresh() {
    method String (line 44) | func (battery *Battery) String() string {
    method execute (line 50) | func (battery *Battery) execute() string {
    method parse (line 55) | func (battery *Battery) parse(data string) string {
    method formatCharge (line 79) | func (battery *Battery) formatCharge(data string) string {
    method formatRemaining (line 84) | func (battery *Battery) formatRemaining(data string) string {
    method formatState (line 95) | func (battery *Battery) formatState(data string) string {
  function NewBattery (line 28) | func NewBattery() *Battery {

FILE: modules/power/battery_freebsd.go
  type Battery (line 16) | type Battery struct
    method Refresh (line 31) | func (battery *Battery) Refresh() {
    method String (line 36) | func (battery *Battery) String() string {
    method execute (line 47) | func (battery *Battery) execute() string {
    method parse (line 52) | func (battery *Battery) parse(data string) string {
    method formatCharge (line 75) | func (battery *Battery) formatCharge(data string) string {
    method formatState (line 80) | func (battery *Battery) formatState(data string) string {
  function NewBattery (line 25) | func NewBattery() *Battery {

FILE: modules/power/battery_linux.go
  type Battery (line 16) | type Battery struct
    method Refresh (line 29) | func (battery *Battery) Refresh() {
    method String (line 34) | func (battery *Battery) String() string {
    method execute (line 40) | func (battery *Battery) execute() string {
    method parse (line 54) | func (battery *Battery) parse(data string) string {
    method formatCharge (line 80) | func (battery *Battery) formatCharge(data string) string {
    method formatState (line 85) | func (battery *Battery) formatState(data string) string {
  function NewBattery (line 23) | func NewBattery() *Battery {

FILE: modules/power/managed_device_test.go
  function Test_Refresh (line 10) | func Test_Refresh(t *testing.T) {
  function Test_Add (line 117) | func Test_Add(t *testing.T) {
  function Test_Attributes (line 162) | func Test_Attributes(t *testing.T) {
  function Test_BatteryPercent (line 229) | func Test_BatteryPercent(t *testing.T) {

FILE: modules/power/managed_devices.go
  type ManagedDevices (line 14) | type ManagedDevices struct
    method Refresh (line 33) | func (manDevices *ManagedDevices) Refresh() {
    method parse (line 43) | func (manDevices *ManagedDevices) parse(data string) []*ManagedDevice {
  function NewManagedDevices (line 21) | func NewManagedDevices() *ManagedDevices {
  type ManagedDevice (line 62) | type ManagedDevice struct
    method Add (line 94) | func (manDev *ManagedDevice) Add(chunk string) {
    method Dump (line 111) | func (manDev *ManagedDevice) Dump() string {
    method BatteryPercent (line 124) | func (manDev *ManagedDevice) BatteryPercent() int64 {
    method BluetoothDevice (line 134) | func (manDev *ManagedDevice) BluetoothDevice() bool {
    method BuiltIn (line 139) | func (manDev *ManagedDevice) BuiltIn() bool {
    method HasBattery (line 144) | func (manDev *ManagedDevice) HasBattery() bool {
    method Product (line 149) | func (manDev *ManagedDevice) Product() string {
  function NewManagedDevice (line 66) | func NewManagedDevice() *ManagedDevice {

FILE: modules/power/settings.go
  constant defaultFocusable (line 9) | defaultFocusable = false
  constant defaultTitle (line 10) | defaultTitle     = "Power"
  type Settings (line 13) | type Settings struct
  function NewSettingsFromYAML (line 17) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/power/source.go
  constant SingleQuotesRegExp (line 13) | SingleQuotesRegExp = "'(.*)'"
  function powerSource (line 17) | func powerSource() string {

FILE: modules/power/source_freebsd.go
  function powerSource (line 7) | func powerSource() string {

FILE: modules/power/source_linux.go
  function powerSource (line 7) | func powerSource() string {

FILE: modules/power/widget.go
  constant msgNoBattery (line 13) | msgNoBattery       = " no battery found"
  constant productNameTrimLen (line 14) | productNameTrimLen = 14
  type Widget (line 17) | type Widget struct
    method Refresh (line 43) | func (widget *Widget) Refresh() {
    method content (line 60) | func (widget *Widget) content() (string, string, bool) {
  function NewWidget (line 26) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, settin...

FILE: modules/progress/settings.go
  constant defaultFocusable (line 9) | defaultFocusable = false
  constant defaultTitle (line 10) | defaultTitle     = "Progress"
  type colors (line 13) | type colors struct
  type Settings (line 20) | type Settings struct
  function NewSettingsFromYAML (line 37) | func NewSettingsFromYAML(name string, ymlConfig, globalConfig *config.Co...

FILE: modules/progress/widget.go
  type Widget (line 21) | type Widget struct
    method Refresh (line 60) | func (widget *Widget) Refresh() {
    method content (line 97) | func (widget *Widget) content() string {
    method display (line 126) | func (widget *Widget) display() {
    method execValueCmd (line 141) | func (widget *Widget) execValueCmd(cmd string) (float64, error) {
    method buildProgressBar (line 161) | func (widget *Widget) buildProgressBar(percent string) *progress.Model {
    method calcPercent (line 180) | func (widget *Widget) calcPercent() {
    method formatPercent (line 207) | func (widget *Widget) formatPercent(p float64) string {
    method calcBarWidth (line 220) | func (widget *Widget) calcBarWidth(percent string) int {
  function NewWidget (line 39) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, settin...

FILE: modules/resourceusage/settings.go
  constant defaultFocusable (line 9) | defaultFocusable       = false
  constant defaultRefreshInterval (line 10) | defaultRefreshInterval = "1s"
  constant defaultTitle (line 11) | defaultTitle           = "ResourceUsage"
  type Settings (line 14) | type Settings struct
  function NewSettingsFromYAML (line 23) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/resourceusage/widget.go
  type Widget (line 16) | type Widget struct
    method Refresh (line 128) | func (widget *Widget) Refresh() {
  function NewWidget (line 23) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, settin...
  function MakeGraph (line 40) | func MakeGraph(widget *Widget) {
  function getDataFromSystem (line 139) | func getDataFromSystem(widget *Widget) (cpuStats []float64, memInfo mem....

FILE: modules/rollbar/client.go
  function CurrentActiveItems (line 11) | func CurrentActiveItems(accessToken, assignedToName string, activeOnly b...
  function rollbarItemRequest (line 35) | func rollbarItemRequest(accessToken, assignedToName string, activeOnly b...

FILE: modules/rollbar/keyboard.go
  method initializeKeyboardControls (line 5) | func (widget *Widget) initializeKeyboardControls() {

FILE: modules/rollbar/rollbar.go
  type ActiveItems (line 3) | type ActiveItems struct
  type Item (line 6) | type Item struct
  type Result (line 15) | type Result struct

FILE: modules/rollbar/settings.go
  constant defaultFocusable (line 11) | defaultFocusable = true
  constant defaultTitle (line 12) | defaultTitle     = "Rollbar"
  type Settings (line 15) | type Settings struct
  function NewSettingsFromYAML (line 26) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/rollbar/widget.go
  type Widget (line 12) | type Widget struct
    method Refresh (line 36) | func (widget *Widget) Refresh() {
    method Render (line 61) | func (widget *Widget) Render() {
    method content (line 65) | func (widget *Widget) content() (string, string, bool) {
    method openBuild (line 121) | func (widget *Widget) openBuild() {
  function NewWidget (line 21) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, pages ...
  function statusColor (line 98) | func statusColor(item *Item) string {
  function levelColor (line 108) | func levelColor(item *Item) string {

FILE: modules/security/dns.go
  function DnsServers (line 13) | func DnsServers() []string {
  function dnsLinux (line 28) | func dnsLinux() []string {
  function dnsMacOS (line 47) | func dnsMacOS() []string {
  function dnsWindows (line 61) | func dnsWindows() []string {

FILE: modules/security/firewall.go
  constant osxFirewallCmd (line 13) | osxFirewallCmd = "/usr/libexec/ApplicationFirewall/socketfilterfw"
  function FirewallState (line 17) | func FirewallState() string {
  function FirewallStealthState (line 30) | func FirewallStealthState() string {
  function firewallStateLinux (line 45) | func firewallStateLinux() string {
  function checkFirewalld (line 69) | func checkFirewalld() string {
  function checkUfw (line 101) | func checkUfw() string {
  function checkNftables (line 117) | func checkNftables() string {
  function checkIptables (line 133) | func checkIptables() string {
  function firewallStateMacOS (line 156) | func firewallStateMacOS() string {
  function firewallStateWindows (line 163) | func firewallStateWindows() string {
  function firewallStealthStateLinux (line 192) | func firewallStealthStateLinux() string {
  function firewallStealthStateMacOS (line 196) | func firewallStealthStateMacOS() string {
  function firewallStealthStateWindows (line 203) | func firewallStealthStateWindows() string {
  function statusLabel (line 207) | func statusLabel(str string) string {

FILE: modules/security/security_data.go
  type SecurityData (line 3) | type SecurityData struct
    method DnsAt (line 16) | func (data SecurityData) DnsAt(idx int) string {
    method Fetch (line 23) | func (data *SecurityData) Fetch() {
  function NewSecurityData (line 12) | func NewSecurityData() *SecurityData {

FILE: modules/security/settings.go
  constant defaultFocusable (line 9) | defaultFocusable = false
  constant defaultTitle (line 10) | defaultTitle     = "Security"
  type Settings (line 13) | type Settings struct
  function NewSettingsFromYAML (line 17) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/security/users.go
  function LoggedInUsers (line 15) | func LoggedInUsers() []string {
  function cleanUsers (line 30) | func cleanUsers(users []string) []string {
  function loggedInUsersLinux (line 52) | func loggedInUsersLinux() []string {
  function loggedInUsersMacOs (line 78) | func loggedInUsersMacOs() []string {
  function loggedInUsersWindows (line 85) | func loggedInUsersWindows() []string {

FILE: modules/security/widget.go
  type Widget (line 11) | type Widget struct
    method Refresh (line 29) | func (widget *Widget) Refresh() {
    method content (line 40) | func (widget *Widget) content() (string, string, bool) {
  function NewWidget (line 17) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, settin...

FILE: modules/security/wifi.go
  constant osxWifiCmd (line 12) | osxWifiCmd = "/System/Library/PrivateFrameworks/Apple80211.framework/Ver...
  constant osxWifiArg (line 13) | osxWifiArg = "-I"
  function WifiEncryption (line 17) | func WifiEncryption() string {
  function WifiName (line 30) | func WifiName() string {
  function wifiEncryptionLinux (line 45) | func wifiEncryptionLinux() string {
  function wifiEncryptionMacOS (line 58) | func wifiEncryptionMacOS() string {
  function wifiInfo (line 63) | func wifiInfo() string {
  function wifiNameLinux (line 68) | func wifiNameLinux() string {
  function wifiNameMacOS (line 73) | func wifiNameMacOS() string {
  function matchStr (line 78) | func matchStr(data [][]string) string {
  function wifiEncryptionWindows (line 87) | func wifiEncryptionWindows() string {
  function wifiNameWindows (line 91) | func wifiNameWindows() string {
  function parseWlanNetsh (line 95) | func parseWlanNetsh(target string) string {

FILE: modules/spacex/client.go
  constant spacexLaunchAPI (line 10) | spacexLaunchAPI = "https://api.spacexdata.com/v3/launches/next"
  type Launch (line 13) | type Launch struct
  type LaunchSite (line 24) | type LaunchSite struct
  type Rocket (line 28) | type Rocket struct
  type Links (line 32) | type Links struct
  function NextLaunch (line 37) | func NextLaunch() (*Launch, error) {

FILE: modules/spacex/settings.go
  constant defaultFocusable (line 9) | defaultFocusable = false
  type Settings (line 12) | type Settings struct
  function NewSettingsFromYAML (line 16) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/spacex/widget.go
  type Widget (line 12) | type Widget struct
    method Refresh (line 26) | func (widget *Widget) Refresh() {
    method Render (line 33) | func (widget *Widget) Render() {
    method content (line 37) | func (widget *Widget) content() (string, string, bool) {
  function NewWidget (line 18) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, settin...
  function handleError (line 69) | func handleError(widget *Widget, err error) {

FILE: modules/spotify/keyboard.go
  method initializeKeyboardControls (line 9) | func (widget *Widget) initializeKeyboardControls() {
  method previous (line 21) | func (widget *Widget) previous() {
  method next (line 27) | func (widget *Widget) next() {
  method playPause (line 33) | func (widget *Widget) playPause() {

FILE: modules/spotify/settings.go
  constant defaultFocusable (line 9) | defaultFocusable = true
  constant defaultTitle (line 10) | defaultTitle     = "Spotify"
  type colors (line 13) | type colors struct
  type Settings (line 18) | type Settings struct
  function NewSettingsFromYAML (line 23) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/spotify/widget.go
  type Widget (line 15) | type Widget struct
    method refreshSpotifyInfos (line 44) | func (w *Widget) refreshSpotifyInfos() error {
    method Refresh (line 50) | func (w *Widget) Refresh() {
    method createOutput (line 54) | func (w *Widget) createOutput() (string, string, bool) {
  function NewWidget (line 24) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, pages ...

FILE: modules/spotifyweb/keyboard.go
  method initializeKeyboardControls (line 9) | func (widget *Widget) initializeKeyboardControls() {
  method selectPrevious (line 22) | func (widget *Widget) selectPrevious() {
  method selectNext (line 32) | func (widget *Widget) selectNext() {
  method playPause (line 42) | func (widget *Widget) playPause() {
  method toggleShuffle (line 57) | func (widget *Widget) toggleShuffle() {

FILE: modules/spotifyweb/settings.go
  constant defaultFocusable (line 11) | defaultFocusable = true
  constant defaultTitle (line 12) | defaultTitle     = "Spotify Web"
  type Settings (line 15) | type Settings struct
  function NewSettingsFromYAML (line 23) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/spotifyweb/widget.go
  type Info (line 26) | type Info struct
  type Widget (line 35) | type Widget struct
    method refreshSpotifyInfos (line 133) | func (w *Widget) refreshSpotifyInfos() error {
    method Refresh (line 160) | func (w *Widget) Refresh() {
    method createOutput (line 164) | func (w *Widget) createOutput() (string, string, bool) {
  function authHandler (line 46) | func authHandler(w http.ResponseWriter, r *http.Request) {
  function NewWidget (line 64) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, pages ...

FILE: modules/status/settings.go
  constant defaultFocusable (line 9) | defaultFocusable = false
  constant defaultTitle (line 10) | defaultTitle     = "Status"
  type Settings (line 13) | type Settings struct
  function NewSettingsFromYAML (line 17) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/status/widget.go
  type Widget (line 8) | type Widget struct
    method Refresh (line 30) | func (widget *Widget) Refresh() {
    method animation (line 36) | func (widget *Widget) animation() (string, string, bool) {
  function NewWidget (line 16) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, settin...

FILE: modules/steam/client.go
  type Steam (line 10) | type Steam struct
    method Status (line 48) | func (s *Steam) Status(steamID string) (*Player, error) {
    method fetch (line 63) | func (s *Steam) fetch(id string) ([]byte, error) {
  type ClientOpts (line 15) | type ClientOpts struct
  function NewClient (line 20) | func NewClient(opts *ClientOpts) *Steam {
  type Player (line 35) | type Player struct
  type SteamResponse (line 42) | type SteamResponse struct

FILE: modules/steam/keyboard.go
  method initializeKeyboardControls (line 5) | func (widget *Widget) initializeKeyboardControls() {

FILE: modules/steam/settings.go
  constant defaultFocusable (line 12) | defaultFocusable = true
  type Settings (line 15) | type Settings struct
  function NewSettingsFromYAML (line 23) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/steam/widget.go
  type Widget (line 13) | type Widget struct
    method Refresh (line 35) | func (widget *Widget) Refresh() {
    method Render (line 69) | func (widget *Widget) Render() {
    method content (line 93) | func (widget *Widget) content() (string, string, bool) {
  function NewWidget (line 22) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, pages ...
  function friendlyStatus (line 73) | func friendlyStatus(personastate int) string {

FILE: modules/stocks/finnhub/client.go
  type Client (line 11) | type Client struct
    method Getquote (line 27) | func (client *Client) Getquote() ([]Quote, error) {
    method finnhubRequest (line 54) | func (client *Client) finnhubRequest(symbol string) (*http.Response, e...
  function NewClient (line 17) | func NewClient(symbols []string, apiKey string) *Client {

FILE: modules/stocks/finnhub/quote.go
  type Quote (line 3) | type Quote struct

FILE: modules/stocks/finnhub/settings.go
  constant defaultFocusable (line 12) | defaultFocusable = true
  constant defaultTitle (line 13) | defaultTitle     = "📈 Stocks Price"
  type Settings (line 17) | type Settings struct
  function NewSettingsFromYAML (line 25) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/stocks/finnhub/widget.go
  type Widget (line 12) | type Widget struct
    method Refresh (line 33) | func (widget *Widget) Refresh() {
    method content (line 43) | func (widget *Widget) content() (string, string, bool) {
  function NewWidget (line 20) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, settin...

FILE: modules/stocks/yfinance/settings.go
  constant defaultFocusable (line 10) | defaultFocusable = false
  constant defaultTitle (line 11) | defaultTitle     = "Yahoo Finance"
  type colors (line 14) | type colors struct
  type Settings (line 22) | type Settings struct
  function NewSettingsFromYAML (line 31) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/stocks/yfinance/widget.go
  type Widget (line 13) | type Widget struct
    method Refresh (line 33) | func (widget *Widget) Refresh() {
    method content (line 41) | func (widget *Widget) content() string {
    method display (line 70) | func (widget *Widget) display() {
  function NewWidget (line 20) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, settin...

FILE: modules/stocks/yfinance/yquote.go
  type MarketState (line 9) | type MarketState
  type yquote (line 11) | type yquote struct
  function tableStyle (line 21) | func tableStyle() table.Style {
  function quotes (line 63) | func quotes(symbols []string) []yquote {
  function GetMarketIcon (line 111) | func GetMarketIcon(state string) string {
  function GetTrendIcon (line 125) | func GetTrendIcon(trend string) string {
  function GetTrend (line 135) | func GetTrend(pct float64) string {

FILE: modules/subreddit/api.go
  function GetLinks (line 13) | func GetLinks(subreddit string, sortMode string, topTimePeriod string) (...

FILE: modules/subreddit/keyboard.go
  method initializeKeyboardControls (line 5) | func (widget *Widget) initializeKeyboardControls() {

FILE: modules/subreddit/link.go
  type Link (line 3) | type Link struct
  type RedditDocument (line 10) | type RedditDocument struct
  type RedditLinkDocument (line 14) | type RedditLinkDocument struct
  type Subreddit (line 18) | type Subreddit struct

FILE: modules/subreddit/settings.go
  constant defaultFocusable (line 9) | defaultFocusable = true
  type Settings (line 13) | type Settings struct
  function NewSettingsFromYAML (line 23) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/subreddit/widget.go
  type Widget (line 11) | type Widget struct
    method Refresh (line 35) | func (widget *Widget) Refresh() {
    method Render (line 55) | func (widget *Widget) Render() {
    method content (line 61) | func (widget *Widget) content() (string, string, bool) {
    method openLink (line 81) | func (widget *Widget) openLink() {
    method openReddit (line 89) | func (widget *Widget) openReddit() {
  function NewWidget (line 19) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, pages ...

FILE: modules/system/settings.go
  constant defaultFocusable (line 9) | defaultFocusable = false
  constant defaultTitle (line 10) | defaultTitle     = "System"
  type Settings (line 13) | type Settings struct
  function NewSettingsFromYAML (line 17) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/system/system_info.go
  type SystemInfo (line 13) | type SystemInfo struct
  function NewSystemInfo (line 19) | func NewSystemInfo() *SystemInfo {

FILE: modules/system/system_info_windows.go
  type SystemInfo (line 10) | type SystemInfo struct
  function NewSystemInfo (line 16) | func NewSystemInfo() *SystemInfo {

FILE: modules/system/widget.go
  type Widget (line 12) | type Widget struct
    method display (line 37) | func (widget *Widget) display() (string, string, bool) {
    method Refresh (line 53) | func (widget *Widget) Refresh() {
    method prettyDate (line 57) | func (widget *Widget) prettyDate() string {
  function NewWidget (line 22) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, date, ...

FILE: modules/textfile/keyboard.go
  method initializeKeyboardControls (line 8) | func (widget *Widget) initializeKeyboardControls() {
  method openFile (line 21) | func (widget *Widget) openFile() {

FILE: modules/textfile/settings.go
  constant defaultFocusable (line 9) | defaultFocusable = true
  constant defaultTitle (line 10) | defaultTitle     = "Textfile"
  type Settings (line 14) | type Settings struct
  function NewSettingsFromYAML (line 24) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/textfile/widget.go
  constant pollingIntervalms (line 21) | pollingIntervalms = 100
  type Widget (line 24) | type Widget struct
    method Refresh (line 59) | func (widget *Widget) Refresh() {
    method content (line 65) | func (widget *Widget) content() (string, string, bool) {
    method formattedText (line 84) | func (widget *Widget) formattedText() string {
    method plainText (line 121) | func (widget *Widget) plainText() string {
    method watchForFileChanges (line 131) | func (widget *Widget) watchForFileChanges() {
  function NewWidget (line 33) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, pages ...

FILE: modules/todo/display.go
  method display (line 13) | func (widget *Widget) display() {
  method content (line 17) | func (widget *Widget) content() (string, string, bool) {
  method sortListByChecked (line 48) | func (widget *Widget) sortListByChecked(firstGroup []*checklist.Checklis...
  method shouldShowItem (line 84) | func (widget *Widget) shouldShowItem(item *checklist.ChecklistItem) bool {
  method RowColor (line 111) | func (widget *Widget) RowColor(idx int, hidden int, checked bool) string {
  method formattedItemLine (line 131) | func (widget *Widget) formattedItemLine(idx int, hidden int, currItem *c...
  method getDateString (line 175) | func (widget *Widget) getDateString(date *time.Time) string {
  function getNowDate (line 220) | func getNowDate() time.Time {

FILE: modules/todo/keyboard.go
  method initializeKeyboardControls (line 12) | func (widget *Widget) initializeKeyboardControls() {
  method NextTodo (line 36) | func (widget *Widget) NextTodo() {
  method PrevTodo (line 47) | func (widget *Widget) PrevTodo() {
  method deleteSelected (line 58) | func (widget *Widget) deleteSelected() {
  method demoteSelected (line 71) | func (widget *Widget) demoteSelected() {
  method makeSelectedLast (line 88) | func (widget *Widget) makeSelectedLast() {
  method openFile (line 112) | func (widget *Widget) openFile() {
  method setTag (line 117) | func (widget *Widget) setTag() {
  method setFilter (line 127) | func (widget *Widget) setFilter() {
  method promoteSelected (line 133) | func (widget *Widget) promoteSelected() {
  method makeSelectedFirst (line 149) | func (widget *Widget) makeSelectedFirst() {
  method toggleChecked (line 173) | func (widget *Widget) toggleChecked() {
  method unselect (line 189) | func (widget *Widget) unselect() {

FILE: modules/todo/settings.go
  constant defaultFocusable (line 9) | defaultFocusable = true
  constant defaultTitle (line 10) | defaultTitle     = "Todo"
  type Settings (line 14) | type Settings struct
  function NewSettingsFromYAML (line 36) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/todo/widget.go
  constant modalHeight (line 22) | modalHeight = 7
  constant modalWidth (line 23) | modalWidth  = 80
  constant offscreen (line 24) | offscreen   = -1000
  type Widget (line 28) | type Widget struct
    method SelectedItem (line 73) | func (widget *Widget) SelectedItem() *checklist.ChecklistItem {
    method Refresh (line 83) | func (widget *Widget) Refresh() {
    method SetList (line 92) | func (widget *Widget) SetList(list checklist.Checklist) {
    method init (line 98) | func (widget *Widget) init() {
    method isItemSelected (line 106) | func (widget *Widget) isItemSelected() bool {
    method load (line 111) | func (widget *Widget) load() error {
    method newItem (line 145) | func (widget *Widget) newItem() {
    method getTextComponents (line 162) | func (widget *Widget) getTextComponents(text string) (string, *time.Ti...
    method getTextAndDate (line 202) | func (widget *Widget) getTextAndDate(text string) (string, *time.Time) {
    method persist (line 276) | func (widget *Widget) persist() {
    method setItemChecks (line 291) | func (widget *Widget) setItemChecks() {
    method updateSelected (line 299) | func (widget *Widget) updateSelected() {
    method processFormInput (line 316) | func (widget *Widget) processFormInput(prompt string, initValue string...
    method updateSelectedItem (line 335) | func (widget *Widget) updateSelectedItem(text string, date *time.Time,...
    method placeItemBasedOnDate (line 346) | func (widget *Widget) placeItemBasedOnDate(index int) int {
    method todoDateIsEarlier (line 360) | func (widget *Widget) todoDateIsEarlier(i, j int) bool {
    method addButtons (line 376) | func (widget *Widget) addButtons(form *tview.Form, saveFctn func()) {
    method addCancelButton (line 381) | func (widget *Widget) addCancelButton(form *tview.Form) {
    method addSaveButton (line 392) | func (widget *Widget) addSaveButton(form *tview.Form, fctn func()) {
    method modalFocus (line 396) | func (widget *Widget) modalFocus(form *tview.Form) {
    method modalForm (line 405) | func (widget *Widget) modalForm(lbl, text string) *tview.Form {
    method modalFrame (line 416) | func (widget *Widget) modalFrame(form *tview.Form) *tview.Frame {
  function NewWidget (line 44) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, pages ...
  function getTodoTags (line 177) | func getTodoTags(text string) (string, []string) {
  type PatternDuration (line 195) | type PatternDuration struct

FILE: modules/todo_plus/backend/backend.go
  type Backend (line 7) | type Backend interface

FILE: modules/todo_plus/backend/project.go
  type Task (line 3) | type Task struct
  type Project (line 9) | type Project struct
    method IsLast (line 19) | func (proj *Project) IsLast() bool {
    method loadTasks (line 23) | func (proj *Project) loadTasks() {
    method LongestLine (line 29) | func (proj *Project) LongestLine() int {
    method currentTask (line 41) | func (proj *Project) currentTask() *Task {
    method CloseSelectedTask (line 49) | func (proj *Project) CloseSelectedTask() {
    method DeleteSelectedTask (line 58) | func (proj *Project) DeleteSelectedTask() {

FILE: modules/todo_plus/backend/todoist.go
  type Todoist (line 10) | type Todoist struct
    method Title (line 15) | func (todo *Todoist) Title() string {
    method Setup (line 19) | func (todo *Todoist) Setup(config *config.Config) {
    method BuildProjects (line 25) | func (todo *Todoist) BuildProjects() []*Project {
    method GetProject (line 36) | func (todo *Todoist) GetProject(id string) *Project {
    method LoadTasks (line 69) | func (todo *Todoist) LoadTasks(id string) ([]Task, error) {
    method CloseTask (line 83) | func (todo *Todoist) CloseTask(task *Task) error {
    method DeleteTask (line 91) | func (todo *Todoist) DeleteTask(task *Task) error {
    method Sources (line 99) | func (todo *Todoist) Sources() []string {
  function toTask (line 61) | func toTask(task api.Task) Task {

FILE: modules/todo_plus/backend/trello.go
  type Trello (line 11) | type Trello struct
    method Title (line 19) | func (todo *Trello) Title() string {
    method Setup (line 23) | func (todo *Trello) Setup(config *config.Config) {
    method BuildProjects (line 92) | func (todo *Trello) BuildProjects() []*Project {
    method GetProject (line 102) | func (todo *Trello) GetProject(id string) *Project {
    method LoadTasks (line 131) | func (todo *Trello) LoadTasks(id string) ([]Task, error) {
    method CloseTask (line 144) | func (todo *Trello) CloseTask(task *Task) error {
    method DeleteTask (line 160) | func (todo *Trello) DeleteTask(_ *Task) error {
    method Sources (line 164) | func (todo *Trello) Sources() []string {
  function getBoardID (line 38) | func getBoardID(client *trello.Client, username, boardName string) (stri...
  function getListId (line 58) | func getListId(client *trello.Client, boardID string, listName string) (...
  function getCardsOnList (line 78) | func getCardsOnList(client *trello.Client, listID string) ([]*trello.Car...
  function fromTrello (line 123) | func fromTrello(task *trello.Card) Task {

FILE: modules/todo_plus/display.go
  method content (line 10) | func (widget *Widget) content() (string, string, bool) {
  method display (line 41) | func (widget *Widget) display() {

FILE: modules/todo_plus/keyboard.go
  method initializeKeyboardControls (line 5) | func (widget *Widget) initializeKeyboardControls() {

FILE: modules/todo_plus/settings.go
  constant defaultTitle (line 11) | defaultTitle     = "Todo"
  constant defaultFocusable (line 12) | defaultFocusable = true
  type Settings (line 15) | type Settings struct
  function NewSettingsFromYAML (line 22) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...
  function FromTodoist (line 36) | func FromTodoist(name string, ymlConfig *config.Config, globalConfig *co...
  function FromTrello (line 53) | func FromTrello(name string, ymlConfig *config.Config, globalConfig *con...

FILE: modules/todo_plus/widget.go
  type Widget (line 12) | type Widget struct
    method CurrentProject (line 58) | func (widget *Widget) CurrentProject() *backend.Project {
    method ProjectAt (line 62) | func (widget *Widget) ProjectAt(idx int) *backend.Project {
    method Refresh (line 70) | func (widget *Widget) Refresh() {
    method NextSource (line 81) | func (widget *Widget) NextSource() {
    method PrevSource (line 88) | func (widget *Widget) PrevSource() {
    method Prev (line 95) | func (widget *Widget) Prev() {
    method Next (line 100) | func (widget *Widget) Next() {
    method Unselect (line 105) | func (widget *Widget) Unselect() {
    method Close (line 114) | func (w *Widget) Close() {
    method Delete (line 127) | func (w *Widget) Delete() {
  function NewWidget (line 22) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, pages ...
  function getBackend (line 41) | func getBackend(backendType string) backend.Backend {

FILE: modules/transmission/display.go
  method display (line 12) | func (widget *Widget) display() {
  method content (line 16) | func (widget *Widget) content() (string, string, bool) {
  method prettyTorrentName (line 49) | func (widget *Widget) prettyTorrentName(name string) string {
  method torrentPercentDone (line 56) | func (widget *Widget) torrentPercentDone(torrent transmissionrpc.Torrent...
  method torrentSeedRatio (line 72) | func (widget *Widget) torrentSeedRatio(torrent transmissionrpc.Torrent) ...
  method torrentState (line 82) | func (widget *Widget) torrentState(torrent transmissionrpc.Torrent) stri...

FILE: modules/transmission/keyboard.go
  method initializeKeyboardControls (line 5) | func (widget *Widget) initializeKeyboardControls() {

FILE: modules/transmission/settings.go
  constant defaultFocusable (line 9) | defaultFocusable = true
  constant defaultTitle (line 10) | defaultTitle     = "Transmission"
  type Settings (line 14) | type Settings struct
  function NewSettingsFromYAML (line 27) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/transmission/widget.go
  type Widget (line 14) | type Widget struct
    method Fetch (line 43) | func (widget *Widget) Fetch() ([]transmissionrpc.Torrent, error) {
    method Refresh (line 68) | func (widget *Widget) Refresh() {
    method Next (line 86) | func (widget *Widget) Next() {
    method Prev (line 91) | func (widget *Widget) Prev() {
    method Unselect (line 96) | func (widget *Widget) Unselect() {
    method currentTorrent (line 121) | func (widget *Widget) currentTorrent() *transmissionrpc.Torrent {
    method deleteSelectedTorrent (line 135) | func (widget *Widget) deleteSelectedTorrent() {
    method pauseUnpauseTorrent (line 161) | func (widget *Widget) pauseUnpauseTorrent() {
  function NewWidget (line 25) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, pages ...
  function buildClient (line 104) | func buildClient(widget *Widget) {

FILE: modules/travisci/client.go
  function BuildsFor (line 16) | func BuildsFor(settings *Settings) (*Builds, error) {
  function travisBuildRequest (line 43) | func travisBuildRequest(settings *Settings) (*http.Response, error) {

FILE: modules/travisci/keyboard.go
  method initializeKeyboardControls (line 5) | func (widget *Widget) initializeKeyboardControls() {

FILE: modules/travisci/settings.go
  constant defaultFocusable (line 11) | defaultFocusable = true
  constant defaultTitle (line 12) | defaultTitle     = "TravisCI"
  type Settings (line 15) | type Settings struct
  function NewSettingsFromYAML (line 26) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/travisci/travis.go
  type Builds (line 3) | type Builds struct
  type Build (line 7) | type Build struct
  type Owner (line 17) | type Owner struct
  type Branch (line 21) | type Branch struct
  type Repository (line 25) | type Repository struct
  type Commit (line 30) | type Commit struct

FILE: modules/travisci/widget.go
  type Widget (line 12) | type Widget struct
    method Refresh (line 35) | func (widget *Widget) Refresh() {
    method Render (line 56) | func (widget *Widget) Render() {
    method content (line 60) | func (widget *Widget) content() (string, string, bool) {
    method openBuild (line 111) | func (widget *Widget) openBuild() {
  function NewWidget (line 20) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, pages ...
  function buildColor (line 90) | func buildColor(build Build) string {

FILE: modules/twitch/client.go
  type Twitch (line 7) | type Twitch struct
    method RefreshOAuthToken (line 55) | func (t *Twitch) RefreshOAuthToken() error {
    method TopStreams (line 78) | func (t *Twitch) TopStreams(params *helix.StreamsParams) (*helix.Strea...
    method FollowedStreams (line 85) | func (t *Twitch) FollowedStreams(params *helix.FollowedStreamsParams) ...
  type ClientOpts (line 14) | type ClientOpts struct
  function NewClient (line 25) | func NewClient(opts *ClientOpts) (*Twitch, error) {

FILE: modules/twitch/keyboard.go
  method initializeKeyboardControls (line 5) | func (widget *Widget) initializeKeyboardControls() {

FILE: modules/twitch/settings.go
  constant defaultFocusable (line 12) | defaultFocusable = true
  type Settings (line 15) | type Settings struct
  function defaultLanguage (line 34) | func defaultLanguage() []interface{} {
  function NewSettingsFromYAML (line 40) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/twitch/widget.go
  type Widget (line 14) | type Widget struct
    method Refresh (line 60) | func (widget *Widget) Refresh() {
    method Render (line 103) | func (widget *Widget) Render() {
    method content (line 127) | func (widget *Widget) content() (string, string, bool) {
    method openTwitch (line 158) | func (widget *Widget) openTwitch() {
    method openStreamlink (line 167) | func (widget *Widget) openStreamlink() {
  type Stream (line 23) | type Stream struct
  function NewWidget (line 31) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, pages ...
  function makeStreams (line 107) | func makeStreams(response *helix.StreamsResponse) []*Stream {
  function handleError (line 121) | func handleError(widget *Widget, err error) {

FILE: modules/twitter/client.go
  type Client (line 19) | type Client struct
    method Tweets (line 59) | func (client *Client) Tweets() []Tweet {
    method getTweets (line 71) | func (client *Client) getTweets() (tweets []Tweet, err error) {
  function NewClient (line 27) | func NewClient(settings *Settings) *Client {

FILE: modules/twitter/keyboard.go
  method initializeKeyboardControls (line 8) | func (widget *Widget) initializeKeyboardControls() {
  method openFile (line 21) | func (widget *Widget) openFile() {

FILE: modules/twitter/request.go
  function Request (line 8) | func Request(httpClient *http.Client, apiURL string) ([]byte, error) {
  function ParseBody (line 23) | func ParseBody(resp *http.Response) ([]byte, error) {

FILE: modules/twitter/settings.go
  constant defaultFocusable (line 11) | defaultFocusable = true
  constant defaultTitle (line 12) | defaultTitle     = "Twitter"
  type Settings (line 15) | type Settings struct
  function NewSettingsFromYAML (line 25) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/twitter/tweet.go
  type Tweet (line 8) | type Tweet struct
    method String (line 14) | func (tweet *Tweet) String() string {
    method Username (line 20) | func (tweet *Tweet) Username() string {
    method Created (line 24) | func (tweet *Tweet) Created() time.Time {
    method PrettyCreatedAt (line 29) | func (tweet *Tweet) PrettyCreatedAt() string {

FILE: modules/twitter/user.go
  type User (line 4) | type User struct

FILE: modules/twitter/widget.go
  type Widget (line 14) | type Widget struct
    method Refresh (line 48) | func (widget *Widget) Refresh() {
    method content (line 54) | func (widget *Widget) content() (string, string, bool) {
    method displayName (line 76) | func (widget *Widget) displayName(tweet Tweet) string {
    method formatText (line 83) | func (widget *Widget) formatText(text string) string {
    method format (line 108) | func (widget *Widget) format(tweet Tweet) string {
    method currentSourceURI (line 125) | func (widget *Widget) currentSourceURI() string {
  function NewWidget (line 23) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, pages ...

FILE: modules/twitterstats/client.go
  type Client (line 16) | type Client struct
    method GetStatsForUser (line 69) | func (client *Client) GetStatsForUser(username string) TwitterStats {
    method GetStats (line 98) | func (client *Client) GetStats() []TwitterStats {
  type TwitterStats (line 22) | type TwitterStats struct
  constant userTimelineURL (line 28) | userTimelineURL = "https://api.twitter.com/1.1/users/show.json"
  function NewClient (line 32) | func NewClient(settings *Settings) *Client {

FILE: modules/twitterstats/settings.go
  constant defaultFocusable (line 11) | defaultFocusable = true
  constant defaultTitle (line 12) | defaultTitle     = "Twitter Stats"
  type Settings (line 15) | type Settings struct
  function NewSettingsFromYAML (line 24) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/twitterstats/widget.go
  type Widget (line 10) | type Widget struct
    method Refresh (line 32) | func (widget *Widget) Refresh() {
    method content (line 36) | func (widget *Widget) content() (string, string, bool) {
  function NewWidget (line 17) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, _ *tvi...

FILE: modules/unknown/settings.go
  constant defaultFocusable (line 9) | defaultFocusable = false
  constant defaultTitle (line 10) | defaultTitle     = "Unknown"
  type Settings (line 13) | type Settings struct
  function NewSettingsFromYAML (line 17) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/unknown/widget.go
  type Widget (line 10) | type Widget struct
    method Refresh (line 28) | func (widget *Widget) Refresh() {
  function NewWidget (line 16) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, settin...

FILE: modules/updown/keyboard.go
  method initializeKeyboardControls (line 3) | func (widget *Widget) initializeKeyboardControls() {

FILE: modules/updown/settings.go
  constant defaultFocusable (line 12) | defaultFocusable = true
  constant defaultTitle (line 13) | defaultTitle     = "Updown.io"
  type Settings (line 16) | type Settings struct
  function NewSettingsFromYAML (line 23) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/updown/widget.go
  constant userAgent (line 15) | userAgent = "WTFUtil"
  constant apiURLBase (line 17) | apiURLBase = "https://updown.io"
  type Widget (line 20) | type Widget struct
    method Refresh (line 76) | func (widget *Widget) Refresh() {
    method Render (line 89) | func (widget *Widget) Render() {
    method content (line 93) | func (widget *Widget) content() (string, string, bool) {
    method contentFrom (line 116) | func (widget *Widget) contentFrom(checks []Check) string {
    method getExistingChecks (line 167) | func (widget *Widget) getExistingChecks() ([]Check, error) {
  type Check (line 29) | type Check struct
  type SSL (line 53) | type SSL struct
  function NewWidget (line 59) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, pages ...
  function timeSincePing (line 142) | func timeSincePing(ts time.Time) string {
  function makeURL (line 147) | func makeURL(baseurl string, path string) (string, error) {
  function filterChecks (line 156) | func filterChecks(checks []Check, tokenSet map[string]struct{}) []Check {

FILE: modules/uptimekuma/keyboard.go
  method initializeKeyboardControls (line 3) | func (widget *Widget) initializeKeyboardControls() {

FILE: modules/uptimekuma/settings.go
  constant defaultFocusable (line 9) | defaultFocusable = true
  constant defaultTitle (line 10) | defaultTitle     = "Uptime Kuma"
  type Settings (line 13) | type Settings struct
  function NewSettingsFromYAML (line 20) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/uptimekuma/widget.go
  type HeartbeatStatus (line 17) | type HeartbeatStatus
  constant DOWN (line 20) | DOWN HeartbeatStatus = iota
  constant UP (line 21) | UP
  constant PENDING (line 22) | PENDING
  constant MAINTENANCE (line 23) | MAINTENANCE
  type StatusPageData (line 27) | type StatusPageData struct
  type Incident (line 32) | type Incident struct
  type HeartbeatData (line 37) | type HeartbeatData struct
  type Heartbeat (line 43) | type Heartbeat struct
  type Widget (line 48) | type Widget struct
    method Refresh (line 72) | func (widget *Widget) Refresh() {
    method content (line 103) | func (widget *Widget) content() string {
    method display (line 169) | func (widget *Widget) display() {
    method fetchStatusData (line 175) | func (*Widget) fetchStatusData(baseURL, slug string) (*StatusPageData,...
    method fetchHeartbeatData (line 195) | func (*Widget) fetchHeartbeatData(baseURL, slug string) (*HeartbeatDat...
  function NewWidget (line 58) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, pages ...
  function parseURL (line 215) | func parseURL(rawURL string) (string, string, error) {

FILE: modules/uptimerobot/keyboard.go
  method initializeKeyboardControls (line 3) | func (widget *Widget) initializeKeyboardControls() {

FILE: modules/uptimerobot/settings.go
  constant defaultFocusable (line 11) | defaultFocusable = true
  constant defaultTitle (line 12) | defaultTitle     = "Uptime Robot"
  type Settings (line 15) | type Settings struct
  function NewSettingsFromYAML (line 23) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/uptimerobot/widget.go
  type Widget (line 16) | type Widget struct
    method Refresh (line 39) | func (widget *Widget) Refresh() {
    method Render (line 69) | func (widget *Widget) Render() {
    method content (line 75) | func (widget *Widget) content() (string, string, bool) {
    method contentFrom (line 98) | func (widget *Widget) contentFrom(monitors []Monitor) string {
    method getMonitors (line 148) | func (widget *Widget) getMonitors() ([]Monitor, error) {
  function NewWidget (line 24) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, pages ...
  function formatUptimes (line 125) | func formatUptimes(str string) string {
  type Monitor (line 140) | type Monitor struct

FILE: modules/urlcheck/client.go
  function DoRequest (line 14) | func DoRequest(urlRequest string, timeout time.Duration, client *http.Cl...

FILE: modules/urlcheck/client_test.go
  function TestTimeout (line 12) | func TestTimeout(t *testing.T) {

FILE: modules/urlcheck/settings.go
  constant defaultFocusable (line 9) | defaultFocusable = false
  constant defaultTitle (line 10) | defaultTitle     = "URLcheck"
  type Settings (line 13) | type Settings struct
  function NewSettingsFromYAML (line 20) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/urlcheck/urlResult.go
  constant InvalidResultCode (line 7) | InvalidResultCode = 999
  type urlResult (line 10) | type urlResult struct
  function newUrlResult (line 18) | func newUrlResult(urlString string) *urlResult {

FILE: modules/urlcheck/urlResult_test.go
  function checkValid (line 9) | func checkValid(t *testing.T, got *urlResult) {
  function checkInvalid (line 15) | func checkInvalid(t *testing.T, got *urlResult) {
  function Test_newUrlResult (line 21) | func Test_newUrlResult(t *testing.T) {

FILE: modules/urlcheck/view.go
  method PrepareTemplate (line 11) | func (widget *Widget) PrepareTemplate() {
  method parseTemplate (line 27) | func (widget *Widget) parseTemplate() *template.Template {
  method FormatResult (line 32) | func (widget *Widget) FormatResult() string {
  function getResultColor (line 48) | func getResultColor(ur urlResult) string {

FILE: modules/urlcheck/widget.go
  type Widget (line 12) | type Widget struct
    method Refresh (line 45) | func (widget *Widget) Refresh() {
    method init (line 53) | func (widget *Widget) init() {
    method check (line 64) | func (widget *Widget) check() {
    method display (line 73) | func (widget *Widget) display() {
  function NewWidget (line 24) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, settin...

FILE: modules/victorops/client.go
  function Fetch (line 13) | func Fetch(apiID, apiKey string) ([]OnCallTeam, error) {
  function victorOpsRequest (line 22) | func victorOpsRequest(url string, apiID string, apiKey string) ([]OnCall...
  function parseTeams (line 53) | func parseTeams(input *OnCallResponse) []OnCallTeam {

FILE: modules/victorops/oncallresponse.go
  type OnCallResponse (line 4) | type OnCallResponse struct

FILE: modules/victorops/oncallteam.go
  type OnCallTeam (line 5) | type OnCallTeam struct
  type OnCall (line 13) | type OnCall struct

FILE: modules/victorops/settings.go
  constant defaultFocusable (line 11) | defaultFocusable = true
  constant defaultTitle (line 12) | defaultTitle     = "VictorOps"
  type Settings (line 15) | type Settings struct
  function NewSettingsFromYAML (line 23) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/victorops/widget.go
  type Widget (line 11) | type Widget struct
    method Refresh (line 33) | func (widget *Widget) Refresh() {
    method content (line 46) | func (widget *Widget) content() (string, string, bool) {
  function NewWidget (line 20) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, settin...

FILE: modules/weatherservices/arpansagovau/client.go
  type Stations (line 10) | type Stations struct
  type location (line 26) | type location struct
  function getLocationData (line 34) | func getLocationData(cityname string) (*location, error) {
  function apiRequest (line 57) | func apiRequest() (*http.Response, error) {
  function parseXML (line 76) | func parseXML(text io.Reader) (Stations, error) {

FILE: modules/weatherservices/arpansagovau/settings.go
  constant defaultFocusable (line 9) | defaultFocusable = false
  constant defaultTitle (line 10) | defaultTitle     = "ARPANSA UV Data"
  type Settings (line 13) | type Settings struct
  function NewSettingsFromYAML (line 19) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/weatherservices/arpansagovau/widget.go
  type Widget (line 10) | type Widget struct
    method content (line 33) | func (widget *Widget) content() (string, string, bool) {
    method Refresh (line 46) | func (widget *Widget) Refresh() {
  function NewWidget (line 18) | func NewWidget(tviewApp *tview.Application, redrawChan chan bool, settin...
  function formatLocationData (line 50) | func formatLocationData(location *location) string {

FILE: modules/weatherservices/prettyweather/settings.go
  constant defaultFocusable (line 9) | defaultFocusable = false
  constant defaultTitle (line 10) | defaultTitle     = "Pretty Weather"
  type Settings (line 13) | type Settings struct
  function NewSettingsFromYAML (line 22) | func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalCo...

FILE: modules/weatherservices/prettyweather/widget.go
  type Widget (line 13) | typ
Condensed preview — 519 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,311K chars).
[
  {
    "path": ".editorconfig",
    "chars": 308,
    "preview": "root = true\n\n[*]\nend_of_line = lf\ninsert_final_newline = true\nmax_line_length=120\n\n[*.go]\nindent_style = tab\nindent_size"
  },
  {
    "path": ".gitattributes",
    "chars": 78,
    "preview": "_site/* linguist-vendored\ndocs/* linguist-vendored\nvendor/* linguist-vendored\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "chars": 22,
    "preview": "github: FelicianoTech\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/Bug.md",
    "chars": 78,
    "preview": "---\nname:  🐞 Report a Bug\nabout: Tell us what's broken\n---\n\n## What's broken?\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/Feature.md",
    "chars": 91,
    "preview": "---\nname: ⚡️ Request a Feature\nabout: Tell us what it should do\n---\n\n## What should it do?\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/Support.md",
    "chars": 84,
    "preview": "---\nname: ❓Ask a Question\nabout: Tell us how we can help\n---\n\n## How can we help?\n\n\n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE/Improvement.md",
    "chars": 199,
    "preview": "---\nname: Improvement\nabout: You have some improvement to make wtf better?\n---\n\nThanks for submitting a pull request. Pl"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE/Other.md",
    "chars": 197,
    "preview": "---\nname: Other\nabout: You have some other ideas you want to introduce?\n---\n\nThanks for submitting a pull request. Pleas"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "chars": 124,
    "preview": "\r\nThanks for submitting a pull request. Please provide enough information so that others can review your pull request.\r\n"
  },
  {
    "path": ".github/dependabot.yml",
    "chars": 307,
    "preview": "version: 2\nupdates:\n- package-ecosystem: gomod\n  directory: \"/\"\n  schedule:\n    interval: daily\n  open-pull-requests-lim"
  },
  {
    "path": ".github/stale.yml",
    "chars": 1990,
    "preview": "# Configuration for probot-stale - https://github.com/probot/stale\n\n# Number of days of inactivity before an Issue or Pu"
  },
  {
    "path": ".github/workflows/codeql-analysis.yml",
    "chars": 434,
    "preview": "name: \"CodeQL Analysis\"\n\non:\n  push:\n    branches:\n      - trunk\n  pull_request:\n\njobs:\n  analyze:\n    runs-on: ubuntu-2"
  },
  {
    "path": ".github/workflows/golangci-lint.yml",
    "chars": 356,
    "preview": "name: golangci-lint\non:\n  push:\n    tags:\n      - v*\n    branches:\n      - trunk\n  pull_request:\njobs:\n  golangci:\n    n"
  },
  {
    "path": ".github/workflows/goreleaser.yml",
    "chars": 868,
    "preview": "name: goreleaser\n\non:\n  push:\n    tags:\n      - '*'\n\njobs:\n  goreleaser:\n    runs-on: ubuntu-24.04\n    steps:\n      - na"
  },
  {
    "path": ".github/workflows/pr-checks.yml",
    "chars": 500,
    "preview": "name: \"PR Checks\"\n\non:\n  pull_request:\n    branches:\n      - trunk\n\njobs:\n  goreleaser:\n    runs-on: ubuntu-24.04\n    st"
  },
  {
    "path": ".github/workflows/staticcheck.yml",
    "chars": 2107,
    "preview": "name: static check\non: pull_request\n\njobs:\n  imports:\n    name: Imports\n    runs-on: ubuntu-latest\n    steps:\n    - uses"
  },
  {
    "path": ".gitignore",
    "chars": 399,
    "preview": "### Go ###\n# Binaries for programs and plugins\n*.exe\n*.exe~\n*.dll\nftw*\n*.so\n*.dylib\n\n# Test binary, build with `go test "
  },
  {
    "path": ".gitmodules",
    "chars": 0,
    "preview": ""
  },
  {
    "path": ".golangci.yml",
    "chars": 243,
    "preview": "version: \"2\"\n\nrun:\n  timeout: 3m\n\nlinters:\n  enable:\n    - govet\n    - errcheck\n    - staticcheck\n    - unconvert\n  excl"
  },
  {
    "path": ".goreleaser.yml",
    "chars": 1453,
    "preview": "version: 2\n\nbuilds:\n  - binary: wtfutil\n    goos:\n      - darwin\n      - linux\n    goarch:\n      - amd64\n      - arm\n   "
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "chars": 3219,
    "preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, w"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 3681,
    "preview": "# Contributing\n\n## Pull Request Process\n\n1. Ensure any install or build dependencies are removed before the end of the l"
  },
  {
    "path": "LICENSE.md",
    "chars": 14789,
    "preview": "*Mozilla Public License, version 2.0* \n\n1. Definitions \n \n1.1. “Contributor” means each individual or legal entity that "
  },
  {
    "path": "Makefile",
    "chars": 4448,
    "preview": ".PHONY: build clean contrib_check coverage docker-build docker-install help install isntall lint run size test uninstall"
  },
  {
    "path": "README.md",
    "chars": 8008,
    "preview": "<p align=\"center\">\n    <img src=\"./images/logo_transparent.png?raw=true\" title=\"WTF\" alt=\"WTF\" width=\"560\" height=\"560\" "
  },
  {
    "path": "SECURITY.md",
    "chars": 81,
    "preview": "# Security Policy\n\nTo file a security issue, open a new Issue in the Issues tab.\n"
  },
  {
    "path": "_sample_configs/bargraph_config.yml",
    "chars": 363,
    "preview": "wtf:\n  colors:\n    border:\n      focusable: darkslateblue\n      focused: orange\n      normal: gray\n  grid:\n    columns: "
  },
  {
    "path": "_sample_configs/dynamic_sizing.yml",
    "chars": 359,
    "preview": "wtf:\n  mods:\n    battery:\n      type: power\n      title: \"⚡️\"\n      enabled: true\n      position:\n        top: 0\n       "
  },
  {
    "path": "_sample_configs/kubernetes_config.yml",
    "chars": 463,
    "preview": "wtf:\n  colors:\n    border:\n      focusable: darkslateblue\n      focused: orange\n      normal: gray\n  grid:\n    columns: "
  },
  {
    "path": "_sample_configs/sample_config.yml",
    "chars": 3941,
    "preview": "wtf:\n  colors:\n    background: black\n    border:\n      focusable: darkslateblue\n      focused: orange\n      normal: gray"
  },
  {
    "path": "_sample_configs/small_config.yml",
    "chars": 278,
    "preview": "wtf:\n  grid:\n    columns: [20, 20]\n    rows: [3, 3]\n  refreshInterval: 1\n  mods:\n    uptime:\n      type: cmdrunner\n     "
  },
  {
    "path": "_sample_configs/uniconfig.yml",
    "chars": 904,
    "preview": "wtf:\n  colors:\n    background: black\n    border:\n      focusable: darkslateblue\n  grid:\n    columns: [40, 40]\n    rows: "
  },
  {
    "path": "app/app_manager.go",
    "chars": 1909,
    "preview": "package app\n\nimport (\n\t\"errors\"\n\n\t\"github.com/olebedev/config\"\n\t\"github.com/rivo/tview\"\n)\n\n// WtfAppManager handles the "
  },
  {
    "path": "app/display.go",
    "chars": 1380,
    "preview": "package app\n\nimport (\n\t\"github.com/olebedev/config\"\n\t\"github.com/rivo/tview\"\n\t\"github.com/wtfutil/wtf/utils\"\n\t\"github.co"
  },
  {
    "path": "app/exit_message.go",
    "chars": 3085,
    "preview": "package app\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"strings\"\n\n\t\"github.com/logrusorgru/aurora/v4\"\n\t\"github.com/olebedev/config\"\n)\n\ncons"
  },
  {
    "path": "app/exit_message_test.go",
    "chars": 1473,
    "preview": "package app\n\nimport (\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/wtfutil/wtf/support\"\n\t\"gotest.tools/assert\"\n)\n\nfunc Test_displ"
  },
  {
    "path": "app/focus_tracker.go",
    "chars": 5674,
    "preview": "package app\n\nimport (\n\t\"fmt\"\n\t\"sort\"\n\n\t\"github.com/olebedev/config\"\n\t\"github.com/rivo/tview\"\n\t\"github.com/wtfutil/wtf/wt"
  },
  {
    "path": "app/module_validator.go",
    "chars": 1874,
    "preview": "package app\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/logrusorgru/aurora/v4\"\n\t\"github.com/wtfutil/wtf/cfg\"\n\t\"github.com/wtfut"
  },
  {
    "path": "app/module_validator_test.go",
    "chars": 1988,
    "preview": "package app\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/logrusorgru/aurora/v4\"\n\t\"github.com/olebedev/config\"\n\t\"github.com/"
  },
  {
    "path": "app/scheduler.go",
    "chars": 580,
    "preview": "package app\n\nimport (\n\t\"time\"\n\n\t\"github.com/wtfutil/wtf/wtf\"\n)\n\n// Schedule kicks off the first refresh of a module's da"
  },
  {
    "path": "app/scheduler_test.go",
    "chars": 1767,
    "preview": "package app\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/olebedev/config\"\n)\n\nconst (\n\tconfigExample = `\n  wtf:\n    mods:\n "
  },
  {
    "path": "app/widget_maker.go",
    "chars": 19280,
    "preview": "package app\n\nimport (\n\t\"github.com/olebedev/config\"\n\t\"github.com/rivo/tview\"\n\t\"github.com/wtfutil/wtf/modules/airbrake\"\n"
  },
  {
    "path": "app/widget_maker_test.go",
    "chars": 1384,
    "preview": "package app\n\nimport (\n\t\"testing\"\n\n\t\"github.com/olebedev/config\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/wtfut"
  },
  {
    "path": "app/wtf_app.go",
    "chars": 6166,
    "preview": "package app\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"time\"\n\n\t_ \"github.com/gdamore/tcell/terminfo/extended\"\n\t\"github.com/gdamore/"
  },
  {
    "path": "cfg/common_settings.go",
    "chars": 8176,
    "preview": "package cfg\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/olebedev/config\"\n\t\"golang.org/x/text/language\"\n\t\"golang.or"
  },
  {
    "path": "cfg/common_settings_test.go",
    "chars": 3686,
    "preview": "package cfg\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/olebedev/config\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nvar (\n\t"
  },
  {
    "path": "cfg/config_files.go",
    "chars": 5317,
    "preview": "package cfg\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"os/user\"\n\t\"path/filepath\"\n\n\t\"github.com/olebedev/config\"\n)\n\nconst (\n\t// X"
  },
  {
    "path": "cfg/copy.go",
    "chars": 1388,
    "preview": "// Copied verbatim from:\n//\n//   https://github.com/otiai10/copy/blob/master/copy.go\n\npackage cfg\n\nimport (\n\t\"io\"\n\t\"os\"\n"
  },
  {
    "path": "cfg/default_color_theme.go",
    "chars": 2254,
    "preview": "package cfg\n\nimport (\n\t\"github.com/olebedev/config\"\n\t\"gopkg.in/yaml.v2\"\n)\n\n// BorderTheme defines the default color sche"
  },
  {
    "path": "cfg/default_color_theme_test.go",
    "chars": 654,
    "preview": "package cfg\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc Test_NewDefaultColorTheme(t *testing.T) "
  },
  {
    "path": "cfg/default_config_file.go",
    "chars": 2042,
    "preview": "package cfg\n\nconst defaultConfigFile = `wtf:\n  colors:\n    border:\n      focusable: darkslateblue\n      focused: orange\n"
  },
  {
    "path": "cfg/error_messages.go",
    "chars": 1458,
    "preview": "package cfg\n\n// This file contains the error messages that get written to the terminal when\n// something goes wrong with"
  },
  {
    "path": "cfg/parsers.go",
    "chars": 1103,
    "preview": "package cfg\n\nimport (\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/olebedev/config\"\n)\n\n// ParseAsMapOrList takes a configuration key and"
  },
  {
    "path": "cfg/parsers_test.go",
    "chars": 2510,
    "preview": "package cfg\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/olebedev/config\"\n)\n\nfunc Test_ParseAsMapOrList(t *testing.T) {\n\tt"
  },
  {
    "path": "cfg/position_settings.go",
    "chars": 1325,
    "preview": "package cfg\n\nimport (\n\t\"github.com/olebedev/config\"\n)\n\nconst (\n\tpositionPath = \"position\"\n)\n\n// PositionSettings represe"
  },
  {
    "path": "cfg/position_validation.go",
    "chars": 1171,
    "preview": "package cfg\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/logrusorgru/aurora/v4\"\n)\n\n// Common examples of invalid position configuratio"
  },
  {
    "path": "cfg/position_validation_test.go",
    "chars": 496,
    "preview": "package cfg\n\nimport (\n\t\"errors\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nvar (\n\tposVal = &positionValidation"
  },
  {
    "path": "cfg/secrets.go",
    "chars": 5153,
    "preview": "package cfg\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"runtime\"\n\n\t\"github.com/docker/docker-credential-helpers/client\"\n\t\"github.com/do"
  },
  {
    "path": "cfg/validatable.go",
    "chars": 191,
    "preview": "package cfg\n\n// Validatable is implemented by any value that validates a configuration setting\ntype Validatable interfac"
  },
  {
    "path": "cfg/validations.go",
    "chars": 579,
    "preview": "package cfg\n\n// Validations represent a collection of config setting validations\ntype Validations struct {\n\tvalidations "
  },
  {
    "path": "cfg/validations_test.go",
    "chars": 577,
    "preview": "package cfg\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nvar (\n\tvals = NewValidations()\n)\n\nfunc Test_i"
  },
  {
    "path": "checklist/checklist.go",
    "chars": 3219,
    "preview": "package checklist\n\nimport (\n\t\"time\"\n)\n\n// Checklist is a module for creating generic checklist implementations\n// See 'T"
  },
  {
    "path": "checklist/checklist_item.go",
    "chars": 1895,
    "preview": "package checklist\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\n// ChecklistItem is a module for creating generic checklist implementation"
  },
  {
    "path": "checklist/checklist_item_test.go",
    "chars": 601,
    "preview": "package checklist\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc testChecklistItem() *ChecklistItem"
  },
  {
    "path": "checklist/checklist_test.go",
    "chars": 9015,
    "preview": "package checklist\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc Test_NewCheckist(t *testing.T) {\n\t"
  },
  {
    "path": "flags/flags.go",
    "chars": 5268,
    "preview": "package flags\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"runtime/debug\"\n\t\"strings\"\n\n\t\"github.com/chzyer/readline\"\n\tgoFlag"
  },
  {
    "path": "generator/settings.tpl",
    "chars": 777,
    "preview": "package {{(Lower .Name)}}\n\nimport (\n\t\"github.com/olebedev/config\"\n\t\"github.com/wtfutil/wtf/cfg\"\n)\n\nconst (\n\tdefaultFocus"
  },
  {
    "path": "generator/textwidget.go",
    "chars": 2072,
    "preview": "//go:build ignore\n\n// This package takes care of generates for empty widgets. Each generator is named after the\n// type "
  },
  {
    "path": "generator/textwidget.tpl",
    "chars": 1060,
    "preview": "package {{(Lower .Name)}}\n\nimport (\n\t\"github.com/rivo/tview\"\n\t\"github.com/wtfutil/wtf/view\"\n)\n\n// Widget is the containe"
  },
  {
    "path": "go.mod",
    "chars": 13398,
    "preview": "module github.com/wtfutil/wtf\n\ngo 1.26.1\n\nrequire (\n\tbitbucket.org/mikehouston/asana-go v0.0.0-20201102222432-715318d034"
  },
  {
    "path": "go.sum",
    "chars": 117891,
    "preview": "bitbucket.org/mikehouston/asana-go v0.0.0-20201102222432-715318d0343a h1:qH51iOpTres3x2kNb0f2R3ggMpbYCyCvaRrsvdndhvY=\nbi"
  },
  {
    "path": "help/help.go",
    "chars": 980,
    "preview": "package help\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/olebedev/config\"\n\t\"github.com/wtfutil/wtf/app\"\n\t\"github.com/wtfutil/wtf/util"
  },
  {
    "path": "logger/log.go",
    "chars": 611,
    "preview": "package logger\n\nimport (\n\t\"log\"\n\t\"os\"\n\t\"path/filepath\"\n)\n\n/* -------------------- Exported Functions -------------------"
  },
  {
    "path": "main.go",
    "chars": 1581,
    "preview": "package main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\n\t// Blank import of tzdata embeds the timezone database to allow Windows hos"
  },
  {
    "path": "modules/airbrake/client.go",
    "chars": 2931,
    "preview": "package airbrake\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\n\t\"github.com/wtfutil/wtf/utils\"\n)\n\nfunc project(projectID int, authToken "
  },
  {
    "path": "modules/airbrake/group_info_table.go",
    "chars": 1184,
    "preview": "package airbrake\n\nimport (\n\t\"fmt\"\n\t\"strconv\"\n\n\t\"github.com/wtfutil/wtf/view\"\n)\n\ntype groupInfoTable struct {\n\tgroup     "
  },
  {
    "path": "modules/airbrake/keyboard.go",
    "chars": 1008,
    "preview": "package airbrake\n\nimport \"github.com/gdamore/tcell/v2\"\n\nfunc (widget *Widget) initializeKeyboardControls() {\n\twidget.Ini"
  },
  {
    "path": "modules/airbrake/result_table.go",
    "chars": 704,
    "preview": "package airbrake\n\nimport (\n\t\"github.com/wtfutil/wtf/utils\"\n\t\"github.com/wtfutil/wtf/view\"\n)\n\ntype resultTable struct {\n\t"
  },
  {
    "path": "modules/airbrake/settings.go",
    "chars": 972,
    "preview": "package airbrake\n\nimport (\n\t\"os\"\n\t\"strconv\"\n\n\t\"github.com/olebedev/config\"\n\t\"github.com/wtfutil/wtf/cfg\"\n)\n\nconst (\n\tdef"
  },
  {
    "path": "modules/airbrake/util.go",
    "chars": 179,
    "preview": "package airbrake\n\nfunc reverseString(s string) string {\n\tr := []rune(s)\n\tfor i, j := 0, len(r)-1; i < len(r)/2; i, j = i"
  },
  {
    "path": "modules/airbrake/widget.go",
    "chars": 7003,
    "preview": "package airbrake\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/rivo/tview\"\n\t\"github.com/wtfutil/wtf/utils\"\n\t\"github"
  },
  {
    "path": "modules/asana/client.go",
    "chars": 6202,
    "preview": "package asana\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\t\"time\"\n\n\tasana \"bitbucket.org/mikehouston/asana-go\"\n)\n\nfunc fetchTasksFromPro"
  },
  {
    "path": "modules/asana/keyboard.go",
    "chars": 939,
    "preview": "package asana\n\nimport \"github.com/gdamore/tcell/v2\"\n\nfunc (widget *Widget) initializeKeyboardControls() {\n\twidget.Initia"
  },
  {
    "path": "modules/asana/settings.go",
    "chars": 1994,
    "preview": "package asana\n\nimport (\n\t\"github.com/olebedev/config\"\n\t\"github.com/wtfutil/wtf/cfg\"\n\t\"github.com/wtfutil/wtf/utils\"\n)\n\nc"
  },
  {
    "path": "modules/asana/widget.go",
    "chars": 5396,
    "preview": "package asana\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"github.com/rivo/tview\"\n\t\"github.com/wtfutil/wtf/utils\"\n\t\"gith"
  },
  {
    "path": "modules/azuredevops/client.go",
    "chars": 1892,
    "preview": "package azuredevops\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\n\tazrBuild \"github.com/microsoft/azure-devops-go-api/azuredevops/build\"\n"
  },
  {
    "path": "modules/azuredevops/example-conf.yml",
    "chars": 1190,
    "preview": "wtf:\n  colors:\n    # background: black\n    # foreground: blue\n    border:\n      focusable: darkslateblue\n      focused: "
  },
  {
    "path": "modules/azuredevops/settings.go",
    "chars": 1266,
    "preview": "package azuredevops\n\nimport (\n\t\"os\"\n\n\t\"github.com/olebedev/config\"\n\t\"github.com/wtfutil/wtf/cfg\"\n)\n\nconst (\n\tdefaultFocu"
  },
  {
    "path": "modules/azuredevops/widget.go",
    "chars": 1559,
    "preview": "package azuredevops\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\tazr \"github.com/microsoft/azure-devops-go-api/azuredevops\"\n\tazrBuild \""
  },
  {
    "path": "modules/azurelogs/config.go",
    "chars": 1638,
    "preview": "package azurelogs\n\nimport (\n\t_ \"embed\"\n\t\"fmt\"\n\t\"os\"\n\n\t\"gopkg.in/yaml.v3\"\n)\n\n// QueryFile represents the structure of a q"
  },
  {
    "path": "modules/azurelogs/config_test.go",
    "chars": 7153,
    "preview": "package azurelogs\n\nimport (\n\t\"os\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/requir"
  },
  {
    "path": "modules/azurelogs/query.go",
    "chars": 3189,
    "preview": "package azurelogs\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"sync\"\n\n\t\"github.com/Azure/azure-sdk-for-go/sdk/azcore/to\"\n\t\"github.com/A"
  },
  {
    "path": "modules/azurelogs/query_concurrent_test.go",
    "chars": 5071,
    "preview": "package azurelogs\n\nimport (\n\t\"fmt\"\n\t\"sync\"\n\t\"testing\"\n\n\t\"github.com/Azure/azure-sdk-for-go/sdk/monitor/azquery\"\n\t\"github"
  },
  {
    "path": "modules/azurelogs/query_test.go",
    "chars": 2555,
    "preview": "package azurelogs\n\nimport (\n\t\"testing\"\n\n\t\"github.com/Azure/azure-sdk-for-go/sdk/monitor/azquery\"\n\t\"github.com/stretchr/t"
  },
  {
    "path": "modules/azurelogs/session.go",
    "chars": 3212,
    "preview": "package azurelogs\n\nimport (\n\t\"fmt\"\n\t\"github.com/Azure/azure-sdk-for-go/sdk/azcore\"\n\t\"github.com/Azure/azure-sdk-for-go/s"
  },
  {
    "path": "modules/azurelogs/session_test.go",
    "chars": 5628,
    "preview": "package azurelogs\n\nimport (\n\t\"os\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestAZClientSecretCredential"
  },
  {
    "path": "modules/azurelogs/settings.go",
    "chars": 832,
    "preview": "package azurelogs\n\nimport (\n\t\"github.com/olebedev/config\"\n\n\t\"github.com/wtfutil/wtf/cfg\"\n)\n\nconst (\n\tdefaultFocusable = "
  },
  {
    "path": "modules/azurelogs/settings_test.go",
    "chars": 4466,
    "preview": "package azurelogs\n\nimport (\n\t\"testing\"\n\n\t\"github.com/olebedev/config\"\n\t\"github.com/stretchr/testify/assert\"\n\n\t\"github.co"
  },
  {
    "path": "modules/azurelogs/widget.go",
    "chars": 7574,
    "preview": "package azurelogs\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/Azure/azure-sdk-for-go/sdk/azcore/to\"\n\n\t\"github.com/"
  },
  {
    "path": "modules/azurelogs/widget_test.go",
    "chars": 8843,
    "preview": "package azurelogs\n\nimport (\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/rivo/tview\"\n\t\"github.com/stretchr/testify/assert"
  },
  {
    "path": "modules/bamboohr/calendar.go",
    "chars": 751,
    "preview": "package bamboohr\n\ntype Calendar struct {\n\tItems []Item `xml:\"item\"`\n}\n\n/* -------------------- Public Functions --------"
  },
  {
    "path": "modules/bamboohr/client.go",
    "chars": 1494,
    "preview": "package bamboohr\n\nimport (\n\t\"encoding/xml\"\n\t\"fmt\"\n)\n\n// A Client represents the data required to connect to the BambooHR"
  },
  {
    "path": "modules/bamboohr/employee.go",
    "chars": 269,
    "preview": "package bamboohr\n\n/*\n* Note: this currently implements the minimum number of fields to fulfill the Away functionality.\n*"
  },
  {
    "path": "modules/bamboohr/item.go",
    "chars": 815,
    "preview": "package bamboohr\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/wtfutil/wtf/wtf\"\n)\n\ntype Item struct {\n\tEmployee Employee `xml:\"employee"
  },
  {
    "path": "modules/bamboohr/request.go",
    "chars": 652,
    "preview": "package bamboohr\n\nimport (\n\t\"bytes\"\n\t\"net/http\"\n)\n\nfunc Request(apiKey string, apiURL string) ([]byte, error) {\n\treq, er"
  },
  {
    "path": "modules/bamboohr/settings.go",
    "chars": 829,
    "preview": "package bamboohr\n\nimport (\n\t\"os\"\n\n\t\"github.com/olebedev/config\"\n\t\"github.com/wtfutil/wtf/cfg\"\n)\n\nconst (\n\tdefaultFocusab"
  },
  {
    "path": "modules/bamboohr/widget.go",
    "chars": 1600,
    "preview": "package bamboohr\n\nimport (\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/rivo/tview\"\n\t\"github.com/wtfutil/wtf/utils\"\n\t\"github.com/wtfutil"
  },
  {
    "path": "modules/bargraph/settings.go",
    "chars": 455,
    "preview": "package bargraph\n\nimport (\n\t\"github.com/olebedev/config\"\n\t\"github.com/wtfutil/wtf/cfg\"\n)\n\nconst (\n\tdefaultFocusable = fa"
  },
  {
    "path": "modules/bargraph/widget.go",
    "chars": 1322,
    "preview": "package bargraph\n\n/**************\nThis is a demo bargraph that just populates some random date/val data\n*/\n\nimport (\n\t\"m"
  },
  {
    "path": "modules/buildkite/client.go",
    "chars": 2349,
    "preview": "package buildkite\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\n\t\"github.com/wtfutil/wtf/utils\"\n)\n\ntype Pipeline struct {\n\tSlug string `"
  },
  {
    "path": "modules/buildkite/keyboard.go",
    "chars": 190,
    "preview": "package buildkite\n\nfunc (widget *Widget) initializeKeyboardControls() {\n\twidget.InitializeHelpTextKeyboardControl(widget"
  },
  {
    "path": "modules/buildkite/pipelines_display_data.go",
    "chars": 2386,
    "preview": "package buildkite\n\nimport (\n\t\"fmt\"\n\t\"sort\"\n\t\"strings\"\n)\n\ntype pipelinesDisplayData struct {\n\tbuildsForPipeline map[strin"
  },
  {
    "path": "modules/buildkite/settings.go",
    "chars": 1740,
    "preview": "package buildkite\n\nimport (\n\t\"os\"\n\n\t\"github.com/olebedev/config\"\n\t\"github.com/wtfutil/wtf/cfg\"\n\t\"github.com/wtfutil/wtf/"
  },
  {
    "path": "modules/buildkite/widget.go",
    "chars": 1317,
    "preview": "package buildkite\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/rivo/tview\"\n\t\"github.com/wtfutil/wtf/view\"\n)\n\ntype Widget struct {\n\tvie"
  },
  {
    "path": "modules/cds/favorites/display.go",
    "chars": 2055,
    "preview": "package cdsfavorites\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/ovh/cds/sdk\"\n)\n\nfunc (widget *Widget) display() {\n\twidget.Redraw(wid"
  },
  {
    "path": "modules/cds/favorites/keyboard.go",
    "chars": 1072,
    "preview": "package cdsfavorites\n\nimport (\n\t\"github.com/gdamore/tcell/v2\"\n)\n\nfunc (widget *Widget) initializeKeyboardControls() {\n\tw"
  },
  {
    "path": "modules/cds/favorites/settings.go",
    "chars": 1072,
    "preview": "package cdsfavorites\n\nimport (\n\t\"os\"\n\n\t\"github.com/olebedev/config\"\n\t\"github.com/wtfutil/wtf/cfg\"\n\t\"github.com/wtfutil/w"
  },
  {
    "path": "modules/cds/favorites/widget.go",
    "chars": 3638,
    "preview": "package cdsfavorites\n\nimport (\n\t\"fmt\"\n\t\"strconv\"\n\n\t\"github.com/ovh/cds/sdk\"\n\t\"github.com/ovh/cds/sdk/cdsclient\"\n\t\"github"
  },
  {
    "path": "modules/cds/queue/display.go",
    "chars": 2841,
    "preview": "package cdsqueue\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/ovh/cds/sdk\"\n\t\"github.com/ovh/cds/sdk/cdsclient\"\n)\n\nf"
  },
  {
    "path": "modules/cds/queue/keyboard.go",
    "chars": 1068,
    "preview": "package cdsqueue\n\nimport (\n\t\"github.com/gdamore/tcell/v2\"\n)\n\nfunc (widget *Widget) initializeKeyboardControls() {\n\twidge"
  },
  {
    "path": "modules/cds/queue/settings.go",
    "chars": 910,
    "preview": "package cdsqueue\n\nimport (\n\t\"os\"\n\n\t\"github.com/olebedev/config\"\n\t\"github.com/wtfutil/wtf/cfg\"\n)\n\nconst (\n\tdefaultFocusab"
  },
  {
    "path": "modules/cds/queue/widget.go",
    "chars": 3498,
    "preview": "package cdsqueue\n\nimport (\n\t\"fmt\"\n\t\"strconv\"\n\n\t\"github.com/ovh/cds/sdk\"\n\t\"github.com/ovh/cds/sdk/cdsclient\"\n\t\"github.com"
  },
  {
    "path": "modules/cds/status/display.go",
    "chars": 2157,
    "preview": "package cdsstatus\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/ovh/cds/sdk\"\n)\n\nfunc (widget *Widget) display() {\n\twidget.Re"
  },
  {
    "path": "modules/cds/status/keyboard.go",
    "chars": 742,
    "preview": "package cdsstatus\n\nimport (\n\t\"github.com/gdamore/tcell/v2\"\n)\n\nfunc (widget *Widget) initializeKeyboardControls() {\n\twidg"
  },
  {
    "path": "modules/cds/status/settings.go",
    "chars": 913,
    "preview": "package cdsstatus\n\nimport (\n\t\"os\"\n\n\t\"github.com/olebedev/config\"\n\t\"github.com/wtfutil/wtf/cfg\"\n)\n\nconst (\n\tdefaultFocusa"
  },
  {
    "path": "modules/cds/status/widget.go",
    "chars": 2984,
    "preview": "package cdsstatus\n\nimport (\n\t\"fmt\"\n\t\"strconv\"\n\n\t\"github.com/ovh/cds/sdk\"\n\t\"github.com/ovh/cds/sdk/cdsclient\"\n\t\"github.co"
  },
  {
    "path": "modules/circleci/build.go",
    "chars": 272,
    "preview": "package circleci\n\ntype Build struct {\n\tAuthorEmail string `json:\"author_email\"`\n\tAuthorName  string `json:\"author_name\"`"
  },
  {
    "path": "modules/circleci/client.go",
    "chars": 1462,
    "preview": "package circleci\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\n\t\"github.com/wtfutil/wtf/utils\"\n)\n\ntype Client "
  },
  {
    "path": "modules/circleci/settings.go",
    "chars": 818,
    "preview": "package circleci\n\nimport (\n\t\"os\"\n\n\t\"github.com/olebedev/config\"\n\t\"github.com/wtfutil/wtf/cfg\"\n)\n\nconst (\n\tdefaultFocusab"
  },
  {
    "path": "modules/circleci/widget.go",
    "chars": 1466,
    "preview": "package circleci\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/rivo/tview\"\n\t\"github.com/wtfutil/wtf/view\"\n)\n\ntype Widget struct {\n\tview"
  },
  {
    "path": "modules/clocks/clock.go",
    "chars": 931,
    "preview": "package clocks\n\nimport (\n\t\"strings\"\n\t\"time\"\n)\n\ntype Clock struct {\n\tLabel    string\n\tLocation *time.Location\n}\n\nfunc New"
  },
  {
    "path": "modules/clocks/clock_collection.go",
    "chars": 1132,
    "preview": "package clocks\n\nimport (\n\t\"sort\"\n\t\"time\"\n)\n\ntype ClockCollection struct {\n\tClocks []Clock\n}\n\nfunc (clocks *ClockCollecti"
  },
  {
    "path": "modules/clocks/display.go",
    "chars": 704,
    "preview": "package clocks\n\nimport \"fmt\"\n\nfunc (widget *Widget) display(clocks []Clock, dateFormat string, timeFormat string) {\n\tstr"
  },
  {
    "path": "modules/clocks/settings.go",
    "chars": 2375,
    "preview": "package clocks\n\nimport (\n\t\"github.com/olebedev/config\"\n\t\"github.com/wtfutil/wtf/cfg\"\n\t\"github.com/wtfutil/wtf/utils\"\n)\n\n"
  },
  {
    "path": "modules/clocks/widget.go",
    "chars": 1100,
    "preview": "package clocks\n\nimport (\n\t\"github.com/rivo/tview\"\n\t\"github.com/wtfutil/wtf/view\"\n)\n\ntype Widget struct {\n\tview.TextWidge"
  },
  {
    "path": "modules/cmdrunner/settings.go",
    "chars": 2286,
    "preview": "package cmdrunner\n\nimport (\n\t\"github.com/olebedev/config\"\n\t\"github.com/wtfutil/wtf/cfg\"\n\t\"github.com/wtfutil/wtf/utils\"\n"
  },
  {
    "path": "modules/cmdrunner/widget.go",
    "chars": 5126,
    "preview": "package cmdrunner\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"os/exec\"\n\t\"os/signal\"\n\t\"strings\"\n\t\"sync\"\n\t\"syscall\"\n"
  },
  {
    "path": "modules/cryptocurrency/bittrex/bittrex.go",
    "chars": 1072,
    "preview": "package bittrex\n\ntype summaryList struct {\n\titems []*bCurrency\n}\n\n// Base Currency\ntype bCurrency struct {\n\tname        "
  },
  {
    "path": "modules/cryptocurrency/bittrex/display.go",
    "chars": 1833,
    "preview": "package bittrex\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"text/template\"\n)\n\nfunc (widget *Widget) display() {\n\n\twidget.Redraw(widget.c"
  },
  {
    "path": "modules/cryptocurrency/bittrex/settings.go",
    "chars": 1469,
    "preview": "package bittrex\n\nimport (\n\t\"github.com/olebedev/config\"\n\t\"github.com/wtfutil/wtf/cfg\"\n)\n\nconst (\n\tdefaultFocusable = fal"
  },
  {
    "path": "modules/cryptocurrency/bittrex/widget.go",
    "chars": 3571,
    "preview": "package bittrex\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"net/http\"\n\n\t\"github.com/rivo/tview\"\n\t\"github.com/wtfutil/wt"
  },
  {
    "path": "modules/cryptocurrency/blockfolio/settings.go",
    "chars": 761,
    "preview": "package blockfolio\n\nimport (\n\t\"github.com/olebedev/config\"\n\t\"github.com/wtfutil/wtf/cfg\"\n)\n\nconst (\n\tdefaultFocusable = "
  },
  {
    "path": "modules/cryptocurrency/blockfolio/widget.go",
    "chars": 3459,
    "preview": "package blockfolio\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"net/http\"\n\n\t\"github.com/rivo/tview\"\n\t\"github.com/wtf"
  },
  {
    "path": "modules/cryptocurrency/cryptolive/price/price.go",
    "chars": 511,
    "preview": "package price\n\ntype list struct {\n\titems []*fromCurrency\n}\n\ntype fromCurrency struct {\n\tname        string\n\tdisplayName "
  },
  {
    "path": "modules/cryptocurrency/cryptolive/price/settings.go",
    "chars": 1770,
    "preview": "package price\n\nimport (\n\t\"github.com/olebedev/config\"\n\t\"github.com/wtfutil/wtf/cfg\"\n)\n\nconst (\n\tdefaultFocusable = false"
  },
  {
    "path": "modules/cryptocurrency/cryptolive/price/widget.go",
    "chars": 2914,
    "preview": "package price\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"sync\"\n\t\"time\"\n)\n\nvar baseURL = \"https://min-api.cryptocomp"
  },
  {
    "path": "modules/cryptocurrency/cryptolive/settings.go",
    "chars": 1998,
    "preview": "package cryptolive\n\nimport (\n\t\"github.com/olebedev/config\"\n\t\"github.com/wtfutil/wtf/cfg\"\n\t\"github.com/wtfutil/wtf/module"
  },
  {
    "path": "modules/cryptocurrency/cryptolive/toplist/display.go",
    "chars": 1246,
    "preview": "package toplist\n\nimport \"fmt\"\n\nfunc (widget *Widget) display() {\n\tstr := \"\"\n\tfor _, fromCurrency := range widget.list.it"
  },
  {
    "path": "modules/cryptocurrency/cryptolive/toplist/settings.go",
    "chars": 1860,
    "preview": "package toplist\n\nimport (\n\t\"github.com/olebedev/config\"\n\t\"github.com/wtfutil/wtf/cfg\"\n)\n\nconst (\n\tdefaultFocusable = fal"
  },
  {
    "path": "modules/cryptocurrency/cryptolive/toplist/toplist.go",
    "chars": 846,
    "preview": "package toplist\n\ntype cList struct {\n\titems []*fCurrency\n}\n\ntype fCurrency struct {\n\tname, displayName string\n\tlimit    "
  },
  {
    "path": "modules/cryptocurrency/cryptolive/toplist/widget.go",
    "chars": 2252,
    "preview": "package toplist\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"os\"\n\t\"sync\"\n\t\"time\"\n)\n\nvar baseURL = \"https://min-api.cr"
  },
  {
    "path": "modules/cryptocurrency/cryptolive/widget.go",
    "chars": 1525,
    "preview": "package cryptolive\n\nimport (\n\t\"fmt\"\n\t\"sync\"\n\n\t\"github.com/rivo/tview\"\n\t\"github.com/wtfutil/wtf/modules/cryptocurrency/cr"
  },
  {
    "path": "modules/cryptocurrency/mempool/settings.go",
    "chars": 749,
    "preview": "package mempool\n\nimport (\n\t\"github.com/olebedev/config\"\n\t\"github.com/wtfutil/wtf/cfg\"\n)\n\nconst (\n\tdefaultFocusable = fal"
  },
  {
    "path": "modules/cryptocurrency/mempool/widget.go",
    "chars": 2166,
    "preview": "package mempool\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\n\t\"github.com/rivo/tview\"\n\t\"github.com/wtfutil/wtf/logger\"\n\t\"github.com/wtf"
  },
  {
    "path": "modules/datadog/client.go",
    "chars": 466,
    "preview": "package datadog\n\nimport (\n\t\"github.com/wtfutil/wtf/utils\"\n\tdatadog \"github.com/zorkian/go-datadog-api\"\n)\n\n// Monitors re"
  },
  {
    "path": "modules/datadog/keyboard.go",
    "chars": 728,
    "preview": "package datadog\n\nimport (\n\t\"github.com/gdamore/tcell/v2\"\n)\n\nfunc (widget *Widget) initializeKeyboardControls() {\n\twidget"
  },
  {
    "path": "modules/datadog/settings.go",
    "chars": 1084,
    "preview": "package datadog\n\nimport (\n\t\"os\"\n\n\t\"github.com/olebedev/config\"\n\t\"github.com/wtfutil/wtf/cfg\"\n)\n\nconst (\n\tdefaultFocusabl"
  },
  {
    "path": "modules/datadog/widget.go",
    "chars": 2510,
    "preview": "package datadog\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/rivo/tview\"\n\t\"github.com/wtfutil/wtf/utils\"\n\t\"github.com/wtfutil/wtf/view"
  },
  {
    "path": "modules/devto/keyboard.go",
    "chars": 725,
    "preview": "package devto\n\nimport \"github.com/gdamore/tcell/v2\"\n\nfunc (widget *Widget) initializeKeyboardControls() {\n\twidget.Initia"
  },
  {
    "path": "modules/devto/settings.go",
    "chars": 1270,
    "preview": "package devto\n\nimport (\n\t\"github.com/olebedev/config\"\n\n\t\"github.com/wtfutil/wtf/cfg\"\n)\n\nconst (\n\tdefaultFocusable = true"
  },
  {
    "path": "modules/devto/widget.go",
    "chars": 2661,
    "preview": "package devto\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/VictorAvelar/devto-api-go/devto\"\n\t\"github.com/rivo/tview\"\n\n\t\"git"
  },
  {
    "path": "modules/digitalclock/clocks.go",
    "chars": 1858,
    "preview": "package digitalclock\n\nimport (\n\t\"fmt\"\n\t\"strconv\"\n\t\"time\"\n)\n\n// AM defines the AM string format\nconst AM = \"A\"\n\n// PM def"
  },
  {
    "path": "modules/digitalclock/display.go",
    "chars": 1051,
    "preview": "package digitalclock\n\nimport \"strings\"\n\nfunc mergeLines(outString []string) string {\n\treturn strings.Join(outString, \"\\n"
  },
  {
    "path": "modules/digitalclock/fonts.go",
    "chars": 3437,
    "preview": "package digitalclock\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n)\n\n// ClockFontInterface to makes sure all fonts implement join and get"
  },
  {
    "path": "modules/digitalclock/settings.go",
    "chars": 1885,
    "preview": "package digitalclock\n\nimport (\n\t\"github.com/olebedev/config\"\n\t\"github.com/wtfutil/wtf/cfg\"\n)\n\nconst (\n\tdefaultFocusable "
  },
  {
    "path": "modules/digitalclock/widget.go",
    "chars": 765,
    "preview": "package digitalclock\n\nimport (\n\t\"github.com/rivo/tview\"\n\t\"github.com/wtfutil/wtf/view\"\n)\n\n// Widget is a text widget str"
  },
  {
    "path": "modules/digitalocean/display.go",
    "chars": 1411,
    "preview": "package digitalocean\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/wtfutil/wtf/utils\"\n)\n\nconst maxColWidth = 12\n\nfunc (widget *Widget) "
  },
  {
    "path": "modules/digitalocean/droplet.go",
    "chars": 1050,
    "preview": "package digitalocean\n\nimport (\n\t\"strings\"\n\n\t\"github.com/digitalocean/godo\"\n\t\"github.com/wtfutil/wtf/utils\"\n)\n\n// Droplet"
  },
  {
    "path": "modules/digitalocean/droplet_properties_table.go",
    "chars": 2330,
    "preview": "package digitalocean\n\nimport (\n\t\"fmt\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/wtfutil/wtf/utils\"\n\t\"github.com/wtfutil/wtf/vi"
  },
  {
    "path": "modules/digitalocean/keyboard.go",
    "chars": 1139,
    "preview": "package digitalocean\n\nimport \"github.com/gdamore/tcell/v2\"\n\nfunc (widget *Widget) initializeKeyboardControls() {\n\twidget"
  },
  {
    "path": "modules/digitalocean/settings.go",
    "chars": 1426,
    "preview": "package digitalocean\n\nimport (\n\t\"os\"\n\n\t\"github.com/olebedev/config\"\n\t\"github.com/wtfutil/wtf/cfg\"\n\t\"github.com/wtfutil/w"
  },
  {
    "path": "modules/digitalocean/widget.go",
    "chars": 6192,
    "preview": "package digitalocean\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\n\t\"github.com/digitalocean/godo\"\n\t\"github.com/rivo/tview\"\n\t\"g"
  },
  {
    "path": "modules/docker/client.go",
    "chars": 3953,
    "preview": "package docker\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"sort\"\n\t\"strings\"\n\n\t\"github.com/docker/docker/api/types\"\n\t\"github.com/docker"
  },
  {
    "path": "modules/docker/example-conf.yml",
    "chars": 838,
    "preview": "wtf:\n  colors:\n    # background: black\n    # foreground: blue\n    border:\n      focusable: darkslateblue\n      focused: "
  },
  {
    "path": "modules/docker/settings.go",
    "chars": 697,
    "preview": "package docker\n\nimport (\n\t\"github.com/olebedev/config\"\n\t\"github.com/wtfutil/wtf/cfg\"\n)\n\nconst (\n\tdefaultFocusable = fals"
  },
  {
    "path": "modules/docker/utils.go",
    "chars": 529,
    "preview": "package docker\n\nimport (\n\t\"fmt\"\n\t\"math\"\n\t\"reflect\"\n\t\"strconv\"\n)\n\nfunc padSlice(padLeft bool, slice interface{}, getter f"
  },
  {
    "path": "modules/docker/widget.go",
    "chars": 1585,
    "preview": "package docker\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/docker/docker/client\"\n\t\"github.com/pkg/errors\"\n\t\"github.com/rivo/tview\"\n\t\""
  },
  {
    "path": "modules/feedreader/keyboard.go",
    "chars": 841,
    "preview": "package feedreader\n\nimport \"github.com/gdamore/tcell/v2\"\n\nfunc (widget *Widget) initializeKeyboardControls() {\n\twidget.I"
  },
  {
    "path": "modules/feedreader/settings.go",
    "chars": 3359,
    "preview": "package feedreader\n\nimport (\n\t\"github.com/olebedev/config\"\n\t\"github.com/wtfutil/wtf/cfg\"\n\t\"github.com/wtfutil/wtf/utils\""
  },
  {
    "path": "modules/feedreader/widget.go",
    "chars": 6400,
    "preview": "package feedreader\n\nimport (\n\t\"crypto/tls\"\n\t\"fmt\"\n\t\"html\"\n\t\"net/http\"\n\t\"regexp\"\n\t\"sort\"\n\t\"strings\"\n\n\t\"github.com/mmcdole"
  },
  {
    "path": "modules/feedreader/widget_test.go",
    "chars": 1697,
    "preview": "package feedreader\n\nimport (\n\t\"testing\"\n\n\t\"github.com/mmcdole/gofeed\"\n\t\"gotest.tools/assert\"\n)\n\nfunc Test_getShowText(t "
  },
  {
    "path": "modules/football/client.go",
    "chars": 865,
    "preview": "package football\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n)\n\nvar (\n\tfootballAPIUrl = \"https://api.football-data.org/v2\"\n)\n\ntype leag"
  },
  {
    "path": "modules/football/settings.go",
    "chars": 1511,
    "preview": "package football\n\nimport (\n\t\"os\"\n\n\t\"github.com/olebedev/config\"\n\t\"github.com/wtfutil/wtf/cfg\"\n)\n\nconst (\n\tdefaultFocusab"
  },
  {
    "path": "modules/football/types.go",
    "chars": 1027,
    "preview": "package football\n\ntype Team struct {\n\tName string `json:\"name\"`\n}\n\ntype LeagueStandings struct {\n\tStandings []struct {\n\t"
  },
  {
    "path": "modules/football/util.go",
    "chars": 670,
    "preview": "package football\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/olekukonko/tablewriter\"\n)\n\nfunc createTable("
  },
  {
    "path": "modules/football/widget.go",
    "chars": 5200,
    "preview": "package football\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/rivo/tview\"\n\t\"gith"
  },
  {
    "path": "modules/gcal/cal_event.go",
    "chars": 2688,
    "preview": "package gcal\n\nimport (\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/wtfutil/wtf/utils\"\n\t\"google.golang.org/api/calendar/v3\"\n)\n\ntype CalE"
  },
  {
    "path": "modules/gcal/client.go",
    "chars": 6565,
    "preview": "/*\n* This butt-ugly code is direct from Google itself\n* https://developers.google.com/calendar/quickstart/go\n*\n* With so"
  },
  {
    "path": "modules/gcal/display.go",
    "chars": 4706,
    "preview": "package gcal\n\nimport (\n\t\"fmt\"\n\t\"regexp\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/wtfutil/wtf/utils\"\n)\n\nfunc (widget *Widget) dis"
  },
  {
    "path": "modules/gcal/display_test.go",
    "chars": 1953,
    "preview": "package gcal\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/wtfutil/wtf/cfg\"\n\t\"google.golang.o"
  },
  {
    "path": "modules/gcal/settings.go",
    "chars": 5527,
    "preview": "package gcal\n\nimport (\n\t\"github.com/olebedev/config\"\n\t\"github.com/wtfutil/wtf/cfg\"\n)\n\nconst (\n\tdefaultFocusable = true\n\t"
  },
  {
    "path": "modules/gcal/widget.go",
    "chars": 1109,
    "preview": "package gcal\n\nimport (\n\t\"github.com/rivo/tview\"\n\t\"github.com/wtfutil/wtf/view\"\n)\n\ntype Widget struct {\n\tview.TextWidget\n"
  },
  {
    "path": "modules/gerrit/display.go",
    "chars": 2310,
    "preview": "package gerrit\n\nimport (\n\t\"fmt\"\n)\n\nfunc (widget *Widget) display() {\n\twidget.Redraw(widget.content)\n}\n\nfunc (widget *Wid"
  },
  {
    "path": "modules/gerrit/gerrit_repo.go",
    "chars": 2518,
    "preview": "package gerrit\n\nimport (\n\t\"context\"\n\n\tglb \"github.com/andygrunwald/go-gerrit\"\n)\n\ntype GerritProject struct {\n\tgerrit *gl"
  },
  {
    "path": "modules/gerrit/keyboard.go",
    "chars": 1008,
    "preview": "package gerrit\n\nimport (\n\t\"github.com/gdamore/tcell/v2\"\n)\n\nfunc (widget *Widget) initializeKeyboardControls() {\n\twidget."
  },
  {
    "path": "modules/gerrit/settings.go",
    "chars": 1818,
    "preview": "package gerrit\n\nimport (\n\t\"os\"\n\n\t\"github.com/olebedev/config\"\n\t\"github.com/wtfutil/wtf/cfg\"\n)\n\nconst (\n\tdefaultFocusable"
  },
  {
    "path": "modules/gerrit/widget.go",
    "chars": 3692,
    "preview": "package gerrit\n\nimport (\n\t\"context\"\n\t\"crypto/tls\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"regexp\"\n\n\tglb \"github.com/andygrunwald/go-gerrit\""
  },
  {
    "path": "modules/git/display.go",
    "chars": 2691,
    "preview": "package git\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\t\"unicode/utf8\"\n)\n\nfunc (widget *Widget) display() {\n\twidget.Redraw(widget.conte"
  },
  {
    "path": "modules/git/git_repo.go",
    "chars": 2356,
    "preview": "package git\n\nimport (\n\t\"fmt\"\n\t\"os/exec\"\n\t\"strings\"\n\n\t\"github.com/wtfutil/wtf/utils\"\n)\n\ntype GitRepo struct {\n\tBranch    "
  },
  {
    "path": "modules/git/keyboard.go",
    "chars": 651,
    "preview": "package git\n\nimport \"github.com/gdamore/tcell/v2\"\n\nfunc (widget *Widget) initializeKeyboardControls() {\n\twidget.Initiali"
  }
]

// ... and 319 more files (download for full content)

About this extraction

This page contains the full source code of the wtfutil/wtf GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 519 files (1.1 MB), approximately 358.6k tokens, and a symbol index with 2585 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!