├── .github └── workflows │ ├── build.yaml │ ├── go-mod-tidy.yaml │ └── release.yaml ├── .gitignore ├── .go-version ├── .goreleaser.yaml ├── FyneApp.toml ├── LICENSE.md ├── README.md ├── assets ├── Icon.png ├── fynodoro.desktop ├── notification.mp3 └── notify.sh ├── go.mod ├── go.sum ├── main.go ├── pomodoro ├── pomodoro.go └── pomodoro_test.go ├── pref └── pref.go ├── screenshots ├── app.pimped.png ├── app.png ├── app.shadow.png ├── banner.png ├── banner.xcf ├── codioful-formerly-gradienta-WDSN62Qdxuk-unsplash.jpg ├── hero.jpg ├── hero.xcf ├── settings.pimped.png └── settings.png └── ui ├── assets.go ├── format_duration.go ├── format_duration_test.go ├── integer_entry.go ├── notify.go ├── range_validator.go ├── range_validator_test.go ├── settings.go ├── tappable_text.go └── ui.go /.github/workflows/build.yaml: -------------------------------------------------------------------------------- 1 | name: build 2 | on: 3 | push: 4 | branches: 5 | - '**' 6 | concurrency: 7 | group: ${{ github.workflow }}-${{ github.ref }} 8 | cancel-in-progress: true 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v4 14 | - name: Install Dev dependencies 15 | run: sudo apt-get update && sudo apt-get install gcc libgl1-mesa-dev libegl1-mesa-dev libgles2-mesa-dev libx11-dev xorg-dev 16 | - uses: WillAbides/setup-go-faster@v1.14.0 17 | with: 18 | go-version: '1.23.1' 19 | - name: Generate assets 20 | run: | 21 | go install fyne.io/fyne/v2/cmd/fyne@latest 22 | echo "go generate" 23 | go generate -v ./... 24 | echo "Git status" 25 | git status --short 26 | # Ensure repo clean after go generate 27 | git diff-index --quiet HEAD -- 28 | - name: Build 29 | run: go build -v ./... 30 | - name: Test 31 | run: go test -v ./... 32 | -------------------------------------------------------------------------------- /.github/workflows/go-mod-tidy.yaml: -------------------------------------------------------------------------------- 1 | name: 'go-mod-tidy' 2 | on: 3 | push: 4 | branches: 5 | - 'master' 6 | paths: 7 | - 'go.mod' 8 | - 'go.sum' 9 | jobs: 10 | fix: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v4 14 | - uses: WillAbides/setup-go-faster@v1.14.0 15 | with: 16 | go-version: '1.23.1' 17 | - name: tidy 18 | uses: paulvollmer/action-golang-gosum@v1.0.0 19 | env: 20 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 21 | -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | name: release 2 | on: 3 | push: 4 | tags: 5 | - '*' 6 | permissions: 7 | contents: write 8 | jobs: 9 | goreleaser: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v4 13 | with: 14 | fetch-depth: 0 15 | - name: Install Dev dependencies 16 | run: sudo apt-get update && sudo apt-get install gcc libgl1-mesa-dev libegl1-mesa-dev libgles2-mesa-dev libx11-dev xorg-dev 17 | - uses: WillAbides/setup-go-faster@v1.14.0 18 | with: 19 | go-version: '1.23.1' 20 | - name: Install UPX 21 | uses: crazy-max/ghaction-upx@v3 22 | with: 23 | install-only: true 24 | - name: UPX version 25 | run: upx --version 26 | - name: Install Cloudsmith CLI 27 | run: pipx install cloudsmith-cli 28 | - name: Release 29 | uses: goreleaser/goreleaser-action@v6 30 | with: 31 | version: '~> v2' 32 | args: release --clean 33 | env: 34 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 35 | - name: Publish to Cloudsmith 36 | env: 37 | CLOUDSMITH_API_KEY: ${{ secrets.CLOUDSMITH_API_KEY }} 38 | run: | 39 | for filename in dist/*.deb; do 40 | cloudsmith push deb tomsquest/fynodoro/any-distro/any-version $filename 41 | done 42 | for filename in dist/*.rpm; do 43 | cloudsmith push rpm tomsquest/fynodoro/any-distro/any-version $filename 44 | done 45 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | -------------------------------------------------------------------------------- /.go-version: -------------------------------------------------------------------------------- 1 | 1.23.1 2 | -------------------------------------------------------------------------------- /.goreleaser.yaml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://goreleaser.com/static/schema.json 2 | 3 | version: 2 4 | 5 | before: 6 | hooks: 7 | - go install fyne.io/fyne/v2/cmd/fyne@latest 8 | - go mod tidy 9 | - go generate ./... 10 | # Verifiable build 11 | # https://goreleaser.com/customization/gomod/#go-modules 12 | gomod: 13 | proxy: true 14 | builds: 15 | - targets: 16 | - linux_amd64 17 | # Reproducible build 18 | # https://goreleaser.com/customization/build/#reproducible-builds 19 | mod_timestamp: '{{ .CommitTimestamp }}' 20 | flags: 21 | - -trimpath 22 | ldflags: 23 | - -s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.commitDate={{.CommitDate}} 24 | # Enable macOS "Universal Binaries" (amd64 + arm binaries) 25 | universal_binaries: 26 | - replace: true 27 | upx: 28 | - enabled: true 29 | compress: best 30 | lzma: true 31 | archives: 32 | # Upload the binaries, not archives 33 | - format: binary 34 | checksum: 35 | name_template: '{{ .ProjectName }}_checksums.txt' 36 | nfpms: 37 | - maintainer: Thomas Queste 38 | description: Fynodoro is a tiny and cute Pomodoro Widget 39 | homepage: https://github.com/tomsquest/fynodoro 40 | vendor: Tom's Quest 41 | license: MIT 42 | section: utils 43 | formats: 44 | - deb 45 | - rpm 46 | contents: 47 | - src: assets/Icon.png 48 | dst: /usr/share/pixmaps/fynodoro.png 49 | - src: assets/fynodoro.desktop 50 | dst: /usr/local/share/applications/fynodoro.desktop 51 | - src: assets/notification.mp3 52 | dst: /usr/share/fynodoro/notification.mp3 53 | - src: assets/notify.sh 54 | dst: /usr/share/fynodoro/notify.sh 55 | release: 56 | draft: false 57 | footer: | 58 | **Full Changelog**: https://github.com/tomsquest/fynodoro/compare/{{ .PreviousTag }}...{{ .Tag }} 59 | changelog: 60 | sort: asc 61 | use: github 62 | filters: 63 | exclude: 64 | - '^doc:' 65 | - '^ci:' 66 | - '^test:' 67 | - Merge pull request 68 | - Merge remote-tracking branch 69 | - Merge branch 70 | - go mod tidy 71 | groups: 72 | - title: 'New Features' 73 | regexp: "^.*feat[(\\w)]*:+.*$" 74 | order: 0 75 | - title: 'Bug fixes' 76 | regexp: "^.*fix[(\\w)]*:+.*$" 77 | order: 10 78 | - title: Other work 79 | order: 999 80 | -------------------------------------------------------------------------------- /FyneApp.toml: -------------------------------------------------------------------------------- 1 | Website = "https://github.com/tomsquest/fynodoro" 2 | 3 | [Details] 4 | Name = "Fynodoro" 5 | ID = "github.com/tomsquest/fynodoro" 6 | Icon = "assets/Icon.png" 7 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Thomas Queste 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | Fynodoro hero 3 |

4 | 5 | # Fynodoro is a tiny and cute Pomodoro Widget 6 | 7 | ![GitHub release)](https://img.shields.io/github/v/release/tomsquest/fynodoro?style=flat-square) 8 | ![GitHub status](https://img.shields.io/github/actions/workflow/status/tomsquest/fynodoro/build.yaml?branch=master&style=flat-square) 9 | 10 | 11 | 12 | 13 | ## Table of contents 14 | 15 | - [Screenshots](#screenshots) 16 | - [Timer](#timer) 17 | - [Settings](#settings) 18 | - [Features](#features) 19 | - [Changelog](#changelog) 20 | - [Configuration](#configuration) 21 | - [Notification sound](#notification-sound) 22 | - [Install](#install) 23 | - [Install Ubuntu/Debian (.deb)](#install-ubuntudebian-deb) 24 | - [Install Fedora/Redhat (.rpm)](#install-fedoraredhat-rpm) 25 | - [Download Linux binaries](#download-linux-binaries) 26 | - [Releasing the project](#releasing-the-project) 27 | - [TODO & IDEAS](#todo--ideas) 28 | - [Credits](#credits) 29 | 30 | 31 | 32 | ## Screenshots 33 | 34 | ### Timer 35 | 36 |

37 | Fynodoro app screenshot 38 |

39 | 40 | ### Settings 41 | 42 |

43 | Fynodoro settings screenshot 44 |

