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>
[](https://github.com/wtfutil/wtf/releases)
[](https://goreportcard.com/report/github.com/wtfutil/wtf)
[](https://discuss.linodians.com/c/projects/wtf/7)
[](https://bsky.app/profile/wtfutil.bsky.social)
[](https://social.linodians.com/@WTFutil)

---
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
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
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.