45 | 46 | ## Features 47 | 48 | - ⏲️ Pomodoro Timer ✨ 49 | - 🗒️ Configurable: work duration, short break/long break, work rounds 🖊️ 50 | - 🏆 Small download size 51 | - 💼 Releases for Ubuntu/Debian, Fedora/Redhat, and as linux binary 52 | 53 | ## Changelog 54 | 55 | See the [Releases](https://github.com/tomsquest/fynodoro/releases) section on GitHub. 56 | 57 | ## Configuration 58 | 59 | The Pomodoro technique defaults to 4 work rounds of 25 minutes, with a 5 minutes pause ("short break") in-between and a final 15 minutes pause (the "long" break), for a total of 2 hours (4x25m Work + 3x5m Short breaks + 1x15m Long break). 60 | 61 | You can **configure**: 62 | 63 | - the duration in minutes of the Work period (default: `25` minutes) 64 | - the duration in minutes of the Short breaks (default: `5` minutes) 65 | - the duration in minutes of the Long breaks (default: `15` minutes) 66 | - the number of Work rounds before a long break (default: `4` rounds) 67 | 68 | You can **disable** Long breaks by setting the duration of Long breaks to `0` or the number of work rounds to `0`. This will make the timer do a Work period, then a Short break, and so-on, and never do Long break. 69 | 70 | You can **disable** Short breaks by setting the duration of Short breaks to `0`. This will make the timer do a Work period, then a Long break, and so-on, and never do Short break. 71 | 72 | Tips: you can **disable** both Short and Long breaks by setting them to `0`. The timer will then act as a ticker, notifying you after each Work period. 73 | 74 | ### Notification sound 75 | 76 | The notification sound is stored in: `/usr/share/fynodoro/notification.mp3`. 77 | You can override it if you wish with another **mp3** file. 78 | 79 | ## Install 80 | 81 | ### Install Ubuntu/Debian (.deb) 82 | 83 | Latest: [![Latest version of 'fynodoro' @ Cloudsmith](https://api-prd.cloudsmith.io/v1/badges/version/tomsquest/fynodoro/deb/fynodoro/latest/a=amd64;d=any-distro%252Fany-version;t=binary/?render=true&show_latest=true)](https://cloudsmith.io/~tomsquest/repos/fynodoro/packages/detail/deb/fynodoro/latest/a=amd64;d=any-distro%252Fany-version;t=binary/#install) 84 | 85 | Add the repository and install Fynodoro: 86 | 87 | ```shell 88 | curl -1sLf 'https://dl.cloudsmith.io/public/tomsquest/fynodoro/setup.deb.sh' | sudo -E bash 89 | sudo apt install fynodoro 90 | ``` 91 | 92 | _[Complete instructions at CloudSmith.io](https://cloudsmith.io/~tomsquest/repos/fynodoro/packages/detail/deb/fynodoro/latest/a=amd64;d=any-distro%252Fany-version;t=binary/#install)_ 93 | 94 | ### Install Fedora/Redhat (.rpm) 95 | 96 | Latest: [![Latest version of 'fynodoro' @ Cloudsmith](https://api-prd.cloudsmith.io/v1/badges/version/tomsquest/fynodoro/rpm/fynodoro/latest/a=x86_64;d=any-distro%252Fany-version;t=binary/?render=true&show_latest=true)](https://cloudsmith.io/~tomsquest/repos/fynodoro/packages/detail/rpm/fynodoro/latest/a=x86_64;d=any-distro%252Fany-version;t=binary/#install) 97 | 98 | Add the repository and install Fynodoro: 99 | 100 | ```shell 101 | curl -1sLf 'https://dl.cloudsmith.io/public/tomsquest/fynodoro/setup.rpm.sh' | sudo -E bash 102 | # Choose between: 103 | sudo dnf install fynodoro 104 | sudo yum install fynodoro 105 | sudo microdnf install fynodoro 106 | sudo zypper install fynodoro 107 | ``` 108 | 109 | _[Complete instructions at CloudSmith.io](https://cloudsmith.io/~tomsquest/repos/fynodoro/packages/detail/rpm/fynodoro/latest/a=x86_64;d=any-distro%252Fany-version;t=binary/#install)_ 110 | 111 | ### Download Linux binaries 112 | 113 | Latest: [![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/tomsquest/fynodoro?style=flat-square)](https://github.com/tomsquest/fynodoro/releases) 114 | 115 | See the [Releases](https://github.com/tomsquest/fynodoro/releases) section for downloads. 116 | 117 | ## Releasing the project 118 | 119 | Push a new tag from a clean master: 120 | 121 | ```shell 122 | git checkout master && git pull 123 | if ! [ -z "$(git status --untracked-files=no --porcelain)" ]; then 124 | echo "Warning: there are some local changes" 125 | fi 126 | git fetch --all --tags 127 | TAG=v1.0.0 && git tag $TAG && git push origin $TAG 128 | # Publish Release draft: https://github.com/tomsquest/fynodoro/releases 129 | ``` 130 | 131 | ## TODO & IDEAS 132 | 133 | - [ ] Display the type of Pomodoro: Work/Short/Long 134 | - [ ] Display the time left in the Tray (see [Fomato](https://github.com/andydotxyz/fomato) 135 | - [ ] Resizable UI (scale timer and buttons with the window) 136 | - [ ] Pico/Nano/Normal UI presets 137 | - [ ] option for Dark/Light mode (what about Fyne global preferences?) 138 | - [ ] option to disable notification popup 139 | - [ ] option to change notification script 140 | - [ ] Keyboard shortcuts 141 | - [ ] Release Windows, macOS, Android, IOS versions 142 | 143 | ## Credits 144 | 145 | - Fynodoro is built in Go, using the awesome [Fyne toolkit](https://fyne.io) for the UI 146 | - [CloudSmith.io](https://cloudsmith.io) for hosting the packages 147 | - [Go Releaser](https://goreleaser.com) for the release automation 148 | - Icon made by [Freepik](https://www.freepik.com) from [Flaticon](https://www.flaticon.com/free-icon/tomato_877814) 149 | - Screenshot pimped with [PrettySnap](https://prettysnap.app) 150 | - Notification sound from [Zedge](https://www.zedge.net/ringtones) 151 | -------------------------------------------------------------------------------- /assets/Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomsquest/fynodoro/d13e58f45d15ac4d44df3a1790bfbea9ab31b925/assets/Icon.png -------------------------------------------------------------------------------- /assets/fynodoro.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Type=Application 3 | Name=Fynodoro 4 | GenericName=Timer 5 | Comment=Pomodoro widget 6 | Exec=/usr/bin/fynodoro 7 | Icon=/usr/share/pixmaps/fynodoro.png 8 | Categories=Utility;Clock; 9 | -------------------------------------------------------------------------------- /assets/notification.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomsquest/fynodoro/d13e58f45d15ac4d44df3a1790bfbea9ab31b925/assets/notification.mp3 -------------------------------------------------------------------------------- /assets/notify.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # This script is used to notify the user when the pomodoro is done. 4 | # 5 | 6 | SOUND_FILE="/usr/share/fynodoro/notification.mp3" 7 | 8 | if ! [ -r "$SOUND_FILE" ]; then 9 | echo "Missing sound file: $SOUND_FILE" 10 | exit 1 11 | fi 12 | 13 | if command -v ffplay &> /dev/null; then 14 | ffplay "$SOUND_FILE" -nodisp -nostats -hide_banner -autoexit &> /dev/null 15 | elif command -v mplayer &> /dev/null; then 16 | mplayer "$SOUND_FILE" -really-quiet -noconsolecontrols -nolirc -nojoystick -nomouseinput -nogui &> /dev/null 17 | elif command -v mpv &> /dev/null; then 18 | mpv "$SOUND_FILE" --really-quiet --no-audio-display &> /dev/null 19 | elif command -v play &> /dev/null; then 20 | play "$SOUND_FILE" &> /dev/null; 21 | else 22 | echo "No player found. Please install ffplay, mplayer, mpv, or sox." 23 | exit 1 24 | fi 25 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/tomsquest/fynodoro 2 | 3 | go 1.23.1 4 | 5 | require ( 6 | fyne.io/fyne/v2 v2.5.2 7 | github.com/benbjohnson/clock v1.3.5 8 | github.com/godbus/dbus/v5 v5.1.0 // indirect 9 | github.com/stretchr/testify v1.9.0 10 | golang.org/x/sys v0.26.0 // indirect 11 | golang.org/x/text v0.19.0 // indirect; indirect-- 12 | gopkg.in/yaml.v3 v3.0.1 // indirect 13 | ) 14 | 15 | require github.com/gen2brain/beeep v0.0.0-20240516210008-9c006672e7f4 16 | 17 | require ( 18 | fyne.io/systray v1.11.0 // indirect 19 | github.com/BurntSushi/toml v1.4.0 // indirect 20 | github.com/davecgh/go-spew v1.1.1 // indirect 21 | github.com/fredbi/uri v1.1.0 // indirect 22 | github.com/fsnotify/fsnotify v1.7.0 // indirect 23 | github.com/fyne-io/gl-js v0.0.0-20230506162202-1fdaa286a934 // indirect 24 | github.com/fyne-io/glfw-js v0.0.0-20240101223322-6e1efdc71b7a // indirect 25 | github.com/fyne-io/image v0.0.0-20240417123036-dc0ee9e7c964 // indirect 26 | github.com/go-gl/gl v0.0.0-20231021071112-07e5d0ea2e71 // indirect 27 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a // indirect 28 | github.com/go-text/render v0.2.0 // indirect 29 | github.com/go-text/typesetting v0.2.0 // indirect 30 | github.com/go-toast/toast v0.0.0-20190211030409-01e6764cf0a4 // indirect 31 | github.com/gopherjs/gopherjs v1.17.2 // indirect 32 | github.com/jeandeaual/go-locale v0.0.0-20240223122105-ce5225dcaa49 // indirect 33 | github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25 // indirect 34 | github.com/nicksnyder/go-i18n/v2 v2.4.1 // indirect 35 | github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect 36 | github.com/pmezard/go-difflib v1.0.0 // indirect 37 | github.com/rymdport/portal v0.2.6 // indirect 38 | github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c // indirect 39 | github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef // indirect 40 | github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af // indirect 41 | github.com/yuin/goldmark v1.7.7 // indirect 42 | golang.org/x/image v0.21.0 // indirect 43 | golang.org/x/mobile v0.0.0-20241004191011-08a83c5af9f8 // indirect 44 | golang.org/x/net v0.30.0 // indirect 45 | ) 46 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 3 | cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= 4 | cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= 5 | cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= 6 | cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= 7 | cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= 8 | cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= 9 | cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= 10 | cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= 11 | cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= 12 | cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= 13 | cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= 14 | cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= 15 | cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= 16 | cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= 17 | cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= 18 | cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= 19 | cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= 20 | cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= 21 | cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= 22 | cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= 23 | cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= 24 | cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= 25 | cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= 26 | cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= 27 | cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= 28 | cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= 29 | cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= 30 | cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= 31 | cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= 32 | cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= 33 | cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= 34 | cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= 35 | cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= 36 | cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= 37 | cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= 38 | cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= 39 | dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= 40 | fyne.io/fyne/v2 v2.5.2 h1:eSyGTmSkv10yAdAeHpDet6u2KkKxOGFc14kQu81We7Q= 41 | fyne.io/fyne/v2 v2.5.2/go.mod h1:26gqPDvtaxHeyct+C0BBjuGd2zwAJlPkUGSBrb+d7Ug= 42 | fyne.io/systray v1.11.0 h1:D9HISlxSkx+jHSniMBR6fCFOUjk1x/OOOJLa9lJYAKg= 43 | fyne.io/systray v1.11.0/go.mod h1:RVwqP9nYMo7h5zViCBHri2FgjXF7H2cub7MAq4NSoLs= 44 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 45 | github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= 46 | github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= 47 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 48 | github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= 49 | github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= 50 | github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= 51 | github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= 52 | github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= 53 | github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= 54 | github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= 55 | github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= 56 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 57 | github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= 58 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= 59 | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= 60 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 61 | github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= 62 | github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= 63 | github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= 64 | github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 65 | github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= 66 | github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 67 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 68 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 69 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 70 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 71 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 72 | github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= 73 | github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= 74 | github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= 75 | github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= 76 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 77 | github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= 78 | github.com/felixge/fgprof v0.9.3 h1:VvyZxILNuCiUCSXtPtYmmtGvb65nqXh2QFWc0Wpf2/g= 79 | github.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw= 80 | github.com/fredbi/uri v1.1.0 h1:OqLpTXtyRg9ABReqvDGdJPqZUxs8cyBDOMXBbskCaB8= 81 | github.com/fredbi/uri v1.1.0/go.mod h1:aYTUoAXBOq7BLfVJ8GnKmfcuURosB1xyHDIfWeC/iW4= 82 | github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= 83 | github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= 84 | github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= 85 | github.com/fyne-io/gl-js v0.0.0-20230506162202-1fdaa286a934 h1:dZC5aKobSN07hf71oMivxUmAofFja5GrfPK2rBlttX4= 86 | github.com/fyne-io/gl-js v0.0.0-20230506162202-1fdaa286a934/go.mod h1:d4clgH0/GrRwWjRzJJQXxT/h1TyuNSfF/X64zb/3Ggg= 87 | github.com/fyne-io/glfw-js v0.0.0-20240101223322-6e1efdc71b7a h1:ybgRdYvAHTn93HW79bLiBiJwVL4jVeyGQRZMgImoeWs= 88 | github.com/fyne-io/glfw-js v0.0.0-20240101223322-6e1efdc71b7a/go.mod h1:gsGA2dotD4v0SR6PmPCYvS9JuOeMwAtmfvDE7mbYXMY= 89 | github.com/fyne-io/image v0.0.0-20240417123036-dc0ee9e7c964 h1:0pTELtjlVAVGSazfwRNcqTVzqmkWb1GsNozCmmZfdZA= 90 | github.com/fyne-io/image v0.0.0-20240417123036-dc0ee9e7c964/go.mod h1:J9Uunu842kOcTjzQj4Eq8XIDmF55szvT1PTS1cUb1UE= 91 | github.com/gen2brain/beeep v0.0.0-20240516210008-9c006672e7f4 h1:ygs9POGDQpQGLJPlq4+0LBUmMBNox1N4JSpw+OETcvI= 92 | github.com/gen2brain/beeep v0.0.0-20240516210008-9c006672e7f4/go.mod h1:0W7dI87PvXJ1Sjs0QPvWXKcQmNERY77e8l7GFhZB/s4= 93 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 94 | github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6/go.mod h1:9YTyiznxEY1fVinfM7RvRcjRHbw2xLBJ3AAGIT0I4Nw= 95 | github.com/go-gl/gl v0.0.0-20231021071112-07e5d0ea2e71 h1:5BVwOaUSBTlVZowGO6VZGw2H/zl9nrd3eCZfYV+NfQA= 96 | github.com/go-gl/gl v0.0.0-20231021071112-07e5d0ea2e71/go.mod h1:9YTyiznxEY1fVinfM7RvRcjRHbw2xLBJ3AAGIT0I4Nw= 97 | github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= 98 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 99 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 100 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a h1:vxnBhFDDT+xzxf1jTJKMKZw3H0swfWk9RpWbBbDK5+0= 101 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 102 | github.com/go-text/render v0.2.0 h1:LBYoTmp5jYiJ4NPqDc2pz17MLmA3wHw1dZSVGcOdeAc= 103 | github.com/go-text/render v0.2.0/go.mod h1:CkiqfukRGKJA5vZZISkjSYrcdtgKQWRa2HIzvwNN5SU= 104 | github.com/go-text/typesetting v0.2.0 h1:fbzsgbmk04KiWtE+c3ZD4W2nmCRzBqrqQOvYlwAOdho= 105 | github.com/go-text/typesetting v0.2.0/go.mod h1:2+owI/sxa73XA581LAzVuEBZ3WEEV2pXeDswCH/3i1I= 106 | github.com/go-text/typesetting-utils v0.0.0-20240317173224-1986cbe96c66 h1:GUrm65PQPlhFSKjLPGOZNPNxLCybjzjYBzjfoBGaDUY= 107 | github.com/go-text/typesetting-utils v0.0.0-20240317173224-1986cbe96c66/go.mod h1:DDxDdQEnB70R8owOx3LVpEFvpMK9eeH1o2r0yZhFI9o= 108 | github.com/go-toast/toast v0.0.0-20190211030409-01e6764cf0a4 h1:qZNfIGkIANxGv/OqtnntR4DfOY2+BgwR60cAcu/i3SE= 109 | github.com/go-toast/toast v0.0.0-20190211030409-01e6764cf0a4/go.mod h1:kW3HQ4UdaAyrUCSSDR4xUzBKW6O2iA4uHhk7AtyYp10= 110 | github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= 111 | github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= 112 | github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= 113 | github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= 114 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 115 | github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 116 | github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 117 | github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 118 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 119 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 120 | github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= 121 | github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 122 | github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 123 | github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 124 | github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= 125 | github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= 126 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 127 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 128 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 129 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 130 | github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 131 | github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= 132 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 133 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 134 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 135 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 136 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 137 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 138 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 139 | github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 140 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 141 | github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= 142 | github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 143 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 144 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 145 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 146 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 147 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 148 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 149 | github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 150 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 151 | github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 152 | github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 153 | github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 154 | github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 155 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 156 | github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 157 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 158 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= 159 | github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= 160 | github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= 161 | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 162 | github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 163 | github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 164 | github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 165 | github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 166 | github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 167 | github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 168 | github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 169 | github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 170 | github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 171 | github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 172 | github.com/google/pprof v0.0.0-20211214055906-6f57359322fd h1:1FjCyPC+syAzJ5/2S8fqdZK1R22vvA0J7JZKcuOIQ7Y= 173 | github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg= 174 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 175 | github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 176 | github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= 177 | github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= 178 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= 179 | github.com/gopherjs/gopherjs v0.0.0-20211219123610-ec9572f70e60/go.mod h1:cz9oNYuRUWGdHmLF2IodMLkAhcPtXeULvcBNagUrxTI= 180 | github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g= 181 | github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k= 182 | github.com/goxjs/gl v0.0.0-20210104184919-e3fafc6f8f2a/go.mod h1:dy/f2gjY09hwVfIyATps4G2ai7/hLwLkc5TrPqONuXY= 183 | github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= 184 | github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= 185 | github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= 186 | github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 187 | github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= 188 | github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= 189 | github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= 190 | github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= 191 | github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= 192 | github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= 193 | github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= 194 | github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 195 | github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 196 | github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= 197 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 198 | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 199 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 200 | github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= 201 | github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= 202 | github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= 203 | github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= 204 | github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 205 | github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 206 | github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= 207 | github.com/jeandeaual/go-locale v0.0.0-20240223122105-ce5225dcaa49 h1:Po+wkNdMmN+Zj1tDsJQy7mJlPlwGNQd9JZoPjObagf8= 208 | github.com/jeandeaual/go-locale v0.0.0-20240223122105-ce5225dcaa49/go.mod h1:YiutDnxPRLk5DLUFj6Rw4pRBBURZY07GFr54NdV9mQg= 209 | github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 210 | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= 211 | github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= 212 | github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25 h1:YLvr1eE6cdCqjOe972w/cYF+FjW34v27+9Vo5106B4M= 213 | github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25/go.mod h1:kLgvv7o6UM+0QSf0QjAse3wReFDsb9qbZJdfexWlrQw= 214 | github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= 215 | github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= 216 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 217 | github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= 218 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 219 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 220 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 221 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 222 | github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= 223 | github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= 224 | github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= 225 | github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= 226 | github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= 227 | github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 228 | github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= 229 | github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= 230 | github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= 231 | github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 232 | github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 233 | github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 234 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 235 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 236 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 237 | github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= 238 | github.com/neelance/sourcemap v0.0.0-20200213170602-2833bce08e4c/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= 239 | github.com/nicksnyder/go-i18n/v2 v2.4.1 h1:zwzjtX4uYyiaU02K5Ia3zSkpJZrByARkRB4V3YPrr0g= 240 | github.com/nicksnyder/go-i18n/v2 v2.4.1/go.mod h1:++Pl70FR6Cki7hdzZRnEEqdc2dJt+SAGotyFg/SvZMk= 241 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= 242 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= 243 | github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d h1:VhgPp6v9qf9Agr/56bj7Y/xa04UccTW04VP0Qed4vnQ= 244 | github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d/go.mod h1:YUTz3bUH2ZwIWBy3CJBeOBEugqcmXREj14T+iG/4k4U= 245 | github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= 246 | github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= 247 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 248 | github.com/pkg/profile v1.7.0 h1:hnbDkaNWPCLMO9wGLdBFTIZvzDrDfBM2072E1S9gJkA= 249 | github.com/pkg/profile v1.7.0/go.mod h1:8Uer0jas47ZQMJ7VD+OHknK4YDY07LPUC6dEvqDjvNo= 250 | github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= 251 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 252 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 253 | github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= 254 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 255 | github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= 256 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 257 | github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 258 | github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= 259 | github.com/rymdport/portal v0.2.6 h1:HWmU3gORu7vWcpr7VSwUS2Xx1HtJXVcUuTqEZcMEsIg= 260 | github.com/rymdport/portal v0.2.6/go.mod h1:kFF4jslnJ8pD5uCi17brj/ODlfIidOxlgUDTO5ncnC4= 261 | github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= 262 | github.com/shurcooL/go v0.0.0-20200502201357-93f07166e636/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= 263 | github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= 264 | github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 265 | github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw= 266 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= 267 | github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= 268 | github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= 269 | github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 270 | github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= 271 | github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= 272 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 273 | github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= 274 | github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c h1:km8GpoQut05eY3GiYWEedbTT0qnSxrCjsVbb7yKY1KE= 275 | github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c/go.mod h1:cNQ3dwVJtS5Hmnjxy6AgTPd0Inb3pW05ftPSX7NZO7Q= 276 | github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef h1:Ch6Q+AZUxDBCVqdkI8FSpFyZDtCVBc2VmejdNrm5rRQ= 277 | github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef/go.mod h1:nXTWP6+gD5+LUJ8krVhhoeHjvHTutPxMYl5SvkcnJNE= 278 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 279 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 280 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 281 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 282 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 283 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 284 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 285 | github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= 286 | github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 287 | github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= 288 | github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af h1:6yITBqGTE2lEeTPG04SN9W+iWHCRyHqlVYILiSXziwk= 289 | github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af/go.mod h1:4F09kP5F+am0jAwlQLddpoMDM+iewkxxt6nxUQ5nq5o= 290 | github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 291 | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 292 | github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 293 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 294 | github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= 295 | github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= 296 | github.com/yuin/goldmark v1.7.7 h1:5m9rrB1sW3JUMToKFQfb+FGt1U7r57IHu5GrYrG2nqU= 297 | github.com/yuin/goldmark v1.7.7/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= 298 | go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= 299 | go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= 300 | go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= 301 | go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= 302 | go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= 303 | go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 304 | go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 305 | go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 306 | go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= 307 | go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= 308 | go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= 309 | go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= 310 | go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= 311 | golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 312 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 313 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 314 | golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 315 | golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 316 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 317 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 318 | golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 319 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 320 | golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 321 | golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= 322 | golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4= 323 | golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= 324 | golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= 325 | golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 326 | golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 327 | golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 328 | golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= 329 | golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= 330 | golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= 331 | golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 332 | golang.org/x/image v0.21.0 h1:c5qV36ajHpdj4Qi0GnE0jUc/yuo33OLFaa0d+crTD5s= 333 | golang.org/x/image v0.21.0/go.mod h1:vUbsLavqK/W303ZroQQVKQ+Af3Yl6Uz1Ppu5J/cLz78= 334 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 335 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 336 | golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 337 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 338 | golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 339 | golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 340 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 341 | golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= 342 | golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 343 | golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 344 | golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 345 | golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 346 | golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= 347 | golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= 348 | golang.org/x/mobile v0.0.0-20211207041440-4e6c2922fdee/go.mod h1:pe2sM7Uk+2Su1y7u/6Z8KJ24D7lepUjFZbhFOrmDfuQ= 349 | golang.org/x/mobile v0.0.0-20241004191011-08a83c5af9f8 h1:W8YtRRiKAvbIbqAh+xpWKecxEO8w1oaD6DrZhMyotF8= 350 | golang.org/x/mobile v0.0.0-20241004191011-08a83c5af9f8/go.mod h1:snk1Mn2ZpdKCt90JPEsDh4sL3ReK520U2t0d7RHBnSU= 351 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 352 | golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= 353 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 354 | golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 355 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 356 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 357 | golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 358 | golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 359 | golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 360 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 361 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 362 | golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 363 | golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 364 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 365 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 366 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 367 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 368 | golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 369 | golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 370 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 371 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 372 | golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 373 | golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 374 | golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 375 | golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 376 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 377 | golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 378 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 379 | golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 380 | golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 381 | golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 382 | golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 383 | golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 384 | golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 385 | golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 386 | golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 387 | golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 388 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 389 | golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 390 | golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 391 | golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 392 | golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 393 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 394 | golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= 395 | golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= 396 | golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 397 | golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= 398 | golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= 399 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 400 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 401 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 402 | golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 403 | golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 404 | golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 405 | golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 406 | golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 407 | golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 408 | golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 409 | golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 410 | golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 411 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 412 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 413 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 414 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 415 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 416 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 417 | golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 418 | golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 419 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 420 | golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 421 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 422 | golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 423 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 424 | golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 425 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 426 | golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 427 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 428 | golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 429 | golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 430 | golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 431 | golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 432 | golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 433 | golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 434 | golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 435 | golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 436 | golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 437 | golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 438 | golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 439 | golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 440 | golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 441 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 442 | golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 443 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 444 | golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 445 | golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 446 | golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 447 | golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 448 | golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 449 | golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 450 | golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 451 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 452 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 453 | golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 454 | golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 455 | golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 456 | golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 457 | golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 458 | golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 459 | golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 460 | golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 461 | golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 462 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 463 | golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 464 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 465 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 466 | golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 467 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 468 | golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= 469 | golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 470 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 471 | golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 472 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 473 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 474 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 475 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 476 | golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 477 | golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 478 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 479 | golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= 480 | golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= 481 | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 482 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 483 | golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 484 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 485 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 486 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 487 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 488 | golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 489 | golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 490 | golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 491 | golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 492 | golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 493 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 494 | golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 495 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 496 | golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 497 | golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 498 | golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 499 | golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 500 | golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 501 | golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 502 | golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 503 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 504 | golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 505 | golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 506 | golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 507 | golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 508 | golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 509 | golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 510 | golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 511 | golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 512 | golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 513 | golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 514 | golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 515 | golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 516 | golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= 517 | golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= 518 | golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= 519 | golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 520 | golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 521 | golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 522 | golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 523 | golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 524 | golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 525 | golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 526 | golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 527 | golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= 528 | golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 529 | golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 530 | golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 531 | golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 532 | golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 533 | golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= 534 | golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 535 | golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 536 | golang.org/x/tools v0.1.8-0.20211022200916-316ba0b74098/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= 537 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 538 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 539 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 540 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 541 | google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= 542 | google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= 543 | google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 544 | google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 545 | google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 546 | google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 547 | google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 548 | google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 549 | google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 550 | google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 551 | google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 552 | google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 553 | google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= 554 | google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= 555 | google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= 556 | google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= 557 | google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= 558 | google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= 559 | google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= 560 | google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= 561 | google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= 562 | google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= 563 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 564 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 565 | google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 566 | google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= 567 | google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 568 | google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 569 | google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 570 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 571 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 572 | google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 573 | google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 574 | google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 575 | google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 576 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 577 | google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= 578 | google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 579 | google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 580 | google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 581 | google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 582 | google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 583 | google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 584 | google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= 585 | google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 586 | google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 587 | google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 588 | google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 589 | google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 590 | google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 591 | google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 592 | google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 593 | google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 594 | google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= 595 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 596 | google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= 597 | google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 598 | google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 599 | google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 600 | google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 601 | google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 602 | google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 603 | google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 604 | google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 605 | google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 606 | google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 607 | google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 608 | google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 609 | google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= 610 | google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= 611 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 612 | google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= 613 | google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 614 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 615 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 616 | google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 617 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 618 | google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 619 | google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= 620 | google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= 621 | google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 622 | google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 623 | google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 624 | google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= 625 | google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= 626 | google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= 627 | google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= 628 | google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= 629 | google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= 630 | google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= 631 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 632 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 633 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 634 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 635 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 636 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 637 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 638 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 639 | google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= 640 | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 641 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 642 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 643 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 644 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 645 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= 646 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 647 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 648 | gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 649 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 650 | gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 651 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 652 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 653 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 654 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 655 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 656 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 657 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 658 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 659 | honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 660 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 661 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 662 | honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 663 | honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 664 | rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= 665 | rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= 666 | rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= 667 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | //go:generate fyne bundle --package ui -o ui/assets.go --prefix Asset assets/Icon.png 2 | package main 3 | 4 | import ( 5 | "fyne.io/fyne/v2/app" 6 | "github.com/tomsquest/fynodoro/ui" 7 | ) 8 | 9 | // Variables set by goreleaser 10 | // We need to put them in a Struct to be able to access them in the UI 11 | var ( 12 | version string 13 | commit string 14 | commitDate string 15 | ) 16 | 17 | func main() { 18 | myApp := app.NewWithID("com.tomsquest.fynodoro") 19 | myApp.SetIcon(ui.AssetIconPng) 20 | 21 | ui.Display(myApp, ui.BuildInfo{version, commit, commitDate}) 22 | } 23 | -------------------------------------------------------------------------------- /pomodoro/pomodoro.go: -------------------------------------------------------------------------------- 1 | package pomodoro 2 | 3 | import ( 4 | "github.com/benbjohnson/clock" 5 | "time" 6 | ) 7 | 8 | type Kind int 9 | 10 | const ( 11 | Work Kind = iota 12 | ShortBreak 13 | LongBreak 14 | ) 15 | 16 | func (t Kind) String() string { 17 | names := [...]string{"Work", "Short Break", "Long Break"} 18 | return names[t] 19 | } 20 | 21 | type Params struct { 22 | WorkDuration time.Duration 23 | ShortBreakDuration time.Duration 24 | LongBreakDuration time.Duration 25 | WorkRounds int 26 | // use for testing, do not set it yourself 27 | Clock clock.Clock 28 | } 29 | 30 | type Pomodoro struct { 31 | // Config 32 | workDuration time.Duration 33 | shortBreakDuration time.Duration 34 | longBreakDuration time.Duration 35 | workRounds int 36 | clock clock.Clock 37 | // External events 38 | OnTick func() 39 | OnEnd func(kind Kind) 40 | // State 41 | Kind Kind 42 | RemainingTime time.Duration 43 | RemainingRound int 44 | Running bool 45 | // Internal 46 | ticker *clock.Ticker 47 | } 48 | 49 | func NewPomodoro(params *Params) *Pomodoro { 50 | p := &Pomodoro{} 51 | p.workDuration = params.WorkDuration 52 | p.shortBreakDuration = params.ShortBreakDuration 53 | p.longBreakDuration = params.LongBreakDuration 54 | p.workRounds = params.WorkRounds 55 | p.Kind = Work 56 | p.RemainingTime = p.workDuration 57 | p.RemainingRound = p.workRounds 58 | p.Running = false 59 | if params.Clock != nil { 60 | p.clock = params.Clock 61 | } else { 62 | p.clock = clock.New() 63 | } 64 | return p 65 | } 66 | 67 | func (p *Pomodoro) SetWorkDuration(duration time.Duration) { 68 | p.workDuration = duration 69 | } 70 | 71 | func (p *Pomodoro) SetShortBreakDuration(duration time.Duration) { 72 | p.shortBreakDuration = duration 73 | } 74 | 75 | func (p *Pomodoro) SetLongBreakDuration(duration time.Duration) { 76 | p.longBreakDuration = duration 77 | } 78 | 79 | func (p *Pomodoro) SetWorkRounds(workRounds int) { 80 | p.workRounds = workRounds 81 | } 82 | 83 | // SetRemainingTime sets the remaining time depending on the pomodoro kind 84 | func (p *Pomodoro) SetRemainingTime() { 85 | switch p.Kind { 86 | case Work: 87 | p.RemainingTime = p.workDuration 88 | case ShortBreak: 89 | p.RemainingTime = p.shortBreakDuration 90 | case LongBreak: 91 | p.RemainingTime = p.longBreakDuration 92 | } 93 | } 94 | 95 | func (p *Pomodoro) Start() { 96 | p.ticker = p.clock.Ticker(time.Second) 97 | p.Running = true 98 | 99 | go func() { 100 | for { 101 | select { 102 | case <-p.ticker.C: 103 | p.RemainingTime -= time.Second 104 | 105 | if p.RemainingTime > 0 { 106 | if p.OnTick != nil { 107 | p.OnTick() 108 | } 109 | } else { 110 | endedKind := p.Kind 111 | 112 | p.stop() 113 | p.next() 114 | 115 | if p.OnEnd != nil { 116 | p.OnEnd(endedKind) 117 | } 118 | } 119 | } 120 | } 121 | }() 122 | } 123 | 124 | func (p *Pomodoro) Pause() { 125 | p.stop() 126 | } 127 | 128 | func (p *Pomodoro) Stop() { 129 | p.stop() 130 | p.SetRemainingTime() 131 | } 132 | 133 | func (p *Pomodoro) Next() { 134 | p.stop() 135 | p.next() 136 | } 137 | 138 | func (p *Pomodoro) stop() { 139 | p.Running = false 140 | 141 | if p.ticker != nil { 142 | p.ticker.Stop() 143 | } 144 | } 145 | 146 | func (p *Pomodoro) next() { 147 | switch p.Kind { 148 | case ShortBreak, LongBreak: 149 | p.Kind = Work 150 | case Work: 151 | p.RemainingRound-- 152 | shortBreaksDisabled := p.shortBreakDuration == 0 153 | longBreaksDisabled := p.workRounds == 0 || p.longBreakDuration == 0 154 | 155 | if shortBreaksDisabled && longBreaksDisabled { 156 | // Both Short and Long breaks disabled, only do Work 157 | p.Kind = Work 158 | } else if longBreaksDisabled { 159 | // Only LongBreaks disabled, only do ShortBreak 160 | p.Kind = ShortBreak 161 | } else if shortBreaksDisabled { 162 | // Only ShortBreaks disabled, only do LongBreak 163 | p.Kind = LongBreak 164 | } else { 165 | if p.RemainingRound <= 0 { 166 | p.Kind = LongBreak 167 | p.RemainingRound = p.workRounds 168 | } else { 169 | p.Kind = ShortBreak 170 | } 171 | } 172 | } 173 | p.SetRemainingTime() 174 | } 175 | -------------------------------------------------------------------------------- /pomodoro/pomodoro_test.go: -------------------------------------------------------------------------------- 1 | package pomodoro 2 | 3 | import ( 4 | "github.com/benbjohnson/clock" 5 | "github.com/stretchr/testify/assert" 6 | "testing" 7 | "time" 8 | ) 9 | 10 | func TestNewPomodoro(t *testing.T) { 11 | p := NewPomodoro(&Params{ 12 | WorkDuration: 1 * time.Second, 13 | ShortBreakDuration: 2 * time.Minute, 14 | LongBreakDuration: 3 * time.Hour, 15 | WorkRounds: 42, 16 | }) 17 | 18 | assert.Equal(t, 1*time.Second, p.workDuration) 19 | assert.Equal(t, 2*time.Minute, p.shortBreakDuration) 20 | assert.Equal(t, 3*time.Hour, p.longBreakDuration) 21 | assert.Equal(t, 42, p.workRounds) 22 | assert.Equal(t, Work, p.Kind) 23 | assert.Equal(t, 1*time.Second, p.RemainingTime) 24 | assert.False(t, p.Running) 25 | } 26 | 27 | func TestNewPomodoro_disableLongBreaks_zeroDurationLongBreak(t *testing.T) { 28 | clockMock := clock.NewMock() 29 | p := NewPomodoro(&Params{ 30 | LongBreakDuration: 0, 31 | 32 | WorkDuration: 3, 33 | ShortBreakDuration: 3, 34 | WorkRounds: 3, 35 | Clock: clockMock, 36 | }) 37 | 38 | for i := 0; i < 99; i++ { 39 | assert.NotEqual(t, LongBreak, p.Kind) 40 | p.Next() 41 | } 42 | } 43 | 44 | func TestNewPomodoro_disableLongBreaks_zeroWorkRounds(t *testing.T) { 45 | clockMock := clock.NewMock() 46 | p := NewPomodoro(&Params{ 47 | WorkRounds: 0, 48 | 49 | WorkDuration: 3, 50 | ShortBreakDuration: 3, 51 | LongBreakDuration: 3, 52 | Clock: clockMock, 53 | }) 54 | 55 | for i := 0; i < 99; i++ { 56 | assert.NotEqual(t, LongBreak, p.Kind) 57 | p.Next() 58 | } 59 | } 60 | 61 | func TestNewPomodoro_disableShortBreaks_zeroDuration(t *testing.T) { 62 | clockMock := clock.NewMock() 63 | p := NewPomodoro(&Params{ 64 | ShortBreakDuration: 0, 65 | 66 | WorkDuration: 3, 67 | LongBreakDuration: 3, 68 | WorkRounds: 3, 69 | Clock: clockMock, 70 | }) 71 | 72 | for i := 0; i < 99; i++ { 73 | assert.NotEqual(t, ShortBreak, p.Kind) 74 | p.Next() 75 | } 76 | } 77 | 78 | func TestNewPomodoro_disableAnyBreaks(t *testing.T) { 79 | clockMock := clock.NewMock() 80 | p := NewPomodoro(&Params{ 81 | ShortBreakDuration: 0, 82 | LongBreakDuration: 0, 83 | 84 | WorkDuration: 3, 85 | WorkRounds: 3, 86 | Clock: clockMock, 87 | }) 88 | 89 | for i := 0; i < 99; i++ { 90 | assert.NotEqual(t, ShortBreak, p.Kind) 91 | assert.NotEqual(t, LongBreak, p.Kind) 92 | p.Next() 93 | } 94 | } 95 | 96 | func TestPomodoro_SetRemainingTime(t *testing.T) { 97 | p := NewPomodoro(&Params{ 98 | WorkDuration: 3, 99 | ShortBreakDuration: 2, 100 | LongBreakDuration: 1, 101 | WorkRounds: 1, 102 | Clock: clock.NewMock(), 103 | }) 104 | p.Kind = Work 105 | 106 | p.SetRemainingTime() 107 | 108 | assert.Equal(t, time.Duration(3), p.RemainingTime) 109 | 110 | p.Kind = ShortBreak 111 | 112 | p.SetRemainingTime() 113 | 114 | assert.Equal(t, time.Duration(2), p.RemainingTime) 115 | 116 | p.Kind = LongBreak 117 | 118 | p.SetRemainingTime() 119 | 120 | assert.Equal(t, time.Duration(1), p.RemainingTime) 121 | } 122 | 123 | func TestPomodoro_OnTick(t *testing.T) { 124 | clockMock := clock.NewMock() 125 | p := NewPomodoro(&Params{ 126 | WorkDuration: 25 * time.Second, 127 | ShortBreakDuration: 5 * time.Second, 128 | LongBreakDuration: 10 * time.Second, 129 | WorkRounds: 2, 130 | Clock: clockMock, 131 | }) 132 | 133 | // Capture tick events 134 | tickedCount := 0 135 | p.OnTick = func() { 136 | tickedCount++ 137 | } 138 | 139 | // Start Work 140 | p.Start() 141 | 142 | clockMock.Add(1 * time.Second) 143 | assert.Equal(t, 1, tickedCount) 144 | clockMock.Add(1 * time.Second) 145 | assert.Equal(t, 2, tickedCount) 146 | 147 | // Start ShortBreak 148 | p.Next() 149 | assert.Equal(t, ShortBreak, p.Kind) 150 | tickedCount = 0 // Reset count 151 | 152 | p.Start() 153 | 154 | clockMock.Add(1 * time.Second) 155 | assert.Equal(t, 1, tickedCount) 156 | clockMock.Add(1 * time.Second) 157 | assert.Equal(t, 2, tickedCount) 158 | 159 | // Skip to Work 160 | p.Next() 161 | 162 | // Start LongBreak 163 | p.Next() 164 | assert.Equal(t, LongBreak, p.Kind) 165 | tickedCount = 0 // Reset count 166 | 167 | p.Start() 168 | 169 | clockMock.Add(1 * time.Second) 170 | assert.Equal(t, 1, tickedCount) 171 | clockMock.Add(1 * time.Second) 172 | assert.Equal(t, 2, tickedCount) 173 | } 174 | 175 | func TestPomodoro_OnEnd(t *testing.T) { 176 | clockMock := clock.NewMock() 177 | p := NewPomodoro(&Params{ 178 | WorkDuration: 25 * time.Second, 179 | ShortBreakDuration: 5 * time.Second, 180 | LongBreakDuration: 10 * time.Second, 181 | WorkRounds: 2, 182 | Clock: clockMock, 183 | }) 184 | 185 | // Capture end event 186 | endCalled := false 187 | var endKind Kind 188 | p.OnEnd = func(kind Kind) { 189 | endCalled = true 190 | endKind = kind 191 | } 192 | 193 | // Start and finish Work 194 | p.Start() 195 | clockMock.Add(25 * time.Second) 196 | 197 | assert.True(t, endCalled) 198 | assert.Equal(t, Work, endKind) 199 | assert.False(t, p.Running) 200 | 201 | // Start and finish ShortBreak 202 | assert.Equal(t, 5*time.Second, p.RemainingTime) 203 | p.Start() 204 | clockMock.Add(5 * time.Second) 205 | 206 | assert.True(t, endCalled) 207 | assert.Equal(t, ShortBreak, endKind) 208 | assert.False(t, p.Running) 209 | 210 | // Skip Work 211 | p.Start() 212 | p.Next() 213 | 214 | // Start and finish LongBreak 215 | assert.Equal(t, 10*time.Second, p.RemainingTime) 216 | p.Start() 217 | clockMock.Add(10 * time.Second) 218 | 219 | assert.True(t, endCalled) 220 | assert.Equal(t, LongBreak, endKind) 221 | assert.False(t, p.Running) 222 | } 223 | 224 | func TestPomodoro_Stop(t *testing.T) { 225 | clockMock := clock.NewMock() 226 | p := NewPomodoro(&Params{ 227 | WorkDuration: 25 * time.Second, 228 | ShortBreakDuration: 5 * time.Second, 229 | LongBreakDuration: 10 * time.Second, 230 | WorkRounds: 2, 231 | Clock: clockMock, 232 | }) 233 | 234 | // Start Work 235 | p.Start() 236 | 237 | // Stop Work 238 | p.Stop() 239 | assert.False(t, p.Running) 240 | assert.Equal(t, Work, p.Kind) 241 | assert.Equal(t, 25*time.Second, p.RemainingTime) 242 | 243 | // Start ShortBreak 244 | p.Next() 245 | p.Start() 246 | 247 | // Stop ShortBreak 248 | p.Stop() 249 | assert.False(t, p.Running) 250 | assert.Equal(t, ShortBreak, p.Kind) 251 | assert.Equal(t, 5*time.Second, p.RemainingTime) 252 | 253 | // Skip Work 254 | p.Next() 255 | 256 | // Start LongBreak 257 | p.Next() 258 | p.Start() 259 | 260 | // Stop LongBreak 261 | p.Stop() 262 | assert.False(t, p.Running) 263 | assert.Equal(t, LongBreak, p.Kind) 264 | assert.Equal(t, 10*time.Second, p.RemainingTime) 265 | } 266 | 267 | func TestPomodoro_Pause(t *testing.T) { 268 | clockMock := clock.NewMock() 269 | p := NewPomodoro(&Params{ 270 | WorkDuration: 25 * time.Second, 271 | ShortBreakDuration: 5 * time.Second, 272 | Clock: clockMock, 273 | }) 274 | 275 | // Start Work 276 | p.Start() 277 | clockMock.Add(1 * time.Second) 278 | 279 | // Pause 280 | p.Pause() 281 | assert.False(t, p.Running) 282 | assert.Equal(t, Work, p.Kind) 283 | assert.Equal(t, 24*time.Second, p.RemainingTime) 284 | 285 | // Finish Work 286 | p.Start() 287 | clockMock.Add(25 * time.Second) 288 | // Start ShortBreak 289 | p.Start() 290 | clockMock.Add(1 * time.Second) 291 | 292 | // Pause 293 | p.Pause() 294 | assert.False(t, p.Running) 295 | assert.Equal(t, ShortBreak, p.Kind) 296 | assert.Equal(t, 4*time.Second, p.RemainingTime) 297 | } 298 | 299 | func TestPomodoro_Next(t *testing.T) { 300 | clockMock := clock.NewMock() 301 | p := NewPomodoro(&Params{ 302 | WorkDuration: 25 * time.Second, 303 | ShortBreakDuration: 5 * time.Second, 304 | LongBreakDuration: 10 * time.Second, 305 | WorkRounds: 3, 306 | Clock: clockMock, 307 | }) 308 | 309 | // Work 310 | assert.Equal(t, Work, p.Kind) 311 | assert.Equal(t, 25*time.Second, p.RemainingTime) 312 | assert.False(t, p.Running) 313 | assert.Equal(t, 3, p.RemainingRound) 314 | 315 | // ShortBreak 316 | p.Next() 317 | assert.Equal(t, ShortBreak, p.Kind) 318 | assert.Equal(t, 5*time.Second, p.RemainingTime) 319 | assert.False(t, p.Running) 320 | assert.Equal(t, 2, p.RemainingRound) 321 | 322 | // Work 323 | p.Next() 324 | assert.Equal(t, Work, p.Kind) 325 | assert.Equal(t, 25*time.Second, p.RemainingTime) 326 | assert.False(t, p.Running) 327 | assert.Equal(t, 2, p.RemainingRound) 328 | 329 | // ShortBreak 330 | p.Next() 331 | assert.Equal(t, ShortBreak, p.Kind) 332 | assert.Equal(t, 5*time.Second, p.RemainingTime) 333 | assert.False(t, p.Running) 334 | assert.Equal(t, 1, p.RemainingRound) 335 | 336 | // Work 337 | p.Next() 338 | assert.Equal(t, Work, p.Kind) 339 | assert.Equal(t, 25*time.Second, p.RemainingTime) 340 | assert.False(t, p.Running) 341 | assert.Equal(t, 1, p.RemainingRound) 342 | 343 | // LongBreak 344 | p.Next() 345 | assert.Equal(t, LongBreak, p.Kind) 346 | assert.Equal(t, 10*time.Second, p.RemainingTime) 347 | assert.False(t, p.Running) 348 | assert.Equal(t, 3, p.RemainingRound) 349 | 350 | // Work 351 | p.Next() 352 | assert.Equal(t, Work, p.Kind) 353 | assert.Equal(t, 25*time.Second, p.RemainingTime) 354 | assert.False(t, p.Running) 355 | assert.Equal(t, 3, p.RemainingRound) 356 | 357 | // While running 358 | 359 | // ShortBreak 360 | p.Next() 361 | p.Start() 362 | clockMock.Add(1 * time.Second) 363 | assert.Equal(t, ShortBreak, p.Kind) 364 | assert.Equal(t, 4*time.Second, p.RemainingTime) 365 | assert.True(t, p.Running) 366 | assert.Equal(t, 2, p.RemainingRound) 367 | 368 | // Work 369 | p.Next() 370 | p.Start() 371 | clockMock.Add(1 * time.Second) 372 | assert.Equal(t, Work, p.Kind) 373 | assert.Equal(t, 24*time.Second, p.RemainingTime) 374 | assert.True(t, p.Running) 375 | assert.Equal(t, 2, p.RemainingRound) 376 | 377 | // ShortBreak 378 | p.Next() 379 | p.Start() 380 | clockMock.Add(1 * time.Second) 381 | assert.Equal(t, ShortBreak, p.Kind) 382 | assert.Equal(t, 4*time.Second, p.RemainingTime) 383 | assert.True(t, p.Running) 384 | assert.Equal(t, 1, p.RemainingRound) 385 | 386 | // Work 387 | p.Next() 388 | p.Start() 389 | clockMock.Add(1 * time.Second) 390 | assert.Equal(t, Work, p.Kind) 391 | assert.Equal(t, 24*time.Second, p.RemainingTime) 392 | assert.True(t, p.Running) 393 | assert.Equal(t, 1, p.RemainingRound) 394 | 395 | // LongBreak 396 | p.Next() 397 | p.Start() 398 | clockMock.Add(1 * time.Second) 399 | assert.Equal(t, LongBreak, p.Kind) 400 | assert.Equal(t, 9*time.Second, p.RemainingTime) 401 | assert.True(t, p.Running) 402 | assert.Equal(t, 3, p.RemainingRound) 403 | 404 | // Work 405 | p.Next() 406 | p.Start() 407 | clockMock.Add(1 * time.Second) 408 | assert.Equal(t, Work, p.Kind) 409 | assert.Equal(t, 24*time.Second, p.RemainingTime) 410 | assert.True(t, p.Running) 411 | assert.Equal(t, 3, p.RemainingRound) 412 | } 413 | -------------------------------------------------------------------------------- /pref/pref.go: -------------------------------------------------------------------------------- 1 | package pref 2 | 3 | import ( 4 | "fyne.io/fyne/v2" 5 | ) 6 | 7 | type Pref struct { 8 | WorkDuration int 9 | ShortBreakDuration int 10 | LongBreakDuration int 11 | WorkRounds int 12 | } 13 | 14 | func Load() Pref { 15 | app := fyne.CurrentApp() 16 | return Pref{ 17 | WorkDuration: app.Preferences().IntWithFallback("workDuration", 25), 18 | ShortBreakDuration: app.Preferences().IntWithFallback("shortBreakDuration", 5), 19 | LongBreakDuration: app.Preferences().IntWithFallback("longBreakDuration", 15), 20 | WorkRounds: app.Preferences().IntWithFallback("workRounds", 4), 21 | } 22 | } 23 | 24 | func Save(pref Pref) { 25 | app := fyne.CurrentApp() 26 | app.Preferences().SetInt("workDuration", pref.WorkDuration) 27 | app.Preferences().SetInt("shortBreakDuration", pref.ShortBreakDuration) 28 | app.Preferences().SetInt("longBreakDuration", pref.LongBreakDuration) 29 | app.Preferences().SetInt("workRounds", pref.WorkRounds) 30 | } 31 | -------------------------------------------------------------------------------- /screenshots/app.pimped.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomsquest/fynodoro/d13e58f45d15ac4d44df3a1790bfbea9ab31b925/screenshots/app.pimped.png -------------------------------------------------------------------------------- /screenshots/app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomsquest/fynodoro/d13e58f45d15ac4d44df3a1790bfbea9ab31b925/screenshots/app.png -------------------------------------------------------------------------------- /screenshots/app.shadow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomsquest/fynodoro/d13e58f45d15ac4d44df3a1790bfbea9ab31b925/screenshots/app.shadow.png -------------------------------------------------------------------------------- /screenshots/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomsquest/fynodoro/d13e58f45d15ac4d44df3a1790bfbea9ab31b925/screenshots/banner.png -------------------------------------------------------------------------------- /screenshots/banner.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomsquest/fynodoro/d13e58f45d15ac4d44df3a1790bfbea9ab31b925/screenshots/banner.xcf -------------------------------------------------------------------------------- /screenshots/codioful-formerly-gradienta-WDSN62Qdxuk-unsplash.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomsquest/fynodoro/d13e58f45d15ac4d44df3a1790bfbea9ab31b925/screenshots/codioful-formerly-gradienta-WDSN62Qdxuk-unsplash.jpg -------------------------------------------------------------------------------- /screenshots/hero.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomsquest/fynodoro/d13e58f45d15ac4d44df3a1790bfbea9ab31b925/screenshots/hero.jpg -------------------------------------------------------------------------------- /screenshots/hero.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomsquest/fynodoro/d13e58f45d15ac4d44df3a1790bfbea9ab31b925/screenshots/hero.xcf -------------------------------------------------------------------------------- /screenshots/settings.pimped.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomsquest/fynodoro/d13e58f45d15ac4d44df3a1790bfbea9ab31b925/screenshots/settings.pimped.png -------------------------------------------------------------------------------- /screenshots/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomsquest/fynodoro/d13e58f45d15ac4d44df3a1790bfbea9ab31b925/screenshots/settings.png -------------------------------------------------------------------------------- /ui/assets.go: -------------------------------------------------------------------------------- 1 | // auto-generated 2 | // Code generated by '$ fyne bundle'. DO NOT EDIT. 3 | 4 | package ui 5 | 6 | import "fyne.io/fyne/v2" 7 | 8 | var AssetIconPng = &fyne.StaticResource{ 9 | StaticName: "Icon.png", 10 | StaticContent: []byte( 11 | "\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x01\x00\x00\x00\x01\x00\b\x06\x00\x00\x00\\r\xa8f\x00\x00\x00\x04sBIT\b\b\b\b|\bd\x88\x00\x00\x00\tpHYs\x00\x00\x06\xec\x00\x00\x06\xec\x01\x1eu85\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\x9b\xee<\x1a\x00\x00\"iIDATx\xda\xed\x9dwX\x95g\x9a\xc6M1q\x92Lz&3\x99ɵ\xc9f\xb3\x93\xcd\xcc\xecf2\x93\x9dݝ\xb92%\x93\x99\xd9(\x1c\xda) Fłb\x89]Q\xa9\"(\a\x14\xb0P\xec\x1aK\x8c\x1a\x1151\x8a&\x1a\xb1\xc6\x02\x82\x88\r{\xc1\xbe\x89I\xd4\xf8\xee\xf3\x1c\xceA\xc0S\xe1\xfb\xce\xf9\xca\xfd\xc7\xef\xb2\x00\x87s\x9e\xf7\xb9\xef\xf7y\xeb\xd7F\b\xd1\x06\x00\xa0O\x10\x04\x00`\x00\x00\x00\x18\x00\x00\x00\x06\x00\x00\x80\x01\x00 U\x92\xddӧ\xc4\xfa\xdf}Vf\xa5П\xf1}WZ\xbb\xf5Y\x95ݡ\xf7ʌ'\x10\x1f\x18\x00\xd0(C\xd6X\x1f\xee\xb3ʚM\xc2?I\b'\\\xe9\xb3*kt\xdc\xe2)\x8f ^0\x00\xa0!H\xd8\xffD\x02\xdf\xe3B\xf8Ͱ\x9e\xef\xb7\xd2\xfaW\xc4\r\x06\x00\xb4 \xfe\x92\xcc\xd7Y\xd4މ\xbf\x81\xcb}K\xb2^A\xfc`\x00@吐\xb7\xf8(~\a\a\a~2\xe1I\xc4\x10\x06\x00TJ\\\x895\xa8\x85\xe2\xb7ѷĚ\x8c8\xc2\x00\x80Z\xcb\x7f\xaf\xc7\xfd.X\x95\xb5\x17q\x84\x01\x00\x15\xd2{u\xf6\x7f\xb5J\xfcv\xfa\xad\xce|\t\xf1\x84\x01\x00\xf5\xf5\xfeyR\x18@ߒl3\xe2\t\x03\x00*¸x\xf1}$\u07b3R\x18@\\I\xe6\xdf\x10S\x18\x00P\u05fa\xff\xdbR\x88\xdfFI\xe6\xeb\x88)\f\x00\xa8\xab\xfc\x9f%\x95\x01\xf4[=\xfeg\x88)\f\x00\xa8\x84~\xab\xf3\x1e\xb4m\xeb\x95\xc4\x00\xacU\x88)\f\x00\xa8i\xed\x7feV\x98d\xe5\xff\xaa\xacш)\f\x00\xa8j\xeb\xafu\x89t\xe5?\x96\x00a\x00@vF\x7f\x91\xffxbY\xe1\x00\x86\xffފ\xf2\xffQ\x12\xee7\x12\x19\xc0V\xb4\r\f\x00\xc8L\xc2悿\x8d\xdf;\xfbV־\xb9\x82\xe1\xbf\xf3\xff\xb5\xac\xf7\xcf\x1e$U\xef\xdfweV?\xb4\x0f\f\x00\xc8\xdb\xf3?2nϬ\x9b\x0e\xf1;\xe0\xff\xe3\xaf\xf9\xf2Z]6$\xb7#ទ\xc8\x00n\xbdW\x9c\xf3,\xda\b\x06\x00d$yk\xd1\a\xcd\xc5\uf03f\xe6ө?\xea\xb1%\xeb\xfdWe\xafA\xfb\xc0\x00\x80\xbc\xbd\xff\xd3T\xee\x7f\xef\xca\x00\xf8k\xfc=^\x19\xc9\xe2\xe4\aH\xb8'\xa42\x80\xb8\x92\xecw\xd1F0\x00 #)ۊV\xbb\x12\xbf\x03\xfe\x1e\xaf\x96\xfeVe\xc7J\xb6\xf4\xb72\xebzL\xf1\xf8\x1f\xa2\x8d`\x00@\xbe\xde\xff\xf9\xcc\xf29\xb7=\x19\x00\x7f\x0f\x7f\xaf\xbb\xd7깳\xb0-\x89\xf6\xa8\x84\x06\xb0\x18m\x04\x03\x00\xb2\xf6\xfe\xd36z\x12\xff\x9d*`\xdaFw\xaf\xd5u^R\xaf^\xcb\xc6\t\xe96\xffX\rh#\x18\x00\x90\xab\xf7ߜ\xff\x8a\xb5\xc2s\xef\uf03f\x97\x7f\xc6\xd9kE[\x87<\xdceN±\xee\x8b\xc6He\x00G\xb9\xa2@;\xc1\x00\x80L\xa4n\x9f\xf6\xa5\xb7\xe2w\xc0?\xe3t\xe9oN\xe2\xc4.s\x13EW\xa2O\x89\xb5\xf5\x93\x7f\xab\xb2c\xd1F0\x00 [\xef_\xf0\x06\xf5\xe8\xc2W\x03\xe0\x9f\xe1\x9fm\xfcZ1s\x92~C\x06p\x93\r\x80\x89]\x9a\xd1Z\x038\xc1\xab\th'\x18\x00\x90\x891;\xa6W\xf9*~\a\xfc\xb3\r\xcb~\xc9\xc9\xf7\x92\xe8\xbft\x88\x9f\xe9\xb60\xb5\x95c\xff\xec>h#\x18\x00\x90o\xec\xffrKz\xff\xa6U@\xfe˶\x89\xbf\xf7\x13\x874\x16\x7f\xc30\xa0\xe5\x06p\x8a\x8f\x11\xa3\x9d`\x00@\xae]\x7fۊ\x96\xb7T\xfc\r\xbb\x03\xe95\xba\xcdO\xfb'\x12\xfcW\xcd\r\x80\xe9\xb5||K\xcf\xfd\x0f@\x1b\xc1\x00\x80\x8cd\xec\x9e\xf5mk\r\x80_#f^\xe2\x1ag\xe2gz.\x19\xdb\x12\x038;p\xf1\x84\x1f\xa0\x8d`\x00@&\x12\xcb\nz\xb5V\xfc\x0e\xfa\x16\x8f\x17\xae\f\xc0\xe7y\x80\x12\xab\x88+\xb6\x0eE\x1b\xc1\x00\x80\xbc\x93\x7f\x87\xa42\x80\xc4\xcd\x05.\r\x80\xe7\x01\xe2Vd\xba\xd9\xe3o\x15\xbci\xa8\xc7\xe24\xd1mA\x8a\xe8:/\xf1b\xcc\xf4\xa1\xd8\xf6\v\x03\x002n\xfb\xfdik&\xff\x9cM\x06\xb2x]V\x01\xf45\x87\t\xf0\x9f\xb1\xcb2D\x8f\x0f\xd2D\xcc\xfc\xe4\xbb\rcAJ_\xb4\x11\f\x00\xc8HҖ\xc2,\xa9\xc4\xef`\xe0\xeal\x97\x06\xe0-]\xe7%m\xa37x\x0f\xda\b\x06\x00\xe4\xdc\xf9\xb7cz\xa5\xd4\x06\x90\xf0E~k\r\xe0F\xe7\xf9I\xff\x81\xf6\x81\x01\x00\x99\x19\xb7g\xd6\r\xa9\r c\xf7\xacV\x19@̼\xa4l\xb4\r\f\x00\xc8L\xc2\xe6\x82\xff\x91Z\xfc\x0ez.Nk\xa9\x01\xd4\xf6,L~\b\xed\x03\x03P\x05\xc3Ks\x9f\x1a^\x9a\xf7\x9b\xe1\xebr\xc3F\x94\xe6\xf6#\xd2G\xacϝ\x1e_\x9aW\xce\x1e5x\xb5\xf5\xdd\x01\xc5\xe3\xff\xd0s\xce\xe8\x1f\xb7i\xd3\xfa\xf6H\xdaZ4S.\x03\x18\xfc\xe9\xc4\x16\x1a@B\xfb\xe67\x13\x8d\xde\\\U0003b132\x82\xc8IJ\xc2aI[\n\xf3\xf8:\xb2\x94m\xd36\xa4n\x9f\xb6g\xcc\xce\xe9\xb5c\xbf\x9cq\x9eI\xfbr\xc6ٴ\x9d3N\x13'\x89\x13i;\xa7\x1f\xa3\xaf\x1f\xe5U\x0e\xe2 q\x80\xb7,\xf3\xcf\xd1\xcf\x7fF\xaf\xb3,ikaa\xe2\x96\xc21\x89e\x05qd\x88\x06\xfa]\xbf\xa6\xdf\xf9$r\x1b\x06\xe0\x92aks^\x8f/ͭJX\x9fw;q}\xae\xf0?y\"\xb3|\xb6\xed\x86^*\xb7\xafS\xb2\x9f\xe2\x84&qX\xf9\xc6^J\xe0\xfb\xbdZ\xfe#\xf1\xc8e\x00\xf4^\xbc\x16}\xaf%cm\x13\x87\xa36N\xa9\xe3\xcf\u009f\x89?\x9b\x94\xab\x13\xbe\u0097\x9d\xa4\xee\x98^A\x86\xf0\x1ar\x1e\x06pG\xfc\x9f\xe6<@\xbd\xf4\xd5\xc0\b\xbf\x9e\xa4\xcf'y\\\x8aK\xdf=\xf3+\xea\xe9\xb6S\xaf9\xc4\xd9\r\xbed\x14\xef\xc8)0~\xed\xde$l'\xb3\xfb\xa2\x7fI&\x8b]P\xaf-\x02)roH\xdf5\xf3\n\xc5\x0f\xa7\x10a\x00\r\xbd\x7f\xe7@\x8a\xdff\x00\x9fM\xf2Y\x8c\\\x1eSɻ\x90\fa \x97\xd1\x19\xbbg~-\xb7xX\xe0\x83>\x9e \xfa\xad\xc8\x14C\xd7\xe6\xf0\x1d\x82\x8a\x17\xbc\xd3U\x8d\xb2\x82h\xe4>\f\xa0~̿6gD\xa0\r\x80\xc9\xdc;[uBR+\\E!\xf7a\x00\x8e!\xc0C\xa3\xd7\xe7\xde\f\xb4\x01\xa4m-\x848\xfd\xc0\xb8\xbd\xb3\xf9!(X\x95\x80\x014\xaa\x02\xd6\xe5$$\x04\xba\nؐ\x87*@fx\xb8\x92\xb8\xa50\x1e9\x0f\x03p6\x14\xe8\x1f\xb8U\x80z\x927N\x16\xd6\xf29\x10\xabL\xab\x00\xbc4\x88\\\x87\x01\xb8\x9b\x104\xd2p\xe0\xfb@\x9a@ʦ)0\x01\x89\xe1\xa7\x1e%\x94\x15\x84!\xc7a\x00ޘ\xc0\x9fG\x97\xe6\xde\b\xf4\xb2\xe0\xb8]3 ^)\xc6\xfc{f\xddH\xd8\\\xf0&r\x1b\x06\xe0\x83\tL\xfc%\x99\xc07\x81\x9e\x18\x1c\xb3\xa5@\x95\xcblJ!c\xf7\xcc\xeb\xa37翊\x9c\x86\x01\xb4`u`\xe2\xcfF\x96\xe6^\t\xf8\x1e\x01\xae\x06\xbeD5\xe0\xf3\x9e\x85]3/\xf3\xdd\b\xc8e\x18@k\x96\b\x1f\x1b\xb9.\xf7\x8c\x12\xf6\t\xb0\x11\x8c\xddV\xa4\x8a\x95\x02~\x8f<\x84I\xdf1\x8d\xdes\xa1m\x89\x93\xab\x991e\xf9\"u\xf3T\x91\xf2\xc5T\xdb\\\aOz\xf2\xe7b\xf8\xef\xfc\x7f\xa9\xf4\xb5T\xfa>\xfe~\xfe9\xfe\xcc\xe9ۧ\x89\xf4\x9d\xd3\xe95g\nk\xb9\xe7\xcf\xcfۏI\xfc\x8f\"\x87a\x00\x92l\x15&\x138\xa8\x04\x13p\x9c\x1b`\x01e\x90 \x02i\x066\x91Se\xc2\xe2L#\xb1\xb2p\x93?\x9fl[\xce\xf4ǒ)\x9bŘ\xb2\x021v{\x91Ƞ\xf7\x91\xb9w\x96\xe3y\x06\a\xb0\xd5\x17\x06 9#\xd6\xe5lW\x8e\t4\xddF\xcc\xe2\xe3\xde2\xe3KiM\x81\xe7\x1f\xc6\xef\x99e3\x1b\xee\xc9S7\xe7\xdbzj\xbf\x88\xbc\x05\x8cX\x9b\xb3]\x8aS\x950\x00\xe0\xdc\x04\xd6\xe6\x14'(4\xf9\x9d\xf5\x90,\xd81M\xca\xe9B[\x8fɥ9\x97\xd4\xf5%zQ}\x89\xce幣4\xff\\\xb9\"w\x06\xb7I|i\xder\xe4(\f\xc0\x1f\x1b\x86z+a\xeb0h\x10\xff\xcd\xf8\xd2\xdc^\xc8M\x18\x80?\x97\t_\x1eY\x9a{\x0e\x02\f,\xdc\x06\xc3Ks\xff\x059\t\x03\bԼ\xc0ʄ\xf5y\x10\xa3\xbf{}\x8a\xf9p\x8a=r\x10\x06\xa0\x84\x9d\x83\xddG\x97bH\xe0/F\xad˹9ẗ́\x1e\xc8=\x18\x80\x92\x86\x04/\x8c)\xc9<\x9d\xbb(UL\x9d\x93$\xa6M\x1b%fO\x19.\xe6O\x1c,>\x18\xff\x9eX\x96\xdaW\x94$\xf4\x12\x1f\x8f\xe8.\xd6\r\xea*\xd6\x0e\x8e\x11\xabG\xf4\x10%\xa3c\xc5\xf2\x94>bIz?\xb1\xc8:@\xbc\x9f3D̞\x95&\xc3R\x12\xfb!\x12\xfaM\xb9\x84\xee-\xf4\x1e\xc4\xe1\xc80q\xb6\x93I\x9c\xe9\x1a)\xaa\xfbw\xb5\xf5\xa0ܫ.Ώ\x17\xd3\xdeO\x15\xd6\x15\x99\x92\x98\x03\xbf\x06\xbf\x16\xf7\xc0\xfc\xdak\xe8wp\x8fͿ\x93\x7f\xb7\x8c\"o\t7\x89\x03\xc4\"\xa2'\xf1\x12r\x18\x06\xe0\x13\xb5Q\xe1\xcfU\x9bC\xbaT\x99\f\xf3H\xf0'\x02-\xf6\xe6\x1c\xb4\x84\x8a3$|o\x04q\xa6\x8bE\xd4\xc6F\x8b\x83}:\xdbz\xe5\xbdC\xba\x8b\x9d#bŖ\x848\xb11\xa5\xaf(\xa5r\x9b\xe1\xbf\xf3\xff\xf1\xd7\xf8{\xf8{\xf9g\xf8g\xf95\x14&r_9f\xaf\x14:\x12?A\x8e\xc3\x00\x9a\x97\xf4\xf7\xd5XB\xdbSI_@%\xfd\x01\xee]\x95&z\x86ޟ8\x15mT\xbb\x18\x95@\x151\x89\xf8;\xb7=\f@\xbf\xa5\xfd\xbfSi\x9fC\xbd\xfcY%\n\xbe1\\\ue7c3p\xe5\xe04a%~\t\x03\xd0\x01':Fs$2\x1c\xc2S\xe6JB\x1c\xf1 \f@\xa1\x9c\xeddz\x98\x84?\x9c\x84\x7fN\x8d\xc2\xe79\x89cQ\x10\xbf\xc29e\x9f'x\b\x06\xa0 \xe1\xd3\x18?\x91\x84\x7fQ\x95·o\xec9\xd11\x02\x02S\x0f\xe7\x88aZ0\x02U\xbf\xf9ÑaF\xb5\x96\xfa\x0e\xf6\x191ӯbj\x89`\x18\x80\x9f9\xde1\xe2\xa5jsȚ\n\xa3z\x85o[\xe6#\xf1\x9f\x8e6AH\xeag\x05\xf1\x02\f\xc0\x0f3\xfbT\xee'S\xaf\xf9\x8d\x9a\x85\xef\xd8\xd9\xe7\xed\x96^\xa0\n\xae\x13#9Ga\x002p4*\xfc\xed\xfd\xa6\x90Cj\x17>C\x9f\xc3v\x90\a\xa2\xd1$\xd5\xc4[0\x00\x89\xa0^\xf2\xf1\x1as\xe8\aj/\xf7\x1b\x97\xfd\x10\xbf.\x98O<\x0e\x03h\xddɼ\xff\xa4R\xb9V\v\xc2w\xcc\xf6\xe3@\x8f\xae8B\xfc\x16\x06в\x19\xfe\x814ֿ\xa1\x15\xf13DZԧG\xbe#\xfa\xc1\x00\xbc_\xd7\x7f\x8cJ\xfe\xe5\xe5F\xed\b\x1f;\xfc\x00\xb1\x84x\f\x06\xe0\x86cQ\x11\xbf\xa5\x92\xff\xa8\x96\x84\xef\xb8\xc4\x03\x02\x00\xc4!\xe2u\x18\x80\xf3\xf3\xf9\xfd\xa8\xe4\xffNk\xe2\xaf6\x87\xe0,?h̷|\xae\x00\x06pgm\xff^\xea!\xf3+4V\xf2;\xce\xf3c\xc6\x1f\xb8\x80o%\xbaW\xd7\x06`?\xb2\xbbDk¯\x9f\xf1\xe7]~\x98\xf1\anY\x14\xe8\x8dC\x81\x14\xff#\a̡\x1b\xb4(~\x06\x87{\x80\x97\xace-\xe8\xca\x00\xa8,~\x86\xc6ƻ\xb4*\xfe\xa38\xd6\v|c\a\xf1\x8c.\f\x80\xca\xe2\x17I\xfc\x87\xb4*~\xaaj\x90Р%\xd4\x04\xe2@\x91_\x7f\xd9\xc9h#\xdf\xc4{F\xab\xe2\xe7q?\x0e\xf8\x80V^8\xf2+M\x1a\xc0\x89\x8e\xc6W\xaaL!\x97\xb4*~\xa6\x16\xa5?h=\x17\x88\x9fk\xca\x00Nw2=G=\xffq-\x8b\x7f\xbf9\x04\xc9\v\xa4\xbch\xe49M\x18\x00o\x7f\xa41\x7f\xa5\x96\xc5\xcf{\x18p\xb1\a\x90\x98\xbd\xfe\xd8:,\xb7\xf8\x1f<`\x0eݤe\xf1c\x9f?\x90\x91\xcf侉X\xee\x1d~\xc5Z\x17?\xdf\xec\x83D\x052\x1f\"\xbaWu\x06p82\xacH\xeb\xe2/\xc7\xf9~\xe0\x1f\xa6\xaa\xca\x00\x8eD\x86%jqo\xbf\xb3\x87v\"9\x81\x9f\x18\xa5\n\x038\x1a\x15\xfe\x8f\n\xa3\xe1\xb6\xf6K\x7f\x03N\xf9\x01\x7fr\x9b\xf8\x9b\xa2\r\x80\xca័0.h\xbe\xf4'p\x97?\b\xd0\x03I~\xacH\x03\xa07v\x9f\x1ef\xfc\xf1\xf0N\x10`J\xa5\x9c\x14\x94r\xd2o\xac\x1e\xc4\xcfs\x1b8\xe3\x0f\x02L\x82\xa2\f\xe0XLj\xb7\xf7\x19\r\xdf\xeb\xc1\x000\xf1\a\x14\xc0-\xe2ME\x18\x00\xf5\x86\xcf\xee7\x85\x9cC\xef\x0f\x80_9I<\x1dP\x03\xe0\xb1H\x8dE\xbb\x97z`\xec\x0f\x14\xce*➀\x19\x00\x95\xc3}ˍ\xfa\x10?\xf7\xfe8\xea\v\x14HL@\f\x80\xc4\xf0L\xa5\xc9pY/\xbd\xffA\xf4\xfe@\x99\xd4\x11O\xf8\xdd\x00\xa8\xf4\x9f\xab\x17\xf1\x97\xa3\xf7\a\xca&߯\x06p,*\xe2\xf7z\xd8\xed\x87\a{\x00\x95\xf0=\xf1\x86_\f\x807\xfcT\x9bC\xcau\xd3\xfbG\xe0\xac?P\xcdŢ\xf7\xcan\x00\xfc\xd0N=\x89\xbf\x06\xbd?P\x0f\xbdd5\x00\x1a\a?[i2\\\xd5W\xef\x8f=\xff@5\\\xf2\xf5zq_'\xfe\xdeד\xf8q\xc57P!\xd3e1\x80ڨ\xf0\x7f\xad\xd0\xc9v_\a\xb8\xec\x03\xa8t\x9b\xf0K\x92\x1b@\xb59d\x8e\x9e\xc4_\x8d[~\x81z)\x94\xd4\x00\x8ew\x8cx~\x9f\xd1pCO\x06p\f\xcf\xf6\x03\xea\xe5;o\xaf\x15\xf7\xb6\xf7\x9f\xa2'\xf1\xf3\x13~p\xdb\x0fP9\x13$1\x80\x93\xd1Ƨ\xa9\xf7\xbf\xae'\x03\xc0\xb6_\xa0\x01\xbe\"\x9ej\xb5\x01\x1c0\x87\x8eӓ\xf8q\xdd\x17\xd0\x10\xa9\xad2\x80ӝL?\xac4\x1a\xae\xe8I\xfc|\xd9'\x12\ahh_\xc0\x0f[l\x005\x96\xd0Qz\xeb\xfd[\xfb\x94\x9f\xe3\x9d\xcc\xe2\x14\x12OrN\xd9c\x8bX\xf8̰\x16\x19\x00_4@\xbd\xff\t\xbd\x19\x80/\xa7\xfe\xce\x10\xeb\xa3,\xc2j\xb2\x88na\x91\"44R\xfcoH=\x11\xf4\xf7\xd8\xf0H1\x89\xbe\xb6\xb9\xa3\x19\x93\x8a>\xc0\xb1\xe2\x98q\xec8\x86\x11\x8d\xe2\xca1\xe6Xs\xcc9\xf6g\x10/OԺ\xbb4\xc4u\xefo\x0e\xfd\xb3\xde\xc4\xef\xcbc\xbe\xb6P\x82Ƅ\xddILO\xf4\xa7D\xae\x88FBz\x82cı\xf26\xae\xdc\x06\xdc\x16\x88\x9d[\xde\xf4\xd9\x00h,/\xb0\xb2\x91\x01\x04\x1f\xd6\xe4\x98?\xba#\xf5\xfcs\x9d\x8a\x9f9\x9d\xf3\x91\xdf\x12ʶJ c\x83\x96F\x99\x1b\x84\xda\x18\x16\xed\xa6\x16\x88vS#3i\xb2\x04j\xdb^+\xf3\x98\x9fb\x15P\xf17T\x02+E]l\x0f\xad\x1a@\x8d\xcd\x00\x8eEE\xb4-\xd7\xe2\x05 F\x83\xa8N\xc8s)~\xe6L\xeer\xff%\xd3\xe4bQף\xabl\r:0\xdc\xe2\xf6\xd2\xcc\xe9^.7\xf2\xf7\xf0\xf7\xba\xbb\xf4\x94\x7f\x97\\\x9f\x83cıR\x84\x01\xf0\x84\xaeu\x9e8\xdfY\x93\xfb4\xf8\x82\x90\xb6|\x01ȫ\x9a<\xe3\xdfo\x98[\xf13g\xf3\x96\xfb7\x992\xa6\xcb֠\xc6P\xcf\xe7㇄\xd7\xf7\xdeG\x9c\f\v\xf8\xff\xf8kC\xbc\xb8\x9f\x8f\x7f\x97\\\x9f\x83c\xa4\x14\xf17\xcc\xe3ď\xd1j\x15\xf0\xf36U\xa6\x90pM\xee\xf1O\x9d\xee\xd1\x00\xce\xe5\xf9\xbf\xa7\x91\xab\xa4\x8c\xf3\xf1b\xcd.Tޏ1Zlt\xf1\xf1\xa6\xe3\xb8py\xce\xd9sl\x94&~\xdb\xca@\xee2\xad\x1a@0\xdf\x010Rs\xb3\xfe\xddc=\x8a\x9f9?i\x85\xff{\x13\x99&\x04\x8b#Ͳܭ\xe7\xfc\xb4\x9e\\\xb3\xfeY\x8a4\x00\xdb\x1c\xce{\x83\xb5h\x00Cx\x05`\xb2\xd6\f`\xff\xf0q^\x19@\xdd\xe4\x12\xff\xf7&y\x1fɶ\xfc\x97g\xb2\xc8.~\xfe\x1dr-\az\xda\xe5\x17H.\xa7\xe5k\xd1\x00&\xb4)7\x06\xcf\xd6\\\xf9\x9f\x9c\xef\x95\x01\\\b\x80\x01؆\x012\xee6\x9bi\x91\xcf\x04f\xcax\xb4\x96c\xa2T\xf1ی{\xc2\"-\x1a@\x11/\x01.՚\x01\x1cH\x9f\xeb\x95\x01\\\x9c\xb220\x06\x10\xd7G\xf6\v7\xc3$|\x88\t\xbf\x96\xdc\x17\x97rL\x94l\x00\xd7(W4h\x00\v\xd9\x00\xd6hk\xbf\x7f\x84W\xe2\x0f\xa4\x01\\\x1c8\\\xf6\xc6\xe5+\xb1Ӎ\x16\xa7{\x03\xbc\x85\x7f\x96_\xc3\x1f\xd7ksL\x14m\x00\xb6\xca\xed]\xad\x19@\t\x1b\xc0\x17\x9a\xaa\x00\xcca^\x1b@]\x80\x86\x00\xfe\x9cP\xdaڱ\xfe\n\xed\xf0P\xef\x87\x06\xfc\xbd\xfc3[\xfdxM\x19\xc7D\xe9\x06p\xbekG\xad\x19\xc0gl\x00{47\a\x906K\xb1\xab\x006\x03\xe8\x15\x1b\x90\aq\xac\x8d\xb2\x88\t&\x8b\x18e\x8c\x14\xbd\xc3\xeao\xdfe\xf8\xef\xfc\x7f\xfc5\xfe\x9e@<\xa8\x84c\xa2\xe89\x80I\xc5Z\x1c\x02\xecd\x038\xa89\x03H\x9c\xe4\x95\x01\xf8{#\x90\x86{\x92\xd6C1Q\xb4\x01d\xbd\xafŸ\x1f`\x038\xa3\xb9e\xc0AI^\x19\xc0\x99\\\xff/;\xf1\xb9v\b\xde\xc5.@?\x9c\xf9o\xf12`R\xb6\x16c~\x8a\r\xe0\x9a\xe66\x02u\x8a\xf6\xca\x00N\xe5,\xf3\xffF\xa0aI\x10\xbb\xab\x8d@\x14\x1b\xc5n\x04\n\xc0\xb0\xcd\x0f\\e\x03\xb8\xa5ɭ\xc0\t\xb9\x1e\r\xc0_ǁ\x1b\xcaȩ\xab\xc4\xf9\x98w!vWPl\xae\xba\xbb\xc7/`\a\x82\xe6j5\xe6\xb7\xda\xec\x8d\b\xfeZ\xab7\x00y2\x80\xe3\x13\x96\xf8\xb7\xf7\x1f\x9d\t\x91{\xaa\x02Fg*\xaf\xf7\xd7\xee\rA_\xb5!\xb1\x9c\xd3\xecS\x7f\a\xa7\xb85\x80#\xd6\x0f\xfc\u05cbLX\xe8խ\xc1\xba\x87bıR\xccI\xc0\xe4\x1c-\xc7\xfb\f\x1b\xc0a\xcd^\af\x0e\x17\xd5)E.\r@\xaeKA\x9d^1\x85KB}\xba\x04\xf4\xda\xd4Ձ\x9f\xf9\xcf\xf9P\x9c\xef\x1a\xad\xe5X\x1f`\x03أY\x03\xe0\x9d\x81\x1d\xa3Du\xaak\x13\xb8$\xf3\x98\x93Ǵ\x1a=I&\xfbƠ@\xce\a\\\xcd]\"\xeazt\xd7z\x9cwjo'\xa03\x13\x88\xb2P%P\xe8\xff\xcd@SV\x8a\v\xfd\x06@\xd0-5\x01\x8aݵ\x00l\u05fe\x9a\xb3X\xd4u\x8f\xd1C\x8c7\xf0q\xe0\x8fu\xf1L\x80(\x93\xa8\x8e\xb7\xfam)\xf0\xea\xc4E\xb2\x1f\xfa\xd1\x03\x1cC\x8e\xa5\xffnm\x9aF\xe2\uf897\xf8\x16\xf3CA\x16\xeb\xeaq`=\xfb7y>\xc0\xb1\xec\x0f\xa5\x7f\x1e\xc0\xe8LQ\xd79\n\x02\x96\xca\x04(\x96\xb6\xd5\x01\x19\x9f\x0f`{\x1e\x80\xcc\x17\xb7*\x90\xf7\xd9\x00f\xe8홀\xfc\x84\xa0\xaa\xf7F\x89괙\x92=\x1c\x84ǫ\x97S'\x89\v\xbdzA\xb4\xb2\x9d\x17\xe8e\x8b\xb1\x94s\x03W'-'s\x19\xaf\xa7'\x025&\x9f\x9f\n\x9c\xa3ǧ\x02ی\xc0\x18**c\xfb\x8bK\xe3g\x8a\xab\xf9-H\xaa\xfc\x8fm\xdbW/ŏ\xd5\xe2QQ\xe5V\x04\x14k\x8e\xb9m\xebp\v\x9e\x05\xc8m}%s\xb6\xb88x\xa4\xde+\xb5\xf1\\\x01\x8cҫ\x014<\"<*B\xd4u\x89\x12\x17\xfb\r\xb0\x95\x9a<\x0e\xbcb\x9dCc\xcf\x0fl\xa7\xc0\xaeMYa[\x12\xe2'\xd2\U000add57\x93&\xd8\x1e\x1aQ\x17\xd3\x19\x82\f\xb4\x19P\x1bp[p\x9bp\xdbp\x1bq[\xd9ڌڎېے۔ۖۘ\xdb\x1a\xb1\xb31\x94W\x01\"\xf4n\x005\xe6P$\x03\xd0#\xc1\\\x01\xbc\xaaw\x03\xa80\x1a\x90\f@\x8f\xfc\x9c\r\xe0~\x12\xc1M\xbd\x9b\xc0\xc9h#\x12\x02\xe8\t~2\xd0\xfd\x8eg\x03\x1eԻ\x01\x1c\x8e\fCR\x00=Q\xdd\xf0pP\xaa\x02V\xeb\xdd\x00\xaaL!H\n\xa0'\x8a\x1b\x1b\xc0D\xbd\x1b\x00s\x1a\xc3\x00\xa0\xa3%\xc0\x06\x03\xa82\x19z\xc1\x00\x82\xc5!\v\x86\x01@7\xc44\x18@\xb59\xe4\x8f0\x00^\r\b\x16g;\x99\x90\x1c@\x0f\xfc\xbe\xc1\x00j\xa3\u009f\x82\x01\xd4s42\x1c\xc9\x01\xf4\xc0\x93\r\x06\xc0\x94\x1b\x83\xf7\xc3\x00\x82E%\xf6\x04\x00\xedS\xe1\xd0}\x83\x01T\x18\rSa\x00\xf5\x1c\xef\x18\x81$\x01Z&\xe7.\x03\xd8g4\x84C\xfc\xf6\x1b\x85\xcdX\x12\x04\xda\xde\x02|\x97\x01\xec7\x85\xa3\xc1\x00\xd1cU\x00h\x8aw|1\x80\xfbʍ\xc1\xe7!zl\x13\x06\x9a\xe0,q\x9f\xd7\x06`?\x1c4\x19\x82\xc7\xfd\x81@\x13Lp\xa5s\x97\x06Pe2\xbc\x01\xb1\xe3\xc4 \xd0\x04\xbf\xf6\xd9\x00쓁\xb8#\xc0ͳ\x04NGc>\x00\xa8\xe7\xec\x7fK\f`\x10\xc4\xee~R\xf04&\x05\x81\xb2\xe9\xdbb\x03\xd8\x11\xda\xfe\a\x94\xe8\xe7 v\x98\x00P%g\x9ao\xfd\xf5\xc9\x00lW\x85E\x04\x8f\x84\xd0a\x02@\x95\f\xf6\xa4o\x8f\x06\xb0;<\xe8\x11J\xf2\x8b\x10\xba\xe7\xbb\x04\xcf\xc0\x04\x80r\xa8#\x1en\xb5\x01\xd8/\fM\x81\xc8a\x02@U\x8c\xf4F\xdb^\x19\x80\xfd|\xc0U\x88\x1c&\x00T\xc1eg\xfb\xfe[l\x00\xf6\x15\x81q\x10\xb8\xf7s\x02':\xe2\xdc\x00\b\x18)\xde\xea\xdak\x03\xa0\xa4~\x9a\x92\xfb\xff p98\x80\x12\xe3{\x88\x03\xb09\xf0\xb9\x86\xfd\xa6\x10\x9bA\xf0\\\x03\x9b\x04߇\xc0\x97\xa7r\x15\xc1\xf3\x0e*7\x8b\xef\x89>JО\"\f\xc0>9\x18F\tp\x19\"\x00\xbe\xaeR\xf0\xa4$\x0f5x\xf9\x92\x87\x1b\xbcb\xc1\x86Qk7\r\x9e\x97`\xe3\xe0*\x83̓\xe7(\xf8\x02V6\x11>R\xed\xe7\xf9\x8a\x8bD\a\xa5\xe8N1\x06`\x9f\x1c|\x81؎\xc4\x06\x81\xaa>\xd8Px\ue087(\\\x854\xa7\xaa\t\xf5\xdfW\xd9\x18\xfa\xf9}N\xa8\xb0a\xcb\xed\x17\x94\xa49E\x19\x80\xed\x92\xd1\xf0\xa0\xb6\x14\xa8\x89\x14\xa8\xdbHJ\xa0\x118\x97'\xee\bk\xdfVizS\x9c\x014\x9a\x17\b*7\x06_D\xf2\x00\x95s\x91:\xb4 \xa5\xeaL\xb1\x06\xc0Py\xf5<\x95NeH\"\xa0\xd2CWe\x9c\xc3J֘\xa2\r\x809h\t\xbd\x8f\xaa\x81t\n\xe8\r$\x15P\t78g9w\x95\xae/\xc5\x1b@\xa3!\xc1/\xa8\x94ڈ\xe4\x02\n\x9fH\xdcH\xbd\xfe/Ԣ+\xd5\x18@##\xe8LA>\x87d\x03\n\xe3\x1c\x95\xfc\x9dզ'\xd5\x19\x00\xb3\xdf\x14\xf2\x18\x19A>\x05\xfd\x16\x12\x0f\x04\x18\xce\xc1\x82=\x11A\x8f\xabQK\xaa4\x80;\x93\x84!\xbf%\xd7݁$\x04\x01b\xe7\x9e\xf0\xa07Ԭ!U\x1b\x00\xc3w\xa6U\x9a\f\xdd\xf0@\x12\xe0\xc7\xd9\xfd\x1a\xaaBc\xf8\xceK\xb5\xebG\xf5\x06\xe0\xe0hd\xf8\xbd\xd4 \x91d\x04{\x90\xa4@&\xf6p\x8eq\xaeiE7\x9a1\x80f'\f;\x94\x1b\x83\xb1\x7f\x00H\xc2ވ\xe02\x1a\xe3\a\xb5i\xa3=\xbdh\xd2\x00\x1a\x19\xc1\x9f\xc9\b\xd6\"\x89A\v\x97\xf4\xd6\xee\n\x0f\xfa\x8b\x16\x85\xaf\v\x03p@\x0e\xfe:5\xe8T\xa2\x0e\x89\r\xdcb\f\xae\xab4\x1a\xa6\xd4XB\x7f\xad\am\xe8\xc2\x00\x1c\x94\x85\xbcӖ\x1a9\x94XF|\x8b\x84\a\xf6\x12\xff[*\xf1?\xdaC\xb9\xc1\x87\xd1\xf4\xa4\t]\x19@cv\x87\a=A\x8d߇(\xc3\xc9Cݞ\xd0+#\xc1\xf7\xf9\xb4\xfd\xdbO\xe9U\a\xba5\x80f\xf7\x10\xbcD\x8c$\xd6\x11_A\x1c\x9a\xe5+\x1e\xd7W\x99B\xe2\xa9\xc4\xffg\xe4>\f\xc0\xd50\xe1M\x1a\v\xa6П\x9f\x13\xdfh\xac\xd7\xfb\x9a\xb8@\x9f\xef8\x89\xe1\x00\xc3\x7f\xb7\xfd_\xfd״T\r}C\xa5\xfd\xc6]\xe1A\xa9;B\xdb\xff\xa9\xe4\x1fo\xb5E\x8e\xc3\x00Zr\x85\xf9_\x89\f\x12\xca\x06\xfa\xf7)\x85\x89\xe4\x06\xbd\xaf\xa3\xf4\xfe>#\xe6T\x18\r)\xfb\x8c\x86.\xc4\x1f\xf9\x00\x15\xf1\"\xf1\f?\x9e͗G\xb9\xd9\x7f\xe6E\xfbk\xf0ku\xe1\u05ee\xff\x1d\xc1\x9f\xf1\xefT\xd8\tMn\x93S\xf4>7\xf0I max { 20 | return fmt.Errorf("must be lesser than %d", max) 21 | } 22 | 23 | return nil // Nothing to validate with, same as having no validator. 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /ui/range_validator_test.go: -------------------------------------------------------------------------------- 1 | package ui 2 | 3 | import ( 4 | "github.com/stretchr/testify/assert" 5 | "testing" 6 | ) 7 | 8 | func TestValid(t *testing.T) { 9 | inputs := []string{"1", "99", "01", "001"} 10 | validate := NewRangeValidator(0, 100) 11 | 12 | for _, input := range inputs { 13 | err := validate(input) 14 | assert.Nil(t, err) 15 | } 16 | } 17 | 18 | func TestNegativeRange(t *testing.T) { 19 | inputs := []string{"-99", "-11"} 20 | validate := NewRangeValidator(-100, -10) 21 | 22 | for _, input := range inputs { 23 | err := validate(input) 24 | assert.Nil(t, err) 25 | } 26 | } 27 | 28 | func TestNotANumber(t *testing.T) { 29 | inputs := []string{"", " ", "foo", "0x", "1 ", " 1"} 30 | validate := NewRangeValidator(10, 100) 31 | 32 | for _, input := range inputs { 33 | err := validate(input) 34 | assert.EqualError(t, err, "not a valid number") 35 | } 36 | } 37 | 38 | func TestMinimum(t *testing.T) { 39 | err := NewRangeValidator(10, 100)("5") 40 | 41 | assert.EqualError(t, err, "must be greater than 10") 42 | } 43 | 44 | func TestMinimumIncluded(t *testing.T) { 45 | assert.Nil(t, NewRangeValidator(10, 100)("10")) 46 | } 47 | 48 | func TestMaximum(t *testing.T) { 49 | err := NewRangeValidator(10, 100)("105") 50 | 51 | assert.EqualError(t, err, "must be lesser than 100") 52 | } 53 | 54 | func TestMaximumIncluded(t *testing.T) { 55 | assert.Nil(t, NewRangeValidator(10, 100)("100")) 56 | } 57 | -------------------------------------------------------------------------------- /ui/settings.go: -------------------------------------------------------------------------------- 1 | package ui 2 | 3 | import ( 4 | "fmt" 5 | "fyne.io/fyne/v2" 6 | "fyne.io/fyne/v2/data/binding" 7 | "fyne.io/fyne/v2/widget" 8 | "github.com/tomsquest/fynodoro/pref" 9 | ) 10 | 11 | type Settings interface { 12 | Show() 13 | SetOnSubmit(callback func()) 14 | SetOnClosed(callback func()) 15 | } 16 | 17 | // type validation 18 | var _ Settings = (*settings)(nil) 19 | 20 | type settings struct { 21 | win *fyne.Window 22 | form *widget.Form 23 | } 24 | 25 | func NewSettings() Settings { 26 | w := fyne.CurrentApp().NewWindow("Settings") 27 | f := makeForm() 28 | f.OnCancel = func() { 29 | w.Close() 30 | } 31 | // Need to "refresh" to make the Submit and Cancel buttons appears 32 | f.Refresh() 33 | 34 | w.SetContent(f) 35 | return &settings{win: &w, form: f} 36 | } 37 | 38 | func (s *settings) Show() { 39 | (*s.win).Show() 40 | } 41 | 42 | func (s *settings) SetOnSubmit(callback func()) { 43 | formSubmit := s.form.OnSubmit 44 | s.form.OnSubmit = func() { 45 | formSubmit() 46 | (*s.win).Close() 47 | callback() 48 | } 49 | (*s.form).Refresh() 50 | } 51 | 52 | func (s *settings) SetOnClosed(callback func()) { 53 | (*s.win).SetOnClosed(callback) 54 | } 55 | 56 | func makeForm() *widget.Form { 57 | myPref := pref.Load() 58 | form := widget.NewForm() 59 | 60 | workDurationBinding := binding.NewInt() 61 | _ = workDurationBinding.Set(myPref.WorkDuration) 62 | form.AppendItem(newIntegerFormItem(workDurationBinding, "Work duration in minutes", "Set the duration of the Work period. Default is: %d minutes.", NewRangeValidator(0, 999))) 63 | 64 | shortBreakDurationBinding := binding.NewInt() 65 | _ = shortBreakDurationBinding.Set(myPref.ShortBreakDuration) 66 | form.AppendItem(newIntegerFormItem(shortBreakDurationBinding, "Short break duration in minutes", "Set the duration of the short break. Default is: %d minutes. 0 to disable.", NewRangeValidator(0, 999))) 67 | 68 | longBreakDurationBinding := binding.NewInt() 69 | _ = longBreakDurationBinding.Set(myPref.LongBreakDuration) 70 | form.AppendItem(newIntegerFormItem(longBreakDurationBinding, "Long break duration in minutes", "Set the duration of the long break. Default is: %d minutes. 0 to disable.", NewRangeValidator(0, 999))) 71 | 72 | workRoundsBinding := binding.NewInt() 73 | _ = workRoundsBinding.Set(myPref.WorkRounds) 74 | form.AppendItem(newIntegerFormItem(workRoundsBinding, "Work rounds", "Set how many Work rounds before a long break. Default is: %d. 0 to disable.", NewRangeValidator(0, 999))) 75 | 76 | form.OnSubmit = func() { 77 | workDuration, _ := workDurationBinding.Get() 78 | shortBreakDuration, _ := shortBreakDurationBinding.Get() 79 | longBreakDuration, _ := longBreakDurationBinding.Get() 80 | workRounds, _ := workRoundsBinding.Get() 81 | 82 | newPref := pref.Pref{ 83 | workDuration, 84 | shortBreakDuration, 85 | longBreakDuration, 86 | workRounds, 87 | } 88 | pref.Save(newPref) 89 | } 90 | 91 | return form 92 | } 93 | 94 | func newIntegerFormItem(bind binding.Int, entryText string, hintText string, _ fyne.StringValidator) *widget.FormItem { 95 | value, _ := bind.Get() 96 | entry := newIntegerEntryWithData(binding.IntToString(bind)) 97 | 98 | // Validation disabled until https://github.com/fyne-io/fyne/issues/3961 is fixed (Fyne 2.5.1) 99 | //entry.Validator = validator 100 | 101 | formItem := widget.NewFormItem(entryText, entry) 102 | formItem.HintText = fmt.Sprintf(hintText, value) 103 | return formItem 104 | } 105 | -------------------------------------------------------------------------------- /ui/tappable_text.go: -------------------------------------------------------------------------------- 1 | package ui 2 | 3 | import ( 4 | "fyne.io/fyne/v2" 5 | "fyne.io/fyne/v2/canvas" 6 | "fyne.io/fyne/v2/driver/desktop" 7 | "fyne.io/fyne/v2/widget" 8 | "image/color" 9 | ) 10 | 11 | type TappableText struct { 12 | widget.BaseWidget 13 | Label *canvas.Text 14 | OnTapped func() 15 | } 16 | 17 | func NewTappableText(title string, color color.Color, tapped func()) *TappableText { 18 | item := &TappableText{ 19 | Label: canvas.NewText(title, color), 20 | OnTapped: tapped, 21 | } 22 | item.ExtendBaseWidget(item) 23 | return item 24 | } 25 | 26 | func (t *TappableText) CreateRenderer() fyne.WidgetRenderer { 27 | return widget.NewSimpleRenderer(t.Label) 28 | } 29 | 30 | func (t *TappableText) Tapped(*fyne.PointEvent) { 31 | if onTapped := t.OnTapped; onTapped != nil { 32 | onTapped() 33 | } 34 | } 35 | 36 | func (t *TappableText) SetText(text string) { 37 | t.Label.Text = text 38 | t.Refresh() 39 | } 40 | 41 | func (t *TappableText) Cursor() desktop.Cursor { 42 | return desktop.PointerCursor 43 | } 44 | -------------------------------------------------------------------------------- /ui/ui.go: -------------------------------------------------------------------------------- 1 | package ui 2 | 3 | import ( 4 | "fyne.io/fyne/v2" 5 | "fyne.io/fyne/v2/canvas" 6 | "fyne.io/fyne/v2/container" 7 | "fyne.io/fyne/v2/driver/desktop" 8 | "fyne.io/fyne/v2/layout" 9 | "fyne.io/fyne/v2/theme" 10 | "fyne.io/fyne/v2/widget" 11 | "github.com/tomsquest/fynodoro/pomodoro" 12 | "github.com/tomsquest/fynodoro/pref" 13 | "time" 14 | ) 15 | 16 | type BuildInfo struct { 17 | Version string 18 | Commit string 19 | CommitDate string 20 | } 21 | 22 | func Display(app fyne.App, buildInfo BuildInfo) { 23 | myPref := pref.Load() 24 | thePomodoro := pomodoro.NewPomodoro(&pomodoro.Params{ 25 | WorkDuration: time.Duration(myPref.WorkDuration) * time.Minute, 26 | ShortBreakDuration: time.Duration(myPref.ShortBreakDuration) * time.Minute, 27 | LongBreakDuration: time.Duration(myPref.LongBreakDuration) * time.Minute, 28 | WorkRounds: myPref.WorkRounds, 29 | }) 30 | 31 | mainWindow := app.NewWindow("Fynodoro") 32 | mainWindow.SetMaster() 33 | mainWindow.SetContent(MakeClassicLayout(thePomodoro)) 34 | mainWindow.SetCloseIntercept(mainWindow.Hide) 35 | mainWindow.SetFixedSize(true) 36 | 37 | if desk, ok := app.(desktop.App); ok { 38 | aboutWindow := makeAboutWindow(app, buildInfo) 39 | trayMenu := fyne.NewMenu("Fynodoro", 40 | fyne.NewMenuItem("Show", mainWindow.Show), 41 | fyne.NewMenuItem("Hide", mainWindow.Hide), 42 | fyne.NewMenuItem("Center", mainWindow.CenterOnScreen), 43 | fyne.NewMenuItem("About", aboutWindow.Show)) 44 | desk.SetSystemTrayMenu(trayMenu) 45 | } 46 | 47 | mainWindow.ShowAndRun() 48 | } 49 | 50 | func makeAboutWindow(app fyne.App, buildInfo BuildInfo) fyne.Window { 51 | aboutWindow := app.NewWindow("About Fynodoro") 52 | aboutWindow.SetFixedSize(true) 53 | 54 | img := canvas.NewImageFromResource(AssetIconPng) 55 | img.SetMinSize(fyne.NewSquareSize(64)) 56 | imgContainer := container.NewHBox(img, layout.NewSpacer()) 57 | 58 | markdownStr := "# Fynodoro" + "\n" 59 | markdownStr += "" + "\n" 60 | markdownStr += "Fynodoro is a tiny and cute Pomodoro Widget" + "\n" 61 | markdownStr += "" + "\n" 62 | markdownStr += "- `Version: " + buildInfo.Version + "`" + "\n" 63 | markdownStr += "- `Commit date: " + buildInfo.CommitDate + " `" + "\n" 64 | markdownStr += "- `Commit: " + buildInfo.Commit + "`" + "\n" 65 | markdown := widget.NewRichTextFromMarkdown(markdownStr) 66 | 67 | closeButton := &widget.Button{ 68 | Text: "Close", 69 | OnTapped: aboutWindow.Hide, 70 | } 71 | buttonsContainer := container.NewHBox(layout.NewSpacer(), closeButton) 72 | 73 | aboutWindow.SetContent(container.NewVBox(imgContainer, markdown, layout.NewSpacer(), buttonsContainer)) 74 | return aboutWindow 75 | } 76 | 77 | func MakeClassicLayout(thePomodoro *pomodoro.Pomodoro) fyne.CanvasObject { 78 | timer := NewTappableText(formatDuration(thePomodoro.RemainingTime), nil, nil) 79 | timer.Label.TextSize = 60 80 | timer.Label.TextStyle.Bold = true 81 | timer.Label.Alignment = fyne.TextAlignCenter 82 | timerPanel := container.NewCenter(container.NewHBox(timer)) 83 | 84 | playButton := widget.NewButtonWithIcon("", theme.MediaPlayIcon(), nil) 85 | stopButton := widget.NewButtonWithIcon("", theme.MediaStopIcon(), nil) 86 | nextButton := widget.NewButtonWithIcon("", theme.MediaSkipNextIcon(), nil) 87 | settingsButton := widget.NewButtonWithIcon("", theme.SettingsIcon(), nil) 88 | buttons := container.NewCenter(container.NewHBox(playButton, stopButton, nextButton, settingsButton)) 89 | 90 | timer.OnTapped = func() { 91 | playPausePomodoro(thePomodoro, playButton) 92 | } 93 | playButton.OnTapped = func() { 94 | playPausePomodoro(thePomodoro, playButton) 95 | } 96 | stopButton.OnTapped = func() { 97 | stopPomodoro(thePomodoro, playButton, timer) 98 | } 99 | nextButton.OnTapped = func() { 100 | nextPomodoro(thePomodoro, playButton, timer) 101 | } 102 | settingsButton.OnTapped = func() { 103 | settings := NewSettings() 104 | settings.SetOnSubmit(func() { 105 | // Apply new preferences to current pomodoro 106 | newPref := pref.Load() 107 | thePomodoro.SetWorkDuration(time.Duration(newPref.WorkDuration) * time.Minute) 108 | thePomodoro.SetShortBreakDuration(time.Duration(newPref.ShortBreakDuration) * time.Minute) 109 | thePomodoro.SetLongBreakDuration(time.Duration(newPref.LongBreakDuration) * time.Minute) 110 | thePomodoro.SetWorkRounds(newPref.WorkRounds) 111 | thePomodoro.SetRemainingTime() 112 | 113 | // Display new duration 114 | setTimerRemainingTime(thePomodoro, timer) 115 | }) 116 | settings.SetOnClosed(func() { 117 | settingsButton.Enable() 118 | }) 119 | 120 | settingsButton.Disable() 121 | settings.Show() 122 | } 123 | 124 | thePomodoro.OnTick = func() { 125 | setTimerRemainingTime(thePomodoro, timer) 126 | } 127 | thePomodoro.OnEnd = func(kind pomodoro.Kind) { 128 | setTimerRemainingTime(thePomodoro, timer) 129 | 130 | playButton.Icon = theme.MediaPlayIcon() 131 | playButton.Refresh() 132 | 133 | notifyPomodoroDone(kind) 134 | } 135 | 136 | return container.NewVBox(timerPanel, buttons) 137 | } 138 | 139 | func setTimerRemainingTime(thePomodoro *pomodoro.Pomodoro, timer *TappableText) { 140 | timer.SetText(formatDuration(thePomodoro.RemainingTime)) 141 | timer.Refresh() 142 | } 143 | 144 | func playPausePomodoro(thePomodoro *pomodoro.Pomodoro, playButton *widget.Button) { 145 | if thePomodoro.Running { 146 | thePomodoro.Pause() 147 | playButton.Icon = theme.MediaPlayIcon() 148 | } else { 149 | thePomodoro.Start() 150 | playButton.Icon = theme.MediaPauseIcon() 151 | } 152 | playButton.Refresh() 153 | } 154 | 155 | func stopPomodoro(thePomodoro *pomodoro.Pomodoro, playButton *widget.Button, timer *TappableText) { 156 | thePomodoro.Stop() 157 | playButton.Icon = theme.MediaPlayIcon() 158 | playButton.Refresh() 159 | setTimerRemainingTime(thePomodoro, timer) 160 | } 161 | 162 | func nextPomodoro(thePomodoro *pomodoro.Pomodoro, playButton *widget.Button, timer *TappableText) { 163 | thePomodoro.Next() 164 | playButton.Icon = theme.MediaPlayIcon() 165 | playButton.Refresh() 166 | setTimerRemainingTime(thePomodoro, timer) 167 | } 168 | --------------------------------------------------------------------------------