├── .github ├── FUNDING.yml └── workflows │ ├── comment.yml │ ├── deploy.yml │ ├── python_publish.yml │ └── ruff.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── dooit_extras ├── bar_widgets │ ├── __init__.py │ ├── _base.py │ ├── clock.py │ ├── current_workspace.py │ ├── date.py │ ├── mode.py │ ├── platform.py │ ├── powerline.py │ ├── spacer.py │ ├── status_icons.py │ ├── text_box.py │ ├── text_poller.py │ ├── ticker.py │ └── workspace_progress.py ├── formatters │ ├── __init__.py │ ├── description.py │ ├── due.py │ ├── effort.py │ ├── recurrence.py │ ├── status.py │ └── urgency.py └── scripts │ ├── __init__.py │ ├── custom_tree_borders.py │ ├── dim_unfocused.py │ └── toggle_workspaces.py ├── flake.lock ├── flake.nix ├── imgs ├── config1.png ├── config2.png ├── config3.png └── logo.png ├── nix └── default.nix ├── poetry.lock ├── pyproject.toml └── site ├── bun.lockb ├── docs ├── .vitepress │ ├── config.ts │ └── theme │ │ ├── FiraCodeNerdFont-Regular.ttf │ │ ├── index.js │ │ └── style.css ├── configs │ ├── code │ │ ├── config1.py │ │ ├── config2.py │ │ └── config3.py │ ├── config1.md │ ├── config2.md │ ├── config3.md │ └── previews │ │ ├── config1 │ │ ├── dashboard.png │ │ ├── help.png │ │ └── mainscreen.png │ │ ├── config2 │ │ ├── dashboard.png │ │ ├── help.png │ │ └── mainscreen.png │ │ └── config3 │ │ ├── dashboard.png │ │ ├── help.png │ │ └── mainscreen.png ├── formatters │ ├── description.md │ ├── due.md │ ├── effort.md │ ├── recurrence.md │ ├── status.md │ └── urgency.md ├── getting_started │ ├── introduction.md │ └── usage.md ├── index.md ├── scripts │ ├── custom_tree_borders.md │ ├── dim_unfocused.md │ ├── previews │ │ ├── custom_tree_borders.png │ │ ├── dim_unfocused_todos.png │ │ ├── dim_unfocused_workspaces.png │ │ └── toggle_workspaces.mp4 │ └── toggle_workspaces.md └── widgets │ ├── clock.md │ ├── current_workspace.md │ ├── custom.md │ ├── date.md │ ├── imgs │ ├── powerline_arrow.png │ ├── powerline_flame.png │ ├── powerline_ice.png │ ├── powerline_rounded.png │ └── powerline_triangles.png │ ├── mode.md │ ├── platform.md │ ├── powerline.md │ ├── spacer.md │ ├── status_icons.md │ ├── text_box.md │ ├── ticker.md │ └── workspace_progress.md └── package.json /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [kraanzu] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 13 | -------------------------------------------------------------------------------- /.github/workflows/comment.yml: -------------------------------------------------------------------------------- 1 | name: issues 2 | on: 3 | issues: 4 | types: [closed] 5 | jobs: 6 | add-comment: 7 | runs-on: ubuntu-latest 8 | permissions: 9 | issues: write 10 | steps: 11 | - name: Did we solve your problem? 12 | uses: peter-evans/create-or-update-comment@a35cf36e5301d70b76f316e867e7788a55a31dae 13 | with: 14 | issue-number: ${{ github.event.issue.number }} 15 | body: | 16 | Did we solve your problem? 17 | Glad we could help! 18 | 19 | Consider sponsoring my work through [github sponsors](https://github.com/sponsors/kraanzu) :smile: 20 | – @kraanzu 21 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy VitePress site to Pages 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | 7 | workflow_dispatch: 8 | 9 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 10 | permissions: 11 | contents: read 12 | pages: write 13 | id-token: write 14 | 15 | concurrency: 16 | group: pages 17 | cancel-in-progress: false 18 | 19 | jobs: 20 | build: 21 | runs-on: ubuntu-latest 22 | steps: 23 | - name: Checkout 24 | uses: actions/checkout@v4 25 | with: 26 | fetch-depth: 0 27 | - uses: oven-sh/setup-bun@v1 28 | - name: Setup Node 29 | uses: actions/setup-node@v4 30 | with: 31 | node-version: 20 32 | - name: Setup Pages 33 | uses: actions/configure-pages@v4 34 | - name: Install dependencies 35 | run: | 36 | cd site 37 | bun install 38 | - name: Build with VitePress 39 | run: | 40 | cd site 41 | bun run docs:build 42 | - name: Upload artifact 43 | uses: actions/upload-pages-artifact@v3 44 | with: 45 | path: site/docs/.vitepress/dist 46 | 47 | deploy: 48 | environment: 49 | name: github-pages 50 | url: ${{ steps.deployment.outputs.page_url }} 51 | needs: build 52 | runs-on: ubuntu-latest 53 | name: Deploy 54 | steps: 55 | - name: Deploy to GitHub Pages 56 | id: deployment 57 | uses: actions/deploy-pages@v4 58 | -------------------------------------------------------------------------------- /.github/workflows/python_publish.yml: -------------------------------------------------------------------------------- 1 | # This workflow will upload a Python Package using Twine when a release is created 2 | # For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries 3 | 4 | # This workflow uses actions that are not certified by GitHub. 5 | # They are provided by a third-party and are governed by 6 | # separate terms of service, privacy policy, and support 7 | # documentation. 8 | 9 | name: Upload Python Package 10 | 11 | on: 12 | release: 13 | types: [published] 14 | workflow_dispatch: 15 | 16 | permissions: 17 | contents: read 18 | 19 | jobs: 20 | deploy: 21 | 22 | runs-on: ubuntu-latest 23 | 24 | steps: 25 | - uses: actions/checkout@v3 26 | - name: Set up Python 27 | uses: actions/setup-python@v3 28 | with: 29 | python-version: '3.x' 30 | - name: Install dependencies 31 | run: | 32 | python -m pip install --upgrade pip 33 | pip install poetry 34 | pip install . 35 | - name: Configure TOKEN 36 | run: poetry config pypi-token.pypi ${{ secrets.PYPI_API_TOKEN }} 37 | - name: Publish package 38 | run: poetry publish --build 39 | -------------------------------------------------------------------------------- /.github/workflows/ruff.yml: -------------------------------------------------------------------------------- 1 | name: Ruff Checks (lint/format) 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | lint: 7 | runs-on: ubuntu-latest 8 | strategy: 9 | matrix: 10 | python-version: ["3.10"] 11 | steps: 12 | - uses: actions/checkout@v2 13 | - name: Set up Python ${{ matrix.python-version }} 14 | uses: actions/setup-python@v2 15 | with: 16 | python-version: ${{ matrix.python-version }} 17 | - name: Install dependencies 18 | run: | 19 | python -m pip install --upgrade pip 20 | pip install ruff 21 | - name: Lint Check 22 | run: | 23 | ruff check . 24 | - name: Format Check 25 | run: | 26 | ruff format --check . 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # nix builds 2 | /result 3 | 4 | # Byte-compiled / optimized / DLL files 5 | __pycache__/ 6 | *.py[cod] 7 | *$py.class 8 | 9 | # uv 10 | .python-version 11 | 12 | # C extensions 13 | *.so 14 | 15 | # Distribution / packaging 16 | .Python 17 | build/ 18 | develop-eggs/ 19 | dist/ 20 | downloads/ 21 | eggs/ 22 | .eggs/ 23 | lib/ 24 | lib64/ 25 | parts/ 26 | sdist/ 27 | var/ 28 | wheels/ 29 | share/python-wheels/ 30 | *.egg-info/ 31 | .installed.cfg 32 | *.egg 33 | MANIFEST 34 | 35 | # PyInstaller 36 | # Usually these files are written by a python script from a template 37 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 38 | *.manifest 39 | *.spec 40 | 41 | # Installer logs 42 | pip-log.txt 43 | pip-delete-this-directory.txt 44 | 45 | # Unit test / coverage reports 46 | htmlcov/ 47 | .tox/ 48 | .nox/ 49 | .coverage 50 | .coverage.* 51 | .cache 52 | nosetests.xml 53 | coverage.xml 54 | *.cover 55 | *.py,cover 56 | .hypothesis/ 57 | .pytest_cache/ 58 | cover/ 59 | 60 | # Translations 61 | *.mo 62 | *.pot 63 | 64 | # Django stuff: 65 | *.log 66 | local_settings.py 67 | db.sqlite3 68 | db.sqlite3-journal 69 | 70 | # Flask stuff: 71 | instance/ 72 | .webassets-cache 73 | 74 | # Scrapy stuff: 75 | .scrapy 76 | 77 | # Sphinx documentation 78 | docs/_build/ 79 | 80 | # PyBuilder 81 | .pybuilder/ 82 | target/ 83 | 84 | # Jupyter Notebook 85 | .ipynb_checkpoints 86 | 87 | # IPython 88 | profile_default/ 89 | ipython_config.py 90 | 91 | # pyenv 92 | # For a library or package, you might want to ignore these files since the code is 93 | # intended to run in multiple environments; otherwise, check them in: 94 | # .python-version 95 | 96 | # pipenv 97 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 98 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 99 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 100 | # install all needed dependencies. 101 | #Pipfile.lock 102 | 103 | # poetry 104 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 105 | # This is especially recommended for binary packages to ensure reproducibility, and is more 106 | # commonly ignored for libraries. 107 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 108 | #poetry.lock 109 | 110 | # pdm 111 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 112 | #pdm.lock 113 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 114 | # in version control. 115 | # https://pdm.fming.dev/latest/usage/project/#working-with-version-control 116 | .pdm.toml 117 | .pdm-python 118 | .pdm-build/ 119 | 120 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 121 | __pypackages__/ 122 | 123 | # Celery stuff 124 | celerybeat-schedule 125 | celerybeat.pid 126 | 127 | # SageMath parsed files 128 | *.sage.py 129 | 130 | # Environments 131 | .env 132 | .venv 133 | env/ 134 | venv/ 135 | ENV/ 136 | env.bak/ 137 | venv.bak/ 138 | 139 | # Spyder project settings 140 | .spyderproject 141 | .spyproject 142 | 143 | # Rope project settings 144 | .ropeproject 145 | 146 | # mypy 147 | .mypy_cache/ 148 | .dmypy.json 149 | dmypy.json 150 | 151 | # Pyre type checker 152 | .pyre/ 153 | 154 | # pytype static type analyzer 155 | .pytype/ 156 | 157 | # Cython debug symbols 158 | cython_debug/ 159 | 160 | # PyCharm 161 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 162 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 163 | # and can be added to the global gitignore or merged into this file. For a more nuclear 164 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 165 | #.idea/ 166 | 167 | # Node 168 | # Logs 169 | logs 170 | *.log 171 | npm-debug.log* 172 | yarn-debug.log* 173 | yarn-error.log* 174 | lerna-debug.log* 175 | .pnpm-debug.log* 176 | 177 | # Diagnostic reports (https://nodejs.org/api/report.html) 178 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 179 | 180 | # Runtime data 181 | pids 182 | *.pid 183 | *.seed 184 | *.pid.lock 185 | 186 | # Directory for instrumented libs generated by jscoverage/JSCover 187 | lib-cov 188 | 189 | # Coverage directory used by tools like istanbul 190 | coverage 191 | *.lcov 192 | 193 | # nyc test coverage 194 | .nyc_output 195 | 196 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 197 | .grunt 198 | 199 | # Bower dependency directory (https://bower.io/) 200 | bower_components 201 | 202 | # node-waf configuration 203 | .lock-wscript 204 | 205 | # Compiled binary addons (https://nodejs.org/api/addons.html) 206 | build/Release 207 | 208 | # Dependency directories 209 | node_modules/ 210 | jspm_packages/ 211 | 212 | # Snowpack dependency directory (https://snowpack.dev/) 213 | web_modules/ 214 | 215 | # TypeScript cache 216 | *.tsbuildinfo 217 | 218 | # Optional npm cache directory 219 | .npm 220 | 221 | # Optional eslint cache 222 | .eslintcache 223 | 224 | # Optional stylelint cache 225 | .stylelintcache 226 | 227 | # Microbundle cache 228 | .rpt2_cache/ 229 | .rts2_cache_cjs/ 230 | .rts2_cache_es/ 231 | .rts2_cache_umd/ 232 | 233 | # Optional REPL history 234 | .node_repl_history 235 | 236 | # Output of 'npm pack' 237 | *.tgz 238 | 239 | # Yarn Integrity file 240 | .yarn-integrity 241 | 242 | # dotenv environment variable files 243 | .env 244 | .env.development.local 245 | .env.test.local 246 | .env.production.local 247 | .env.local 248 | 249 | # parcel-bundler cache (https://parceljs.org/) 250 | .cache 251 | .parcel-cache 252 | 253 | # Next.js build output 254 | .next 255 | out 256 | 257 | # Nuxt.js build / generate output 258 | .nuxt 259 | dist 260 | 261 | # Gatsby files 262 | .cache/ 263 | # Comment in the public line in if your project uses Gatsby and not Next.js 264 | # https://nextjs.org/blog/next-9-1#public-directory-support 265 | # public 266 | 267 | # vuepress build output 268 | .vuepress/dist 269 | 270 | # vuepress v2.x temp and cache directory 271 | .temp 272 | .cache 273 | 274 | # Docusaurus cache and generated files 275 | .docusaurus 276 | 277 | # Serverless directories 278 | .serverless/ 279 | 280 | # FuseBox cache 281 | .fusebox/ 282 | 283 | # DynamoDB Local files 284 | .dynamodb/ 285 | 286 | # TernJS port file 287 | .tern-port 288 | 289 | # Stores VSCode versions used for testing VSCode extensions 290 | .vscode-test 291 | 292 | # yarn v2 293 | .yarn/cache 294 | .yarn/unplugged 295 | .yarn/build-state.yml 296 | .yarn/install-state.gz 297 | .pnp.* 298 | 299 | # Vite Press 300 | site/docs/.vitepress/dist 301 | site/docs/.vitepress/cache 302 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | 4 | All notable changes to this project will be documented in this file. 5 | 6 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 7 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 8 | 9 | # 2.0.1 10 | 11 | ### Fixed 12 | 13 | - Update query for toggling workspaces and todos (https://github.com/dooit-org/dooit/issues/237) 14 | 15 | # 0.2.0 16 | 17 | ### Fixed 18 | 19 | - All import and variable name changes as per dooit's [`v3.0.4` release](https://github.com/dooit-org/dooit/releases/tag/v3.0.4) 20 | 21 | # 0.1.0 22 | 23 | First Release. Check out the [docs](https://dooit-org.github.io/dooit-extras/)! 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Dooit 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 | # Dooit Extras 🪄 2 | 3 | A collection of utilities to customize your [dooit](https://github.com/dooit-org/dooit) 4 | 5 | [![Wiki](https://img.shields.io/badge/Wiki-Dooit%20Extras-blue?style=flat-square)](https://dooit-org.github.io/dooit-extras/) 6 | [![GitHub issues](https://img.shields.io/github/issues/dooit-org/dooit-extras?color=red&style=flat-square)](https://github.com/dooit-org/doit-extras/issues) 7 | [![GitHub stars](https://img.shields.io/github/stars/dooit-org/dooit-extras?color=green&style=flat-square)](https://github.com/dooit-org/doit/stargazers) 8 | [![GitHub license](https://img.shields.io/github/license/dooit-org/dooit-extras?color=yellow&style=flat-square)](https://github.com/dooit-org/doit/blob/main/LICENSE) 9 | [![Support Server](https://img.shields.io/discord/989186205025464390.svg?label=Discord&logo=Discord&colorB=7289da&style=flat-square)](https://discord.gg/WA2ER9MBWa) 10 | 11 | 12 | -------------- 13 | 14 | For installation and configuration, check out [the wiki](https://dooit-org.github.io/dooit-extras/) 15 | 16 | ## Screenshots 17 | 18 | Some screenshots of app configs done with dooit-extras! 19 | 20 |
21 | An icy configuration based on NordTheme ❄️ 22 | 23 | ![Preview 1](./imgs/config1.png) 24 | 25 |
26 | 27 |
28 | A colorful configuration based on Catpuccin 🐱 29 | 30 | ![Preview 2](./imgs/config2.png) 31 | 32 |
33 | 34 |
35 | A calm configuration based on Everforest 🌲 36 | 37 | ![Preview 3](./imgs/config3.png) 38 | 39 |
40 | -------------------------------------------------------------------------------- /dooit_extras/bar_widgets/__init__.py: -------------------------------------------------------------------------------- 1 | from .mode import Mode 2 | from .workspace_progress import WorkspaceProgress 3 | from .spacer import Spacer 4 | from .clock import Clock 5 | from .date import Date 6 | from .current_workspace import CurrentWorkspace 7 | from .text_box import TextBox 8 | from .ticker import Ticker 9 | from .text_poller import Custom 10 | from .powerline import Powerline 11 | from .platform import Platform 12 | from .status_icons import StatusIcons 13 | 14 | 15 | __all__ = [ 16 | "Mode", 17 | "WorkspaceProgress", 18 | "Spacer", 19 | "Clock", 20 | "Date", 21 | "CurrentWorkspace", 22 | "TextBox", 23 | "Ticker", 24 | "Custom", 25 | "Powerline", 26 | "Platform", 27 | "StatusIcons", 28 | ] 29 | -------------------------------------------------------------------------------- /dooit_extras/bar_widgets/_base.py: -------------------------------------------------------------------------------- 1 | from typing import Callable, Optional 2 | from dooit.ui.api import DooitAPI 3 | from dooit.api.theme import DooitThemeBase 4 | from rich.style import Style, StyleType 5 | from rich.text import Text, TextType 6 | from dooit.ui.widgets.bars import StatusBarWidget 7 | 8 | 9 | class BarUtilWidgetBase(StatusBarWidget): 10 | """ 11 | Base Widget for all Bar Utils Widgets 12 | """ 13 | 14 | def __init__( 15 | self, 16 | func: Callable, 17 | width: Optional[int], 18 | api: DooitAPI, 19 | fmt: TextType = "{}", 20 | fg: str = "", 21 | bg: str = "", 22 | ) -> None: 23 | super().__init__(func, width) 24 | self.api = api 25 | self.fmt = fmt 26 | self.fg = fg 27 | self.bg = bg 28 | 29 | @property 30 | def theme(self) -> DooitThemeBase: 31 | return self.api.vars.theme 32 | 33 | def rich_value(self) -> Text: 34 | return Text.from_markup(self.value) 35 | 36 | @property 37 | def value(self) -> str: 38 | if isinstance(self.fmt, Text): 39 | self.fmt = self.fmt.markup 40 | 41 | return self.fmt.format(super().value) 42 | 43 | def render_text( 44 | self, value: Optional[TextType] = None, style: StyleType = "" 45 | ) -> Text: 46 | if not value: 47 | value = self.rich_value() 48 | 49 | if isinstance(value, str): 50 | value = Text.from_markup(value) 51 | 52 | value.stylize(style) 53 | 54 | # Because the stylize method above will ignore other embedded styles 55 | return Text.from_markup(value.markup) 56 | 57 | def render(self) -> Text: 58 | fg = self.fg or self.api.vars.theme.background1 59 | bg = self.bg or self.api.vars.theme.primary 60 | style = Style(color=fg, bgcolor=bg) 61 | 62 | return self.render_text(style=style) 63 | -------------------------------------------------------------------------------- /dooit_extras/bar_widgets/clock.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | from dooit.ui.api import DooitAPI, timer 3 | from .text_poller import Custom 4 | 5 | 6 | def get_clock_wrapper(format: str): 7 | @timer(1) 8 | def get_clock(*_) -> str: 9 | return datetime.now().strftime(format) 10 | 11 | return get_clock 12 | 13 | 14 | class Clock(Custom): 15 | def __init__( 16 | self, 17 | api: DooitAPI, 18 | fmt: str = " {} ", 19 | format: str = "%H:%M:%S", 20 | fg: str = "", 21 | bg: str = "", 22 | ) -> None: 23 | super().__init__( 24 | api=api, 25 | function=get_clock_wrapper(format), 26 | width=None, 27 | fmt=fmt, 28 | fg=fg, 29 | bg=bg, 30 | ) 31 | -------------------------------------------------------------------------------- /dooit_extras/bar_widgets/current_workspace.py: -------------------------------------------------------------------------------- 1 | from typing import Union 2 | from dooit.ui.api import DooitAPI, subscribe 3 | from dooit.ui.api.events import Startup, WorkspaceSelected 4 | from .text_poller import Custom 5 | 6 | 7 | def get_workspace_name_wrapper(no_workspace_text: str): 8 | @subscribe(WorkspaceSelected, Startup) 9 | def get_workspace_name( 10 | _: DooitAPI, event: Union[WorkspaceSelected, Startup] 11 | ) -> str: 12 | if isinstance(event, Startup): 13 | return no_workspace_text 14 | 15 | text = event.workspace.description 16 | parent = event.workspace.parent_workspace 17 | 18 | if parent and not parent.is_root: 19 | text = f"{parent.description}/{text}" 20 | 21 | return text 22 | 23 | return get_workspace_name 24 | 25 | 26 | class CurrentWorkspace(Custom): 27 | def __init__( 28 | self, 29 | api: DooitAPI, 30 | no_workspace_text="No Workspace Selected", 31 | fmt=" {} ", 32 | fg: str = "", 33 | bg: str = "", 34 | ) -> None: 35 | super().__init__( 36 | api=api, 37 | function=get_workspace_name_wrapper(no_workspace_text), 38 | width=None, 39 | fmt=fmt, 40 | ) 41 | 42 | self.fg = fg 43 | self.bg = bg 44 | -------------------------------------------------------------------------------- /dooit_extras/bar_widgets/date.py: -------------------------------------------------------------------------------- 1 | from dooit.ui.api import DooitAPI 2 | from .clock import Clock 3 | 4 | 5 | class Date(Clock): 6 | def __init__( 7 | self, 8 | api: DooitAPI, 9 | fmt: str = " {} ", 10 | format: str = "%b %d", 11 | fg: str = "", 12 | bg: str = "", 13 | ) -> None: 14 | super().__init__(api=api, format=format, fmt=fmt, fg=fg, bg=bg) 15 | -------------------------------------------------------------------------------- /dooit_extras/bar_widgets/mode.py: -------------------------------------------------------------------------------- 1 | from rich.text import Text, TextType 2 | from dooit.ui.api import DooitAPI, subscribe 3 | from dooit.ui.api.events import ModeChanged 4 | from .text_poller import Custom 5 | 6 | 7 | def get_mode_wrapper( 8 | format_normal: TextType, 9 | format_insert: TextType, 10 | ): 11 | @subscribe(ModeChanged) 12 | def wrapper(_: DooitAPI, event: ModeChanged): 13 | mode = event.mode 14 | 15 | if mode == "NORMAL": 16 | text = format_normal 17 | elif mode == "INSERT": 18 | text = format_insert 19 | else: 20 | text = mode 21 | 22 | if isinstance(text, Text): 23 | text = text.markup 24 | 25 | return text 26 | 27 | return wrapper 28 | 29 | 30 | class Mode(Custom): 31 | """ 32 | Mode Bar Widget to show mode 33 | """ 34 | 35 | def __init__( 36 | self, 37 | api: DooitAPI, 38 | format_normal: TextType = " NORMAL ", 39 | format_insert: TextType = " INSERT ", 40 | fmt="{}", 41 | fg: str = "", 42 | bg: str = "", 43 | ) -> None: 44 | super().__init__( 45 | api=api, 46 | function=get_mode_wrapper(format_normal, format_insert), 47 | width=None, 48 | fmt=fmt, 49 | fg=fg, 50 | bg=bg, 51 | ) 52 | -------------------------------------------------------------------------------- /dooit_extras/bar_widgets/platform.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | from dooit.ui.api import DooitAPI 3 | from dooit.api.theme import DooitThemeBase 4 | from rich.text import Text, TextType 5 | from rich.style import Style 6 | from .text_box import TextBox 7 | 8 | import platform 9 | import os 10 | 11 | 12 | class PlatformInfo: 13 | def __init__(self, icon: str, color: str) -> None: 14 | self.icon = icon 15 | self.color = color 16 | 17 | @classmethod 18 | def default(cls) -> "PlatformInfo": 19 | return cls("", "red") 20 | 21 | 22 | ICONS: defaultdict[str, PlatformInfo] = defaultdict(PlatformInfo.default) 23 | 24 | 25 | def get_user_platform(theme: DooitThemeBase, icon: bool) -> TextType: 26 | default_icons = { 27 | "Windows": PlatformInfo("", theme.blue), 28 | "macOS": PlatformInfo("", theme.foreground3), 29 | "Linux": PlatformInfo("", theme.yellow), 30 | "NixOS": PlatformInfo("", theme.cyan), 31 | "Arch Linux": PlatformInfo("", theme.blue), 32 | "Ubuntu": PlatformInfo("", theme.orange), 33 | } 34 | 35 | ICONS.update(default_icons) 36 | 37 | system = platform.system() 38 | if system == "Linux": 39 | try: 40 | os_release = platform.freedesktop_os_release() 41 | system = os_release.get("NAME", "Linux") 42 | except AttributeError: 43 | if os.path.isfile("/etc/os-release"): 44 | with open("/etc/os-release") as f: 45 | for line in f: 46 | if line.startswith("NAME="): 47 | system = line.strip().split("=")[1].strip('"') 48 | system = "Linux" 49 | else: 50 | system = "Unknown" 51 | 52 | text = Text() 53 | if icon: 54 | icon_info = ICONS[system] 55 | text += Text( 56 | icon_info.icon, 57 | style=Style(color=icon_info.color), 58 | ) 59 | 60 | text.append(" " + system) 61 | return text 62 | 63 | 64 | class Platform(TextBox): 65 | def __init__( 66 | self, 67 | api: DooitAPI, 68 | icon: bool = True, 69 | fmt: str = " {} ", 70 | fg: str = "", 71 | bg: str = "", 72 | ) -> None: 73 | super().__init__( 74 | api, 75 | get_user_platform(api.vars.theme, icon), 76 | fmt, 77 | fg, 78 | bg, 79 | ) 80 | -------------------------------------------------------------------------------- /dooit_extras/bar_widgets/powerline.py: -------------------------------------------------------------------------------- 1 | from dooit.ui.api import DooitAPI 2 | from dooit.ui.tui import Dooit 3 | from .text_box import TextBox 4 | from typing_extensions import Self 5 | 6 | 7 | class Powerline(TextBox): 8 | def __init__( 9 | self, 10 | api: DooitAPI = DooitAPI(Dooit()), 11 | text: str = "", 12 | fmt: str = "{}", 13 | fg: str = "", 14 | bg: str = "", 15 | ) -> None: 16 | bg = bg or api.vars.theme.background2 17 | super().__init__(api, text, fmt, fg, bg) 18 | 19 | # Lower Triangles 20 | @classmethod 21 | def lower_left_triangle(cls, api: DooitAPI, fg: str = "", bg: str = "") -> Self: 22 | return cls(api, "", fg=fg, bg=bg) 23 | 24 | @classmethod 25 | def lower_right_triangle(cls, api: DooitAPI, fg: str = "", bg: str = "") -> Self: 26 | return cls(api, "", fg=fg, bg=bg) 27 | 28 | # Upper Triangles 29 | @classmethod 30 | def upper_left_triangle(cls, api: DooitAPI, fg: str = "", bg: str = "") -> Self: 31 | return cls(api, "", fg=fg, bg=bg) 32 | 33 | @classmethod 34 | def upper_right_triangle(cls, api: DooitAPI, fg: str = "", bg: str = "") -> Self: 35 | return cls(api, "", fg=fg, bg=bg) 36 | 37 | # Arrows 38 | @classmethod 39 | def right_arrow(cls, api: DooitAPI, fg: str = "", bg: str = "") -> Self: 40 | return cls(api, "", fg=fg, bg=bg) 41 | 42 | @classmethod 43 | def left_arrow(cls, api: DooitAPI, fg: str = "", bg: str = "") -> Self: 44 | return cls(api, "", fg=fg, bg=bg) 45 | 46 | # Rounded 47 | @classmethod 48 | def right_rounded(cls, api: DooitAPI, fg: str = "", bg: str = "") -> Self: 49 | return cls(api, "", fg=fg, bg=bg) 50 | 51 | @classmethod 52 | def left_rounded(cls, api: DooitAPI, fg: str = "", bg: str = "") -> Self: 53 | return cls(api, "", fg=fg, bg=bg) 54 | 55 | # Ice 56 | @classmethod 57 | def left_ice(cls, api: DooitAPI, fg: str = "", bg: str = "") -> Self: 58 | return cls(api, " ", fg=fg, bg=bg) 59 | 60 | @classmethod 61 | def right_ice(cls, api: DooitAPI, fg: str = "", bg: str = "") -> Self: 62 | return cls(api, "", fg=fg, bg=bg) 63 | 64 | # Fire 65 | @classmethod 66 | def left_flame(cls, api: DooitAPI, fg: str = "", bg: str = "") -> Self: 67 | return cls(api, " ", fg=fg, bg=bg) 68 | 69 | @classmethod 70 | def right_flame(cls, api: DooitAPI, fg: str = "", bg: str = "") -> Self: 71 | return cls(api, " ", fg=fg, bg=bg) 72 | -------------------------------------------------------------------------------- /dooit_extras/bar_widgets/spacer.py: -------------------------------------------------------------------------------- 1 | from dooit.ui.api import DooitAPI 2 | from ._base import BarUtilWidgetBase 3 | 4 | 5 | class Spacer(BarUtilWidgetBase): 6 | def __init__(self, api: DooitAPI, width: int, fg: str = "", bg: str = "") -> None: 7 | super().__init__(func=lambda *_: "", width=width, api=api, fg=fg, bg=bg) 8 | -------------------------------------------------------------------------------- /dooit_extras/bar_widgets/status_icons.py: -------------------------------------------------------------------------------- 1 | from rich.text import Text 2 | from dooit.ui.api import DooitAPI, subscribe 3 | from dooit.ui.api.events import TodoEvent, WorkspaceSelected 4 | from .text_poller import Custom 5 | 6 | 7 | def get_status_icons(completed_icon, pending_icon, overdue_icon): 8 | @subscribe(TodoEvent, WorkspaceSelected) 9 | def wrapper(api: DooitAPI, _): 10 | workspace = api.vars.current_workspace 11 | if not workspace: 12 | return "" 13 | 14 | theme = api.vars.theme 15 | 16 | completed = sum([i.is_completed for i in workspace.todos]) 17 | pending = sum([i.is_pending for i in workspace.todos]) 18 | overdue = sum([i.is_overdue for i in workspace.todos]) 19 | 20 | return ( 21 | Text() 22 | + Text.from_markup(completed_icon + str(completed), style=theme.green) 23 | + Text(" ") 24 | + Text.from_markup(pending_icon + str(pending), style=theme.yellow) 25 | + Text(" ") 26 | + Text.from_markup(overdue_icon + str(overdue), style=theme.red) 27 | ) 28 | 29 | return wrapper 30 | 31 | 32 | class StatusIcons(Custom): 33 | def __init__( 34 | self, 35 | api: DooitAPI, 36 | completed_icon=" ", 37 | pending_icon=" ", 38 | overdue_icon=" ", 39 | fmt: str = " {} ", 40 | bg: str = "", 41 | ) -> None: 42 | super().__init__( 43 | api, 44 | get_status_icons(completed_icon, pending_icon, overdue_icon), 45 | None, 46 | fmt, 47 | "", 48 | bg or api.vars.theme.background3, 49 | ) 50 | -------------------------------------------------------------------------------- /dooit_extras/bar_widgets/text_box.py: -------------------------------------------------------------------------------- 1 | from dooit.ui.api import DooitAPI, subscribe, events 2 | from rich.text import TextType 3 | from ._base import BarUtilWidgetBase 4 | 5 | 6 | def text_wrapper(text: TextType): 7 | @subscribe(events.Startup) 8 | def wrapper(*_): 9 | return text 10 | 11 | return wrapper 12 | 13 | 14 | class TextBox(BarUtilWidgetBase): 15 | def __init__( 16 | self, 17 | api: DooitAPI, 18 | text: TextType, 19 | fmt: str = "{}", 20 | fg: str = "", 21 | bg: str = "", 22 | ) -> None: 23 | super().__init__( 24 | func=text_wrapper(text), width=None, api=api, fmt=fmt, fg=fg, bg=bg 25 | ) 26 | -------------------------------------------------------------------------------- /dooit_extras/bar_widgets/text_poller.py: -------------------------------------------------------------------------------- 1 | from typing import Callable, Optional 2 | from dooit.ui.api import DooitAPI 3 | from ._base import BarUtilWidgetBase 4 | 5 | 6 | class Custom(BarUtilWidgetBase): 7 | def __init__( 8 | self, 9 | api: DooitAPI, 10 | function: Callable, 11 | width: Optional[int] = None, 12 | fmt: str = " {} ", 13 | fg: str = "", 14 | bg: str = "", 15 | ) -> None: 16 | super().__init__(func=function, width=width, api=api, fmt=fmt, fg=fg, bg=bg) 17 | -------------------------------------------------------------------------------- /dooit_extras/bar_widgets/ticker.py: -------------------------------------------------------------------------------- 1 | from time import time 2 | from dooit.ui.api import DooitAPI, timer 3 | from .text_poller import Custom 4 | 5 | 6 | class Counter: 7 | def __init__(self): 8 | self._start_time = None 9 | self._elapsed_time = 0 10 | self._running = False 11 | self._start_flag = False 12 | 13 | def start(self): 14 | """Starts the counter""" 15 | self._start_flag = True 16 | if not self._running: 17 | self._start_time = time() 18 | self._running = True 19 | 20 | def stop(self): 21 | """Stops the counter. Reset when already stopped""" 22 | if self._running and self._start_time: 23 | self._elapsed_time += time() - self._start_time 24 | self._start_time = None 25 | self._running = False 26 | else: 27 | self.reset() 28 | 29 | def is_paused(self): 30 | """Checks if the counter is currently paused.""" 31 | return not self._running and self._start_flag 32 | 33 | def reset(self): 34 | self._start_time = None 35 | self._elapsed_time = 0 36 | self._running = False 37 | self._start_flag = False 38 | 39 | def current_count(self) -> float: 40 | if self._running and self._start_time: 41 | return self._elapsed_time + (time() - self._start_time) 42 | return self._elapsed_time 43 | 44 | def format_hms(self): 45 | """Converts the current count to h m s format.""" 46 | 47 | total_seconds = int(self.current_count()) 48 | hours, remainder = divmod(total_seconds, 3600) 49 | minutes, seconds = divmod(remainder, 60) 50 | 51 | parts = [] 52 | if hours > 0: 53 | parts.append(f"{hours}h") 54 | if minutes > 0: 55 | parts.append(f"{minutes}m") 56 | parts.append(f"{seconds}s") 57 | 58 | return " ".join(parts) 59 | 60 | 61 | def get_ticker_wrapper(counter: Counter, paused_text: str, default_text: str): 62 | @timer(0.2) 63 | def get_ticker(*_) -> str: 64 | if counter._start_flag: 65 | if counter.is_paused(): 66 | return paused_text 67 | else: 68 | return counter.format_hms() 69 | else: 70 | return default_text 71 | 72 | return get_ticker 73 | 74 | 75 | class Ticker(Custom): 76 | def __init__( 77 | self, 78 | api: DooitAPI, 79 | resume_key: str = "s", 80 | stop_key: str = "S", 81 | paused_text: str = "Paused", 82 | default_text: str = "No Timers", 83 | fmt: str = " {} ", 84 | fg: str = "", 85 | bg: str = "", 86 | ) -> None: 87 | self.counter = Counter() 88 | 89 | super().__init__( 90 | api=api, 91 | function=get_ticker_wrapper(self.counter, paused_text, default_text), 92 | width=None, 93 | fmt=fmt, 94 | fg=fg, 95 | bg=bg, 96 | ) 97 | 98 | # set keybinds 99 | self.api.keys.set(resume_key, self.counter.start) 100 | self.api.keys.set(stop_key, self.counter.stop) 101 | -------------------------------------------------------------------------------- /dooit_extras/bar_widgets/workspace_progress.py: -------------------------------------------------------------------------------- 1 | from typing import Union 2 | from dooit.ui.api import DooitAPI, subscribe 3 | from dooit.ui.api.events import WorkspaceSelected, TodoEvent 4 | from dooit.api import Workspace 5 | from .text_poller import Custom 6 | 7 | 8 | def get_completed(workspace: Workspace): 9 | """ 10 | Get the percentage of completed todos in the workspace 11 | """ 12 | 13 | todos = workspace.todos 14 | 15 | if not todos: 16 | return 0 17 | 18 | complete_count = sum(t.is_completed for t in todos) 19 | total_count = len(todos) 20 | 21 | return int(100 * complete_count / total_count) 22 | 23 | 24 | @subscribe(WorkspaceSelected, TodoEvent) 25 | def get_workspace_completion( 26 | api: DooitAPI, 27 | event: Union[WorkspaceSelected, TodoEvent], 28 | ) -> str: 29 | if isinstance(event, WorkspaceSelected): 30 | workspace = event.workspace 31 | elif isinstance(event, TodoEvent): 32 | workspace = api.app.workspace_tree.current_model 33 | 34 | return str(get_completed(workspace)) 35 | 36 | 37 | class WorkspaceProgress(Custom): 38 | def __init__(self, api: DooitAPI, fmt="{}", fg: str = "", bg: str = "") -> None: 39 | super().__init__( 40 | api=api, 41 | function=get_workspace_completion, 42 | width=None, 43 | fmt=fmt, 44 | fg=fg, 45 | bg=bg, 46 | ) 47 | -------------------------------------------------------------------------------- /dooit_extras/formatters/__init__.py: -------------------------------------------------------------------------------- 1 | from .description import ( 2 | description_highlight_link, 3 | description_children_count, 4 | description_strike_completed, 5 | description_highlight_tags, 6 | todo_description_progress, 7 | ) 8 | from .due import due_danger_today, due_casual_format, due_icon 9 | from .status import status_icons 10 | from .recurrence import recurrence_icon 11 | from .effort import effort_icon 12 | from .urgency import urgency_icons 13 | 14 | __all__ = [ 15 | "todo_description_progress", 16 | "description_highlight_link", 17 | "description_children_count", 18 | "description_strike_completed", 19 | "description_highlight_tags", 20 | "due_danger_today", 21 | "due_casual_format", 22 | "due_icon", 23 | "status_icons", 24 | "recurrence_icon", 25 | "effort_icon", 26 | "urgency_icons", 27 | ] 28 | -------------------------------------------------------------------------------- /dooit_extras/formatters/description.py: -------------------------------------------------------------------------------- 1 | from typing import Union, Optional 2 | from dooit.api import Todo, Workspace 3 | from rich.style import Style, StyleType 4 | from dooit.ui.api import DooitAPI 5 | from rich.text import Text 6 | from dooit.ui.api import extra_formatter 7 | import re 8 | 9 | ModelType = Union[Todo, Workspace] 10 | 11 | 12 | def description_highlight_link(color: Optional[str] = None): 13 | @extra_formatter 14 | def wrapper(value: str, _, api: DooitAPI): 15 | """ 16 | Highlight URLs in the description. 17 | """ 18 | 19 | url_pattern = re.compile( 20 | r"http[s]?://" 21 | r"(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\\(\\),]|" 22 | r"(?:%[0-9a-fA-F][0-9a-fA-F]))+", 23 | re.IGNORECASE, 24 | ) 25 | 26 | text = Text.from_markup(value) 27 | text.highlight_regex( 28 | url_pattern, 29 | style=Style( 30 | color=color or api.vars.theme.primary, 31 | underline=True, 32 | italic=True, 33 | ), 34 | ) 35 | 36 | return text.markup 37 | 38 | return wrapper 39 | 40 | 41 | def description_children_count(fmt: str = " ({}) "): 42 | @extra_formatter 43 | def wrapper(value: str, model: ModelType): 44 | """ 45 | Highlight the number of children in the description. 46 | """ 47 | 48 | if isinstance(model, Todo): 49 | children_count = len(model.todos) 50 | else: 51 | children_count = len(model.workspaces) 52 | 53 | if not children_count: 54 | return 55 | 56 | return value + fmt.format(children_count) 57 | 58 | return wrapper 59 | 60 | 61 | def description_strike_completed(dim: bool = True): 62 | @extra_formatter 63 | def wrapper(value: str, todo: Todo): 64 | if todo.is_completed: 65 | return Text.from_markup(value, style=Style(strike=True, dim=dim)).markup 66 | 67 | return wrapper 68 | 69 | 70 | def description_highlight_tags(color: StyleType = "", fmt=" {}"): 71 | @extra_formatter 72 | def wrapper(value: str, _: Todo, api: DooitAPI): 73 | """ 74 | Highlight tags in the description. 75 | """ 76 | 77 | regex = re.compile(r"@\w+") 78 | style = color or api.vars.theme.primary 79 | 80 | for match in re.finditer(regex, value): 81 | start, end = match.span() 82 | formatted_tag = fmt.format(value[start + 1 : end]) # +1 for @ symbol 83 | formatted_tag = Text.from_markup(formatted_tag, style=style).markup 84 | value = value[:start] + formatted_tag + value[end:] 85 | 86 | return value 87 | 88 | return wrapper 89 | 90 | 91 | def todo_description_progress(fmt=" ({completed_percent}%)"): 92 | @extra_formatter 93 | def wrapper(value: str, todo: Todo): 94 | if not todo.todos: 95 | return 96 | 97 | total_count = len(todo.todos) 98 | 99 | completed_count = len([t for t in todo.todos if t.is_completed]) 100 | remaining_count = total_count - completed_count 101 | 102 | completed_percent = int(round((completed_count / total_count) * 100)) 103 | remaining_percent = 100 - completed_percent 104 | 105 | data = dict( 106 | total_count=total_count, 107 | completed_count=completed_count, 108 | remaining_count=remaining_count, 109 | completed_percent=completed_percent, 110 | remaining_percent=remaining_percent, 111 | ) 112 | 113 | return value + fmt.format(**data) 114 | 115 | return wrapper 116 | -------------------------------------------------------------------------------- /dooit_extras/formatters/due.py: -------------------------------------------------------------------------------- 1 | from typing import Callable, Optional 2 | from rich.style import Style 3 | from dooit.api.todo import datetime, Todo 4 | from dooit.ui.api import DooitAPI, extra_formatter 5 | from rich.text import Text 6 | 7 | 8 | def due_casual_format(fmt="{}") -> Callable: 9 | def wrapper(due: Optional[datetime], _: Todo) -> str: 10 | """ 11 | Shows the date in a more simple format: 12 | Example: `23 Oct` instead of `23-10-2024` 13 | """ 14 | 15 | if not due: 16 | return "" 17 | 18 | current_year = datetime.now().year 19 | dt_format = "%b %d" 20 | 21 | if due.year != current_year: 22 | dt_format += " '%y" 23 | 24 | if due.hour != 0 or due.minute != 0: 25 | dt_format += " (%H:%M)" 26 | 27 | return fmt.format(due.strftime(dt_format)) 28 | 29 | return wrapper 30 | 31 | 32 | def due_danger_today(fmt: str = "{}") -> Callable: 33 | def wrapper(due: Optional[datetime], _: Todo, api: DooitAPI) -> Optional[str]: 34 | """ 35 | If the due date is today, show a bold red "Today" text. 36 | """ 37 | 38 | if not due: 39 | return "" 40 | 41 | if due.date() == datetime.today().date(): 42 | return Text( 43 | fmt.format("Today"), 44 | style=Style( 45 | color=api.vars.theme.red, 46 | bold=True, 47 | ), 48 | ).markup 49 | 50 | return wrapper 51 | 52 | 53 | def due_icon(completed: str = "󰃯 ", pending: str = "󰃰 ", overdue: str = " "): 54 | @extra_formatter 55 | def wrapper(due: str, model: Todo, api: DooitAPI): 56 | theme = api.vars.theme 57 | if not model.due: 58 | return due 59 | 60 | if model.is_completed: 61 | icon = completed 62 | color = theme.green 63 | 64 | elif model.is_overdue: 65 | icon = overdue 66 | color = theme.red 67 | else: 68 | icon = pending 69 | color = theme.yellow 70 | 71 | return Text() + Text.from_markup(icon, style=Style(color=color)) + due 72 | 73 | return wrapper 74 | -------------------------------------------------------------------------------- /dooit_extras/formatters/effort.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | from rich.style import Style 3 | from rich.text import Text 4 | from dooit.api import Todo 5 | from dooit.ui.api import DooitAPI, extra_formatter 6 | 7 | 8 | def effort_icon( 9 | icon: str = "󰈸 ", 10 | color: Optional[str] = None, 11 | show_on_zero: bool = True, 12 | ): 13 | @extra_formatter 14 | def wrapper(value: str, model: Todo, api: DooitAPI): 15 | theme = api.vars.theme 16 | 17 | if not show_on_zero and model.effort == 0: 18 | return "" 19 | 20 | return ( 21 | Text() 22 | + Text.from_markup(icon, style=Style(color=color or theme.orange)) 23 | + value 24 | ) 25 | 26 | return wrapper 27 | -------------------------------------------------------------------------------- /dooit_extras/formatters/recurrence.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | from rich.style import Style 3 | from rich.text import Text 4 | from dooit.api import Todo 5 | from dooit.ui.api import DooitAPI, extra_formatter 6 | 7 | 8 | def recurrence_icon(icon: str = "󰑖 ", color: Optional[str] = None): 9 | @extra_formatter 10 | def wrapper(value: str, model: Todo, api: DooitAPI): 11 | theme = api.vars.theme 12 | 13 | if not model.recurrence: 14 | return value 15 | 16 | return ( 17 | Text() 18 | + Text.from_markup(icon, style=Style(color=color or theme.primary)) 19 | + value 20 | ) 21 | 22 | return wrapper 23 | -------------------------------------------------------------------------------- /dooit_extras/formatters/status.py: -------------------------------------------------------------------------------- 1 | from typing import Callable 2 | from rich.text import Text 3 | from dooit.api import Todo 4 | from dooit.ui.api import DooitAPI 5 | 6 | 7 | def status_icons(completed="x", pending="o", overdue="!", fmt="{}") -> Callable: 8 | """ 9 | Shows status icons for todos 10 | """ 11 | 12 | def wrapper(status: str, _: Todo, api: DooitAPI) -> Text: 13 | """ 14 | Shows icon for various statuses 15 | """ 16 | 17 | theme = api.vars.theme 18 | if status == "completed": 19 | icon = completed 20 | style = theme.green 21 | elif status == "pending": 22 | icon = pending 23 | style = theme.yellow 24 | else: 25 | icon = overdue 26 | style = theme.red 27 | 28 | return Text.from_markup(fmt.format(icon), style=style) 29 | 30 | return wrapper 31 | -------------------------------------------------------------------------------- /dooit_extras/formatters/urgency.py: -------------------------------------------------------------------------------- 1 | from typing import Callable, Dict 2 | from rich.style import StyleType, Style 3 | from rich.text import Text 4 | from dooit.api import Todo 5 | from dooit.ui.api import DooitAPI 6 | 7 | 8 | def urgency_icons( 9 | icons: Dict[int, str] = {}, colors: Dict[int, StyleType] = {}, fmt="{}" 10 | ) -> Callable: 11 | """ 12 | Shows urgency icons for todos 13 | """ 14 | 15 | def wrapper(urgency: int, todo: Todo, api: DooitAPI) -> Text: 16 | """ 17 | Shows icon for various urgency levels (1-4) 18 | """ 19 | 20 | theme = api.vars.theme 21 | default_styles = { 22 | 1: Style(color=theme.green, bold=True), 23 | 2: Style(color=theme.yellow, bold=True), 24 | 3: Style(color=theme.orange, bold=True), 25 | 4: Style(color=theme.red, bold=True), 26 | } 27 | 28 | default_icons = { 29 | 1: "󰲠", # Low urgency 30 | 2: "󰲢", # Medium urgency 31 | 3: "󰲤", # High urgency 32 | 4: "󰲦", # Critical urgency 33 | } 34 | 35 | default_styles.update(colors) 36 | default_icons.update(icons) 37 | 38 | icon = default_icons[urgency] 39 | style = default_styles[urgency] 40 | 41 | return Text.from_markup(fmt.format(icon), style=style) 42 | 43 | return wrapper 44 | -------------------------------------------------------------------------------- /dooit_extras/scripts/__init__.py: -------------------------------------------------------------------------------- 1 | from .toggle_workspaces import toggle_workspaces 2 | from .custom_tree_borders import custom_tree_borders 3 | from .dim_unfocused import dim_unfocused 4 | 5 | __all__ = [ 6 | "toggle_workspaces", 7 | "custom_tree_borders", 8 | "dim_unfocused", 9 | ] 10 | -------------------------------------------------------------------------------- /dooit_extras/scripts/custom_tree_borders.py: -------------------------------------------------------------------------------- 1 | from typing import Literal 2 | from dooit.ui.api import DooitAPI 3 | 4 | BorderKind = Literal[ 5 | "ascii", 6 | "blank", 7 | "dashed", 8 | "double", 9 | "heavy", 10 | "hidden" "hkey", 11 | "inner", 12 | "outer", 13 | "panel", 14 | "round", 15 | "solid", 16 | "tall", 17 | "thick", 18 | "vkey", 19 | "wide", 20 | ] 21 | 22 | 23 | def custom_tree_borders( 24 | api: DooitAPI, 25 | focus_border: BorderKind, 26 | dim_border: BorderKind, 27 | ): 28 | """Customize tree borders""" 29 | CSS = f"""\ 30 | ModelTree {{ 31 | border: {dim_border} $background3; 32 | 33 | &:focus {{ 34 | border: {focus_border} $primary; 35 | }} 36 | }} 37 | """ 38 | 39 | api.css.inject_css(CSS) 40 | api.app.refresh_css() # quick refresh 41 | -------------------------------------------------------------------------------- /dooit_extras/scripts/dim_unfocused.py: -------------------------------------------------------------------------------- 1 | from dooit.ui.api import DooitAPI 2 | 3 | 4 | def dim_unfocused(api: DooitAPI, opacity: str = "50%"): 5 | """Dimmify unfocused tree""" 6 | 7 | CSS = f"""\ 8 | ModelTree {{ 9 | opacity: {opacity}; 10 | 11 | &:focus {{ 12 | opacity: 1; 13 | }} 14 | }} 15 | """ 16 | 17 | api.css.inject_css(CSS) 18 | api.app.refresh_css() # quick refresh 19 | -------------------------------------------------------------------------------- /dooit_extras/scripts/toggle_workspaces.py: -------------------------------------------------------------------------------- 1 | from dooit.ui.api import DooitAPI 2 | 3 | 4 | def toggle_workspaces(api: DooitAPI): 5 | def wrapper(): 6 | """Toggles the visibility of the workspaces tree""" 7 | workspace_switcher = api.app.screen.query_one("#workspace_switcher") 8 | todo_switcher = api.app.screen.query_one("#todo_switcher") 9 | 10 | if workspace_switcher.display: 11 | workspace_switcher.display = False 12 | todo_switcher.styles.column_span = 2 13 | api.switch_focus() 14 | else: 15 | workspace_switcher.display = True 16 | todo_switcher.styles.column_span = 1 17 | api.app.workspace_tree.focus() 18 | 19 | return wrapper 20 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "dooit": { 4 | "inputs": { 5 | "nixpkgs": "nixpkgs" 6 | }, 7 | "locked": { 8 | "lastModified": 1748666860, 9 | "narHash": "sha256-Ux7cqxFcYEpPOfzVTem5IkuDGflh7UmIcdIW6ZPtovM=", 10 | "owner": "dooit-org", 11 | "repo": "dooit", 12 | "rev": "d6e3ac12508b8bb1ec99847f3a2670b709ca54c2", 13 | "type": "github" 14 | }, 15 | "original": { 16 | "owner": "dooit-org", 17 | "repo": "dooit", 18 | "type": "github" 19 | } 20 | }, 21 | "nixpkgs": { 22 | "locked": { 23 | "lastModified": 1746904237, 24 | "narHash": "sha256-3e+AVBczosP5dCLQmMoMEogM57gmZ2qrVSrmq9aResQ=", 25 | "owner": "nixos", 26 | "repo": "nixpkgs", 27 | "rev": "d89fc19e405cb2d55ce7cc114356846a0ee5e956", 28 | "type": "github" 29 | }, 30 | "original": { 31 | "owner": "nixos", 32 | "ref": "nixos-unstable", 33 | "repo": "nixpkgs", 34 | "type": "github" 35 | } 36 | }, 37 | "nixpkgs_2": { 38 | "locked": { 39 | "lastModified": 1748460289, 40 | "narHash": "sha256-7doLyJBzCllvqX4gszYtmZUToxKvMUrg45EUWaUYmBg=", 41 | "owner": "nixos", 42 | "repo": "nixpkgs", 43 | "rev": "96ec055edbe5ee227f28cdbc3f1ddf1df5965102", 44 | "type": "github" 45 | }, 46 | "original": { 47 | "owner": "nixos", 48 | "ref": "nixos-unstable", 49 | "repo": "nixpkgs", 50 | "type": "github" 51 | } 52 | }, 53 | "root": { 54 | "inputs": { 55 | "dooit": "dooit", 56 | "nixpkgs": "nixpkgs_2" 57 | } 58 | } 59 | }, 60 | "root": "root", 61 | "version": 7 62 | } 63 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "Flake for Dooit Extras"; 3 | 4 | inputs = { 5 | nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; 6 | dooit.url = "github:dooit-org/dooit"; 7 | }; 8 | 9 | outputs = { 10 | self, 11 | nixpkgs, 12 | dooit, 13 | ... 14 | }: let 15 | forEachSystem = nixpkgs.lib.genAttrs nixpkgs.lib.platforms.all; 16 | 17 | pkgsFor = forEachSystem ( 18 | system: 19 | import nixpkgs { 20 | inherit system; 21 | } 22 | ); 23 | 24 | packageFor = system: pkgsFor.${system}.callPackage ./nix {dooit = dooit.packages.${system}.default;}; 25 | in { 26 | packages = forEachSystem ( 27 | system: { 28 | default = packageFor system; 29 | } 30 | ); 31 | 32 | overlay = final: prev: { 33 | dooit-extras = packageFor final.system; 34 | }; 35 | }; 36 | } 37 | -------------------------------------------------------------------------------- /imgs/config1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dooit-org/dooit-extras/faab40ab94b1d2ba0a92cb951a6c5ea3926feb8b/imgs/config1.png -------------------------------------------------------------------------------- /imgs/config2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dooit-org/dooit-extras/faab40ab94b1d2ba0a92cb951a6c5ea3926feb8b/imgs/config2.png -------------------------------------------------------------------------------- /imgs/config3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dooit-org/dooit-extras/faab40ab94b1d2ba0a92cb951a6c5ea3926feb8b/imgs/config3.png -------------------------------------------------------------------------------- /imgs/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dooit-org/dooit-extras/faab40ab94b1d2ba0a92cb951a6c5ea3926feb8b/imgs/logo.png -------------------------------------------------------------------------------- /nix/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | lib, 3 | fetchFromGitHub, 4 | python311, 5 | dooit, 6 | }: let 7 | python3 = python311; 8 | version = "0.2.1"; 9 | in 10 | python3.pkgs.buildPythonPackage { 11 | pname = "dooit-extras"; 12 | version = "0.2.1"; 13 | pyproject = true; 14 | 15 | src = fetchFromGitHub { 16 | owner = "dooit-org"; 17 | repo = "dooit-extras"; 18 | rev = "refs/tags/v${version}"; 19 | hash = "sha256-h29lN32Qca8edF1aLhLxnV97MMEapX3Docc+CIEF6I4="; 20 | }; 21 | 22 | build-system = with python3.pkgs; [poetry-core]; 23 | buildInputs = [dooit]; 24 | 25 | # No tests available 26 | doCheck = false; 27 | 28 | meta = with lib; { 29 | description = "Extra Utilities for Dooit"; 30 | homepage = "https://github.com/dooit-org/dooit-extras"; 31 | changelog = "https://github.com/dooit-org/dooit-extras/blob/v${version}/CHANGELOG.md"; 32 | license = licenses.mit; 33 | maintainers = with maintainers; [ 34 | kraanzu 35 | ]; 36 | }; 37 | } 38 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "dooit-extras" 3 | version = "0.2.1" 4 | description = "A collection of utilities to customize your dooit!" 5 | authors = ["kraanzu "] 6 | license = "MIT" 7 | readme = "README.md" 8 | 9 | [tool.poetry.dependencies] 10 | python = "^3.9" 11 | dooit = "^3.2.2" 12 | 13 | [tool.poetry.group.dev.dependencies] 14 | ruff = "^0.7.2" 15 | pre-commit = "^4.0.1" 16 | textual-dev = "^1.6.1" 17 | pytest = "^8.3.3" 18 | coverage = "^7.6.4" 19 | pytest-asyncio = "^0.24.0" 20 | faker = "^30.8.2" 21 | 22 | [build-system] 23 | requires = ["poetry-core"] 24 | build-backend = "poetry.core.masonry.api" 25 | 26 | [tool.ruff.lint] 27 | ignore = ["F405", "F403"] 28 | -------------------------------------------------------------------------------- /site/bun.lockb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dooit-org/dooit-extras/faab40ab94b1d2ba0a92cb951a6c5ea3926feb8b/site/bun.lockb -------------------------------------------------------------------------------- /site/docs/.vitepress/config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vitepress"; 2 | 3 | // https://vitepress.dev/reference/site-config 4 | export default defineConfig({ 5 | title: "Dooit Extras", 6 | description: "A collection of items to customize your dooit!", 7 | base: "/dooit-extras/", 8 | lastUpdated: true, 9 | themeConfig: { 10 | repo: "dooit-org/dooit-extras", 11 | docsDir: "site/docs", 12 | editLink: { 13 | pattern: 14 | "https://github.com/dooit-org/dooit-extras/edit/main/site/docs/:path", 15 | text: "Edit this page on GitHub", 16 | }, 17 | nav: [ 18 | { text: "Home", link: "/" }, 19 | { text: "Get Started", link: "/getting_started/introduction" }, 20 | ], 21 | socialLinks: [ 22 | { icon: "github", link: "https://github.com/dooit-org/dooit-extras" }, 23 | { icon: "discord", link: "https://discord.com/invite/WA2ER9MBWa" }, 24 | { icon: "twitter", link: "https://twitter.com/kraanzu" }, 25 | ], 26 | search: { 27 | provider: "local", 28 | }, 29 | sidebar: [ 30 | { 31 | text: "Getting Started", 32 | items: [ 33 | { text: "Introduction", link: "/getting_started/introduction" }, 34 | { text: "Usage", link: "/getting_started/usage" }, 35 | ], 36 | }, 37 | { 38 | text: "Formatters", 39 | collapsible: true, 40 | collapsed: true, 41 | items: [ 42 | { text: "Description", link: "/formatters/description" }, 43 | { text: "Due", link: "/formatters/due" }, 44 | { text: "Effort", link: "/formatters/effort" }, 45 | { text: "Recurrence", link: "/formatters/recurrence" }, 46 | { text: "Status", link: "/formatters/status" }, 47 | { text: "Urgency", link: "/formatters/urgency" }, 48 | ], 49 | }, 50 | 51 | { 52 | text: "Widgets", 53 | collapsible: true, 54 | collapsed: true, 55 | items: [ 56 | { text: "Clock", link: "/widgets/clock" }, 57 | { text: "Current Workspace", link: "/widgets/current_workspace" }, 58 | { text: "Custom", link: "/widgets/custom" }, 59 | { text: "Date", link: "/widgets/date" }, 60 | { text: "Mode", link: "/widgets/mode" }, 61 | { text: "Platform", link: "/widgets/platform" }, 62 | { text: "Powerline", link: "/widgets/powerline" }, 63 | { text: "Spacer", link: "/widgets/spacer" }, 64 | { text: "Status Icons", link: "/widgets/status_icons" }, 65 | { text: "TextBox", link: "/widgets/text_box" }, 66 | { text: "Ticker", link: "/widgets/ticker" }, 67 | { text: "Workspace Progress", link: "/widgets/workspace_progress" }, 68 | ], 69 | }, 70 | { 71 | text: "Scripts", 72 | collapsible: true, 73 | collapsed: true, 74 | items: [ 75 | { text: "Toggle Workspaces", link: "/scripts/toggle_workspaces" }, 76 | { text: "Custom Tree Borders", link: "/scripts/custom_tree_borders" }, 77 | { text: "Dim Unfocused", link: "/scripts/dim_unfocused" }, 78 | ], 79 | }, 80 | { 81 | text: "Example Configs", 82 | collapsible: true, 83 | collapsed: true, 84 | items: [ 85 | { text: "Config 1", link: "/configs/config1" }, 86 | { text: "Config 2", link: "/configs/config2" }, 87 | { text: "Config 3", link: "/configs/config3" }, 88 | ], 89 | }, 90 | ], 91 | }, 92 | }); 93 | -------------------------------------------------------------------------------- /site/docs/.vitepress/theme/FiraCodeNerdFont-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dooit-org/dooit-extras/faab40ab94b1d2ba0a92cb951a6c5ea3926feb8b/site/docs/.vitepress/theme/FiraCodeNerdFont-Regular.ttf -------------------------------------------------------------------------------- /site/docs/.vitepress/theme/index.js: -------------------------------------------------------------------------------- 1 | import DefaultTheme from 'vitepress/theme' 2 | import './style.css' 3 | 4 | export default DefaultTheme 5 | -------------------------------------------------------------------------------- /site/docs/.vitepress/theme/style.css: -------------------------------------------------------------------------------- 1 | /* .vitepress/theme/style.css */ 2 | @font-face{ 3 | font-family: "NerdFont"; 4 | src:local("FiraCodeNerdFont-Regular"), 5 | url('./FiraCodeNerdFont-Regular.ttf') 6 | } 7 | 8 | .nerd-icon { 9 | font-family: 'NerdFont', sans-serif; 10 | font-size: 20px; 11 | } 12 | -------------------------------------------------------------------------------- /site/docs/configs/code/config1.py: -------------------------------------------------------------------------------- 1 | from dooit.ui.api import DooitAPI, subscribe 2 | from dooit.ui.api.events import Startup 3 | from rich.style import Style 4 | from dooit_extras.formatters import * 5 | from dooit_extras.bar_widgets import * 6 | from dooit_extras.scripts import * 7 | from rich.text import Text 8 | 9 | 10 | @subscribe(Startup) 11 | def setup_formatters(api: DooitAPI, _): 12 | fmt = api.formatter 13 | theme = api.vars.theme 14 | 15 | # ------- WORKSPACES ------- 16 | format = Text(" ({}) ", style=theme.primary).markup 17 | fmt.workspaces.description.add(description_children_count(format)) 18 | 19 | # --------- TODOS --------- 20 | # status formatter 21 | fmt.todos.status.add(status_icons(completed=" ", pending="󰞋 ", overdue="󰅗 ")) 22 | 23 | # urgency formatte 24 | u_icons = {1: " 󰎤", 2: " 󰎧", 3: " 󰎪", 4: " 󰎭"} 25 | fmt.todos.urgency.add(urgency_icons(icons=u_icons)) 26 | 27 | # due formatter 28 | fmt.todos.due.add(due_casual_format()) 29 | fmt.todos.due.add(due_icon(completed=" ", pending=" ", overdue=" ")) 30 | 31 | # description formatter 32 | format = Text("  {completed_count}/{total_count}", style=theme.green).markup 33 | fmt.todos.description.add(todo_description_progress(fmt=format)) 34 | fmt.todos.description.add(description_highlight_tags(fmt=" {}")) 35 | 36 | 37 | @subscribe(Startup) 38 | def setup_bar(api: DooitAPI, _): 39 | theme = api.vars.theme 40 | 41 | widgets = [ 42 | Mode(api), 43 | Spacer(api, width=0), 44 | StatusIcons(api, bg=theme.background2), 45 | TextBox(api, text="  ", bg=theme.primary), 46 | TextBox(api, text=" -4°C ", fg=theme.foreground3, bg=theme.background3), 47 | TextBox(api, text=" 󰥔 ", bg=theme.primary), 48 | Clock(api, format="%I:%M %p", fg=theme.foreground3, bg=theme.background3), 49 | ] 50 | api.bar.set(widgets) 51 | 52 | 53 | @subscribe(Startup) 54 | def setup_dashboard(api: DooitAPI, _): 55 | from datetime import datetime 56 | 57 | theme = api.vars.theme 58 | 59 | now = datetime.now() 60 | formatted_date = now.strftime(" 󰸘 %A, %d %b ") 61 | 62 | header = Text( 63 | "I alone shall stand against the darkness of my overdue tasks", 64 | style=Style(color=theme.primary, bold=True, italic=True), 65 | ) 66 | 67 | ascii_art = r""" 68 | . 69 | / V\ 70 | / ` / 71 | << | 72 | / | 73 | / | 74 | / | 75 | / \ \ / 76 | ( ) | | 77 | ________| _/_ | | 78 | <__________\______)\__) 79 | """ 80 | 81 | items = [ 82 | header, 83 | "", 84 | Text(ascii_art, style=api.vars.theme.primary), 85 | "", 86 | Text( 87 | formatted_date, 88 | style=Style(color=theme.secondary, bold=True, italic=True), 89 | ), 90 | ] 91 | api.dashboard.set(items) 92 | -------------------------------------------------------------------------------- /site/docs/configs/code/config2.py: -------------------------------------------------------------------------------- 1 | from dooit.api import Todo 2 | from dooit.ui.api import DooitAPI, subscribe 3 | from dooit.ui.api.events import Startup 4 | from dooit.api.theme import DooitThemeBase 5 | from dooit.ui.api.widgets import TodoWidget 6 | from rich.style import Style 7 | from dooit_extras.formatters import * 8 | from dooit_extras.bar_widgets import * 9 | from dooit_extras.scripts import * 10 | from rich.text import Text 11 | 12 | 13 | class DooitThemeCatppuccin(DooitThemeBase): 14 | _name: str = "dooit-catppuccin" 15 | 16 | # background colors 17 | background1: str = "#1e1e2e" # Darkest 18 | background2: str = "#313244" 19 | background3: str = "#45475a" # Lightest 20 | 21 | # foreground colors 22 | foreground1: str = "#a6adc8" # Lightest 23 | foreground2: str = "#bac2de" 24 | foreground3: str = "#cdd6f4" # Darkest 25 | 26 | # other colors 27 | red: str = "#f38ba8" 28 | orange: str = "#fab387" 29 | yellow: str = "#f9e2af" 30 | green: str = "#a6e3a1" 31 | blue: str = "#89b4fa" 32 | purple: str = "#b4befe" 33 | magenta: str = "#f5c2e7" 34 | cyan: str = "#89dceb" 35 | 36 | # accent colors 37 | primary: str = purple 38 | secondary: str = blue 39 | 40 | 41 | @subscribe(Startup) 42 | def setup_colorscheme(api: DooitAPI, _): 43 | api.css.set_theme(DooitThemeCatppuccin) 44 | 45 | 46 | @subscribe(Startup) 47 | def setup_formatters(api: DooitAPI, _): 48 | fmt = api.formatter 49 | theme = api.vars.theme 50 | 51 | # ------- WORKSPACES ------- 52 | format = Text(" ({}) ", style=theme.primary).markup 53 | fmt.workspaces.description.add(description_children_count(format)) 54 | 55 | # --------- TODOS --------- 56 | # status formatter 57 | fmt.todos.status.add(status_icons(completed=" ", pending=" ", overdue=" ")) 58 | 59 | # urgency formatte 60 | u_icons = {1: " 󰯬", 2: " 󰯯", 3: " 󰯲", 4: " 󰯵"} 61 | fmt.todos.urgency.add(urgency_icons(icons=u_icons)) 62 | 63 | # due formatter 64 | fmt.todos.due.add(due_casual_format()) 65 | fmt.todos.due.add(due_icon(completed="󱫐 ", pending="󱫚 ", overdue="󱫦 ")) 66 | 67 | # effort formatter 68 | fmt.todos.effort.add(effort_icon(icon="󱠇 ")) 69 | 70 | # description formatter 71 | format = Text("  {completed_count}/{total_count}", style=theme.green).markup 72 | fmt.todos.description.add(todo_description_progress(fmt=format)) 73 | fmt.todos.description.add(description_highlight_tags(fmt=" {}")) 74 | fmt.todos.description.add(description_strike_completed()) 75 | 76 | 77 | @subscribe(Startup) 78 | def setup_layout(api: DooitAPI, _): 79 | api.layouts.todo_layout = [ 80 | TodoWidget.status, 81 | TodoWidget.effort, 82 | TodoWidget.description, 83 | TodoWidget.due, 84 | ] 85 | 86 | 87 | @subscribe(Startup) 88 | def setup_bar(api: DooitAPI, _): 89 | theme = api.vars.theme 90 | 91 | widgets = [ 92 | TextBox(api, " 󰄛 ", bg=theme.magenta), 93 | Spacer(api, width=1), 94 | Mode(api, format_normal=" 󰷸 NORMAL ", format_insert=" 󰛿 INSERT "), 95 | Spacer(api, width=0), 96 | WorkspaceProgress(api, fmt=" 󰞯 {}% ", bg=theme.secondary), 97 | Spacer(api, width=1), 98 | Date(api, fmt=" 󰃰 {} "), 99 | ] 100 | api.bar.set(widgets) 101 | 102 | 103 | @subscribe(Startup) 104 | def setup_dashboard(api: DooitAPI, _): 105 | theme = api.vars.theme 106 | 107 | ascii_art = r""" 108 | ,-. _,---._ __ / \ 109 | / ) .-' `./ / \ 110 | ( ( ,' `/ /| 111 | \ `-" \'\ / | 112 | `. , \ \ / | 113 | /`. ,'-`----Y | 114 | ( ; | ' 115 | | ,-. ,-' | / 116 | | | ( | TODOS | / 117 | ) | \ `.___________|/ 118 | `--' `--' 119 | """ 120 | 121 | ascii_art = Text(ascii_art, style=theme.primary) 122 | ascii_art.highlight_words(["TODOS"], style=theme.red) 123 | 124 | due_today = sum([1 for i in Todo.all() if i.is_due_today and i.is_pending]) 125 | overdue = sum([1 for i in Todo.all() if i.is_overdue]) 126 | 127 | header = Text( 128 | "Another day, another opportunity to organize my todos and then procrastinate", 129 | style=Style(color=theme.secondary, bold=True, italic=True), 130 | ) 131 | 132 | items = [ 133 | header, 134 | ascii_art, 135 | "", 136 | "", 137 | Text("󰠠 Tasks pending today: {}".format(due_today), style=theme.green), 138 | Text("󰁇 Tasks still overdue: {}".format(overdue), style=theme.red), 139 | ] 140 | api.dashboard.set(items) 141 | -------------------------------------------------------------------------------- /site/docs/configs/code/config3.py: -------------------------------------------------------------------------------- 1 | from dooit.ui.api import DooitAPI, subscribe 2 | from dooit.ui.api.events import Startup 3 | from dooit.api.theme import DooitThemeBase 4 | from dooit.ui.api.widgets import TodoWidget 5 | from rich.style import Style 6 | from dooit_extras.formatters import * 7 | from dooit_extras.bar_widgets import * 8 | from dooit_extras.scripts import * 9 | from rich.text import Text 10 | 11 | 12 | class Everforest(DooitThemeBase): 13 | _name: str = "dooit-everforest" 14 | 15 | # background colors 16 | background1: str = "#2b3339" # Darkest 17 | background2: str = "#323d43" # Lighter 18 | background3: str = "#3a454a" # Lightest 19 | 20 | # foreground colors 21 | foreground1: str = "#d3c6aa" # Darkest 22 | foreground2: str = "#e9e8d2" # Lighter 23 | foreground3: str = "#fdf6e3" # Lightest 24 | 25 | # other colors 26 | red: str = "#e67e80" # Red 27 | orange: str = "#e69875" # Orange 28 | yellow: str = "#dbbc7f" # Yellow 29 | green: str = "#a7c080" # Green 30 | blue: str = "#7fbbb3" # Blue 31 | purple: str = "#d699b6" # Purple 32 | magenta: str = "#d699b6" # Magenta (same as purple in Everforest) 33 | cyan: str = "#83c092" # Cyan 34 | 35 | # accent colors 36 | primary: str = cyan 37 | secondary: str = green 38 | 39 | 40 | @subscribe(Startup) 41 | def setup_colorscheme(api: DooitAPI, _): 42 | api.css.set_theme(Everforest) 43 | 44 | 45 | @subscribe(Startup) 46 | def setup_formatters(api: DooitAPI, _): 47 | fmt = api.formatter 48 | theme = api.vars.theme 49 | 50 | # ------- WORKSPACES ------- 51 | format = Text(" ({}) ", style=theme.primary).markup 52 | fmt.workspaces.description.add(description_children_count(format)) 53 | 54 | # --------- TODOS --------- 55 | # status formatter 56 | fmt.todos.status.add(status_icons(completed="󱓻 ", pending="󱓼 ", overdue="󱓼 ")) 57 | 58 | # urgency formatte 59 | u_icons = {1: " 󰯬", 2: " 󰯯", 3: " 󰯲", 4: " 󰯵"} 60 | fmt.todos.urgency.add(urgency_icons(icons=u_icons)) 61 | 62 | # due formatter 63 | fmt.todos.due.add(due_casual_format()) 64 | fmt.todos.due.add(due_icon(completed="󰐅 ", pending="󱢗 ", overdue="󱐚 ")) 65 | 66 | # effort formatter 67 | fmt.todos.effort.add(effort_icon(icon="󱠇 ")) 68 | 69 | # description formatter 70 | format = Text("  {completed_count}/{total_count}", style=theme.green).markup 71 | fmt.todos.description.add(todo_description_progress(fmt=format)) 72 | fmt.todos.description.add(description_highlight_tags(fmt="󰌪 {}")) 73 | fmt.todos.description.add(description_strike_completed()) 74 | 75 | 76 | @subscribe(Startup) 77 | def setup_layout(api: DooitAPI, _): 78 | api.layouts.todo_layout = [ 79 | TodoWidget.status, 80 | TodoWidget.description, 81 | TodoWidget.due, 82 | ] 83 | 84 | 85 | @subscribe(Startup) 86 | def setup_bar(api: DooitAPI, _): 87 | theme = api.vars.theme 88 | mode_style = Style(color=theme.background1, bgcolor=theme.primary, bold=True) 89 | 90 | widgets = [ 91 | Mode( 92 | api, 93 | format_normal="NOR", 94 | format_insert="INS", 95 | fmt=Text(" 󰌪 {}", style=mode_style).markup, 96 | ), 97 | Powerline.right_rounded(api, fg=theme.primary), 98 | Spacer(api, width=0), 99 | Powerline.left_rounded(api, fg=theme.primary), 100 | Ticker(api, fmt=" 󱎫 {} "), 101 | Powerline.left_rounded(api, fg=theme.yellow, bg=theme.primary), 102 | Clock(api, format="%H:%M", fmt=" 󰥔 {} ", bg=theme.yellow), 103 | ] 104 | api.bar.set(widgets) 105 | 106 | 107 | @subscribe(Startup) 108 | def setup_dashboard(api: DooitAPI, _): 109 | theme = api.vars.theme 110 | 111 | ascii_art = r""" 112 | ____ 113 | v _( ) 114 | _ ^ _ v (___(__) 115 | '_\V/ ` 116 | ' oX` 117 | X 118 | X Help, I can't finish! 119 | X - . 120 | X \O/ |\ 121 | X.a##a. M |_\ 122 | .aa########a.>> _____|_____ 123 | .a################aa. \ DOOIT / 124 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 125 | """ 126 | 127 | ascii_art = Text(ascii_art, style=theme.primary) 128 | ascii_art.highlight_words([" Help, I can't finish! "], style="reverse") 129 | ascii_art.highlight_words([" DOOIT "], style=theme.secondary) 130 | 131 | header = Text( 132 | "Welcome again to your daily life, piled with unfinished tasks!", 133 | style=Style(color=theme.secondary, bold=True, italic=True), 134 | ) 135 | 136 | items = [ 137 | header, 138 | ascii_art, 139 | "", 140 | "", 141 | Text("Will you finish your tasks today?", style=theme.secondary), 142 | ] 143 | api.dashboard.set(items) 144 | 145 | 146 | @subscribe(Startup) 147 | def additional_setup(api: DooitAPI, _): 148 | dim_unfocused(api, "60%") 149 | -------------------------------------------------------------------------------- /site/docs/configs/config1.md: -------------------------------------------------------------------------------- 1 | 14 | 15 | # Config 1 16 | 17 | A cool configuration based on [`Nord Theme`](https://www.nordtheme.com/) 18 | 19 | ## Screenshots 20 | 21 |
22 | 23 | ![Dashboard](./previews/config1/dashboard.png) 24 | 25 | ![MainScreen](./previews/config1/mainscreen.png) 26 | 27 | ![Help](./previews/config1/help.png) 28 | 29 |
30 | 31 | ## Code 32 | 33 | <<< ./code/config1.py 34 | -------------------------------------------------------------------------------- /site/docs/configs/config2.md: -------------------------------------------------------------------------------- 1 | 14 | 15 | # Config 2 16 | 17 | A colorful configuration based on [`Catppuccin Colorscheme`](https://catppuccin.com/) 18 | 19 | ## Screenshots 20 | 21 |
22 | 23 | ![Dashboard](./previews/config2/dashboard.png) 24 | 25 | ![MainScreen](./previews/config2/mainscreen.png) 26 | 27 | ![Help](./previews/config2/help.png) 28 | 29 |
30 | 31 | ## Code 32 | <<< ./code/config2.py 33 | -------------------------------------------------------------------------------- /site/docs/configs/config3.md: -------------------------------------------------------------------------------- 1 | 14 | 15 | # Config 3 16 | 17 | A calm configuration based on [`Everforest Colorscheme`](https://github.com/sainnhe/everforest) 18 | 19 | ## Screenshots 20 | 21 |
22 | 23 | ![Dashboard](./previews/config3/dashboard.png) 24 | 25 | ![MainScreen](./previews/config3/mainscreen.png) 26 | 27 | ![Help](./previews/config3/help.png) 28 | 29 |
30 | 31 | ## Code 32 | <<< ./code/config3.py 33 | -------------------------------------------------------------------------------- /site/docs/configs/previews/config1/dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dooit-org/dooit-extras/faab40ab94b1d2ba0a92cb951a6c5ea3926feb8b/site/docs/configs/previews/config1/dashboard.png -------------------------------------------------------------------------------- /site/docs/configs/previews/config1/help.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dooit-org/dooit-extras/faab40ab94b1d2ba0a92cb951a6c5ea3926feb8b/site/docs/configs/previews/config1/help.png -------------------------------------------------------------------------------- /site/docs/configs/previews/config1/mainscreen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dooit-org/dooit-extras/faab40ab94b1d2ba0a92cb951a6c5ea3926feb8b/site/docs/configs/previews/config1/mainscreen.png -------------------------------------------------------------------------------- /site/docs/configs/previews/config2/dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dooit-org/dooit-extras/faab40ab94b1d2ba0a92cb951a6c5ea3926feb8b/site/docs/configs/previews/config2/dashboard.png -------------------------------------------------------------------------------- /site/docs/configs/previews/config2/help.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dooit-org/dooit-extras/faab40ab94b1d2ba0a92cb951a6c5ea3926feb8b/site/docs/configs/previews/config2/help.png -------------------------------------------------------------------------------- /site/docs/configs/previews/config2/mainscreen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dooit-org/dooit-extras/faab40ab94b1d2ba0a92cb951a6c5ea3926feb8b/site/docs/configs/previews/config2/mainscreen.png -------------------------------------------------------------------------------- /site/docs/configs/previews/config3/dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dooit-org/dooit-extras/faab40ab94b1d2ba0a92cb951a6c5ea3926feb8b/site/docs/configs/previews/config3/dashboard.png -------------------------------------------------------------------------------- /site/docs/configs/previews/config3/help.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dooit-org/dooit-extras/faab40ab94b1d2ba0a92cb951a6c5ea3926feb8b/site/docs/configs/previews/config3/help.png -------------------------------------------------------------------------------- /site/docs/configs/previews/config3/mainscreen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dooit-org/dooit-extras/faab40ab94b1d2ba0a92cb951a6c5ea3926feb8b/site/docs/configs/previews/config3/mainscreen.png -------------------------------------------------------------------------------- /site/docs/formatters/description.md: -------------------------------------------------------------------------------- 1 | # Description Formatters 2 | 3 | ## Children Count 4 | 5 | This formatter shows the count of children present in a given `Todo`/`Workspace` 6 | 7 | | Param|
Default
|Description| 8 | | ------------- | :----------------: | :----------------------------------------------------------------------------------------| 9 | | fmt | `" ({}) "` | The format for showing the children | 10 | 11 | ```python 12 | 13 | from dooit_extras.formatters import description_children_count 14 | from dooit.ui.api.events import subscribe, Startup 15 | 16 | 17 | @subscribe(Startup) 18 | def setup(api, _): 19 | # ... 20 | api.formatter.workspaces.description.add(description_children_count(fmt = "...")) 21 | api.formatter.todos.description.add(description_children_count(fmt = "...")) 22 | # ... 23 | ``` 24 | 25 | ## Highlight Link 26 | 27 | This formatter highlights any url present within the description with theme accent 28 | 29 | | Param|
Default
|Description| 30 | | ------------- | :----------------: | :----------------------------------------------------------------------------------------| 31 | | color | `None` | The color to use for highlighting URLs. If not provided, uses the theme's primary color | 32 | 33 | ```python 34 | 35 | from dooit_extras.formatters import description_highlight_link 36 | from dooit.ui.api.events import subscribe, Startup 37 | 38 | @subscribe(Startup) 39 | def setup(api, _): 40 | # ... 41 | api.formatter.workspaces.description.add(description_highlight_link()) 42 | api.formatter.todos.description.add(description_highlight_link()) 43 | # ... 44 | ``` 45 | 46 | ## Highlight Tags 47 | 48 | This formatter highlights the tags(words starting with `@`) present in the description 49 | 50 | | Param|
Default
|Description| 51 | | ------------- | :----------------: | :----------------------------------------------------------------------------------------| 52 | | color | `""` | The color/style to use for highlighting the tags, Defaults to theme's primary color | 53 | | fmt |" {}"| The format to use to show the tags | 54 | 55 | ```python 56 | 57 | from dooit_extras.formatters import description_highlight_tags 58 | from dooit.ui.api.events import subscribe, Startup 59 | 60 | @subscribe(Startup) 61 | def setup(api, _): 62 | # ... 63 | api.formatter.workspaces.description.add(description_highlight_tags()) 64 | api.formatter.todos.description.add(description_highlight_tags()) 65 | # ... 66 | ``` 67 | 68 | 69 | ## Strike Completed 70 | 71 | This formatter strikes the todos which are completed, and optionally dimmify them 72 | 73 | | Param|
Default
|Description| 74 | | ------------- | :----------------: | :----------------------------------------------------------------------------------------| 75 | | dim | `True` | Whether to dim the todo description | 76 | 77 | ```python 78 | 79 | from dooit.ui.api.events import subscribe, Startup 80 | from dooit_extras.formatters import description_strike_completed 81 | 82 | 83 | @subscribe(Startup) 84 | def setup(api, _): 85 | # ... 86 | api.formatter.workspaces.description.add(description_strike_completed(dim = True)) 87 | api.formatter.todos.description.add(description_strike_completed(dim = True)) 88 | # ... 89 | ``` 90 | 91 | ## Todo Progress 92 | 93 | Formatter to show the progress of a current todo with subtasks 94 | 95 | ***Parameters***: 96 | 97 | | Param|
Default
|Description| 98 | | ------------- | :----------------: | :----------------------------------------------------------------------------------------| 99 | | fmt | `" ({completed_percent}%)"` | The format of the progress | 100 | 101 | 102 | Options available for `fmt` parameters are: 103 | 104 | |
Name
|Description| 105 | | :----------------: | :----------------------------------------------------------------------------------------| 106 | | completed_percent | The current progress in percentage (1-100) | 107 | | remaining_percent | The remaining progress in percentage (1-100) | 108 | | completed_count | The number of subtask completed | 109 | | remaining_count | The number of subtask not completed | 110 | | total_count | The total number of subtask the todo has | 111 | 112 | ```python 113 | 114 | from dooit_extras.formatters import todo_description_progress 115 | from dooit.ui.api.events import subscribe, Startup 116 | 117 | 118 | @subscribe(Startup) 119 | def setup(api, _): 120 | # ... 121 | api.formatter.todos.description.add(todo_description_progress()) 122 | # ... 123 | ``` 124 | -------------------------------------------------------------------------------- /site/docs/formatters/due.md: -------------------------------------------------------------------------------- 1 | # Due Formatters 2 | 3 | ## Casual Format 4 | 5 | This widget shows the date in a simple format 6 | 7 | Eg: Instead of the default `yyyy-mm-dd`, it'll show in the format: \ 8 | `{Mon} {day} ['year(optional)] + ` 9 | 10 | | Param|
Default
|Description| 11 | | ------------- | :----------------: | :----------------------------------------------------------------------------------------| 12 | | fmt | `"{}"` | The custom format for showing the value | 13 | 14 | ```python 15 | 16 | from dooit_extras.formatters import due_casual_format 17 | from dooit.ui.api.events import subscribe, Startup 18 | 19 | @subscribe(Startup) 20 | def setup(api, _): 21 | # ... 22 | api.formatter.todos.due.add(due_casual_format()) 23 | # ... 24 | ``` 25 | 26 | 27 | ## Danger Today 28 | 29 | This formatter shows a bold red `Today` text when the todo is due on the same day 30 | 31 | ```python 32 | 33 | from dooit_extras.formatters import due_danger_today 34 | from dooit.ui.api.events import subscribe, Startup 35 | 36 | | Param|
Default
|Description| 37 | | ------------- | :----------------: | :----------------------------------------------------------------------------------------| 38 | | fmt | `"{}"` | The custom format for showing the value | 39 | 40 | @subscribe(Startup) 41 | def setup(api, _): 42 | # ... 43 | api.formatter.todos.due.add(due_danger_today()) 44 | # ... 45 | ``` 46 | 47 | ## Due Icon 48 | 49 | This formatter shows due icons based on status. 50 | 51 | | Param |
Default
| Description | 52 | |-------------|:--------------------------------------:|:-------------------------------------------------| 53 | | completed | 󰃯 | Icon for completed todo | 54 | | pending | 󰃰 | Icon for pending todo | 55 | | overdue | | Icon for overdue todo | 56 | 57 | 58 | ```python 59 | 60 | from dooit_extras.formatters import due_icon 61 | from dooit.ui.api.events import subscribe, Startup 62 | 63 | @subscribe(Startup) 64 | def setup(api, _): 65 | # ... 66 | api.formatter.todos.due.add(due_icon()) 67 | # ... 68 | ``` 69 | -------------------------------------------------------------------------------- /site/docs/formatters/effort.md: -------------------------------------------------------------------------------- 1 | # Effort Formatters 2 | 3 | ## Effort Icon 4 | 5 | This formatter shows an icon for todos that have effort enabled 6 | 7 | | Param|
Default
|Description| 8 | | ------------- | :----------------: | :----------------------------------------------------------------------------------------| 9 | | icon | 󰈸 | The icon to show for todos with effort | 10 | | color | `None` | The color to use for the icon. If not provided, uses the theme's orange color | 11 | | show_on_zero | `True` | Whether to show the icon when effort is 0 | 12 | 13 | ```python 14 | 15 | from dooit_extras.formatters import effort_icon 16 | from dooit.ui.api import DooitAPI, subscribe 17 | from dooit.ui.api.events import Startup 18 | 19 | @subscribe(Startup) 20 | def setup(api: DooitAPI, _): 21 | # ... 22 | api.formatter.todos.effort.add(effort_icon()) 23 | # ... 24 | ``` -------------------------------------------------------------------------------- /site/docs/formatters/recurrence.md: -------------------------------------------------------------------------------- 1 | Recurrence Formatters 2 | 3 | ## Recurrence Icon 4 | 5 | This formatter shows an icon for todos that have recurrence enabled 6 | 7 | | Param|
Default
|Description| 8 | | ------------- | :----------------: | :----------------------------------------------------------------------------------------| 9 | | icon | 󰑖 | The icon to show for recurring todos | 10 | | color | `None` | The color to use for the icon. If not provided, uses the theme's primary color | 11 | 12 | 13 | ```python 14 | 15 | from dooit_extras.formatters import recurrence_icon 16 | from dooit.ui.api import DooitAPI, subscribe 17 | from dooit.ui.api.events import Startup 18 | 19 | @subscribe(Startup) 20 | def setup(api: DooitAPI, _): 21 | # ... 22 | api.formatter.todos.recurrence.add(recurrence_icon()) 23 | # ... 24 | ``` 25 | -------------------------------------------------------------------------------- /site/docs/formatters/status.md: -------------------------------------------------------------------------------- 1 | # Status Formatters 2 | 3 | ## Status Icons 4 | 5 | This formatter shows different icons based on the current status of the todo 6 | 7 | | Param|
Default
|Description| 8 | | ------------- | :----------------: | :----------------------------------------------------------------------------------------| 9 | | completed | `"x"` | The icon to show when status is `completed` | 10 | | pending | `"o"` | The icon to show when status is `pending` | 11 | | overdue | `"!"` | The icon to show when status is `overdue` | 12 | 13 | 14 | ::: tip INFO 15 | By default, the colors are theme versions of `green`, `yellow` and `red` 16 | ::: 17 | 18 | ### Usage: 19 | 20 | ```python 21 | 22 | from dooit_extras.formatters import status_icons 23 | from dooit.ui.api.events import subscribe, Startup 24 | 25 | @subscribe(Startup) 26 | def setup(api, _): 27 | # ... 28 | api.formatter.todos.status.add(status_icons(...)) 29 | # ... 30 | ``` 31 | 32 | 33 | -------------------------------------------------------------------------------- /site/docs/formatters/urgency.md: -------------------------------------------------------------------------------- 1 | # Urgency Formatters 2 | 3 | ## Urgency Icons 4 | 5 | This formatter shows different icons based on the urgency level of the todo 6 | 7 | | Param|
Default
|Description| 8 | | ------------- | :----------------: | :----------------------------------------------------------------------------------------| 9 | | icons | `{}` | A dictionary with icon for respective urgency levels | 10 | | colors | `{}` | A dictionary with styles for respective urgency levels | 11 | 12 | Example icons: 13 | 14 | Example icons: 15 | 16 | ```py 17 | {1: "󰲠", 2: "󰲢", 3: "󰲤", 4: "1"} 18 | ``` 19 | 20 | Example colors: 21 | 22 | ```py 23 | {1: theme.green, 2: theme.yellow, 3: theme.orange, 4: theme.red} 24 | ``` 25 | 26 | ### Usage: 27 | 28 | ```python 29 | 30 | from dooit_extras.formatters import urgency_icons 31 | from dooit.ui.api.events import subscribe, Startup 32 | 33 | @subscribe(Startup) 34 | def setup(api, _): 35 | # ... 36 | api.formatter.todos.urgency.add(urgency_icons()) 37 | # ... 38 | ``` -------------------------------------------------------------------------------- /site/docs/getting_started/introduction.md: -------------------------------------------------------------------------------- 1 | # Dooit Extras 2 | 3 | Dooit extras is a library consisting of pre-built stuff for Dooit \ 4 | which includes various bar widgets and formatters. 5 | 6 | Along with some pre-configured configs to get you started! 7 | 8 | 9 | ## Installation 10 | 11 | You can refere to [dooit's installation guide](/) which also includes `dooit-extras` 12 | 13 | :::tip :grey_exclamation: NOTE 14 | Dooit extras uses [`Nerd Font Icons`](https://www.nerdfonts.com/) for everything, so make sure you're using that \ 15 | otherwise you'll see gibberish icons 16 | ::: 17 | 18 | ### Rich and Style 19 | 20 | Already mentioned in [Dooit's documentation](https://dooit-org.github.io/dooit/getting_started/introduction.html#rich) but just reminding that it'll be great to famaliarize yourself with rich's `Text` and `Style` classes if you want to customize! 21 | -------------------------------------------------------------------------------- /site/docs/getting_started/usage.md: -------------------------------------------------------------------------------- 1 | # Using utilities 2 | 3 | The libray provides `Formatters`, `Bar Widgets` and `Sample Configs` 4 | 5 | ## Formatters 6 | 7 | Check out all the formatters [here](/formatters/description) \ 8 | Every one of them also has an example code to use :) 9 | 10 | 11 | ## Bar Widgets 12 | 13 | Check out all the bar widgets [here](/widgets/clock) 14 | 15 | They can be configured with different colors, plus have some custom options such as `TextBox` and `Custom` 16 | 17 | ## Sample Configs 18 | 19 | This library have a few sample configurations pre-made for you so you can better understand some stuff and create something awesome of your own 20 | 21 | 22 | #### Thank you for using dooit <3 23 | -------------------------------------------------------------------------------- /site/docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: home 3 | 4 | hero: 5 | name: "Dooit Extras" 6 | tagline: "A collection of utilities to customize your dooit!" 7 | actions: 8 | - theme: brand 9 | text: Get Started 10 | link: /getting_started/introduction 11 | - theme: alt 12 | text: View on Github 13 | link: https://www.github.com/dooit-org/dooit-extras 14 | features: 15 | - icon: maintenance 16 | title: Customizability 17 | details: Dooit Extras comes with a bunch of formatters, widgets etc which you can further tweak by changing some parameters! 18 | - icon: settings-3 19 | title: Configs 20 | details: Dooit Extras comes with a few sample configs which you can use or take inspiration from to rich your dooit 21 | --- 22 | -------------------------------------------------------------------------------- /site/docs/scripts/custom_tree_borders.md: -------------------------------------------------------------------------------- 1 | # Custom Tree Borders 2 | 3 | This script allows you to edit the border for workspaces and todo trees 4 | 5 | Check out available border types [here](https://textual.textualize.io/styles/border/#all-border-types) 6 | 7 | | Param|
Default
|Description| 8 | | ------------- | :----------------: | :----------------------------------------------------------------------------------------| 9 | | api | | The dooit api object | 10 | | focus_border | | The border to use for the focused tree | 11 | | dim_border | | The border to use for the unfocused tree | 12 | 13 | Example: 14 | 15 | ```py 16 | from dooit.ui.api import DooitAPI, subscribe 17 | from dooit.ui.api.events import Startup 18 | from dooit_extras.scripts import custom_tree_borders 19 | 20 | @subscribe(Startup) 21 | def setup(api: DooitAPI, _): 22 | custom_tree_borders(api, "panel", "panel") 23 | ``` 24 | 25 | Preview: 26 | 27 | ![Custom Tree Borders](./previews/custom_tree_borders.png) 28 | -------------------------------------------------------------------------------- /site/docs/scripts/dim_unfocused.md: -------------------------------------------------------------------------------- 1 | # Dim Unfocused 2 | 3 | This script allows you to dimmify workspaces or todo tree which is Unfocused 4 | The value for opacity is in the format: `%` where number can be from 1 till 100 5 | 6 | | Param|
Default
|Description| 7 | | ------------- | :----------------: | :----------------------------------------------------------------------------------------| 8 | | api | | The dooit api object | 9 | | opacity | `"50%"` | The opacity of the unfocused tree | 10 | 11 | Example: 12 | 13 | ```py 14 | from dooit.ui.api import DooitAPI, subscribe 15 | from dooit.ui.api.events import Startup 16 | from dooit_extras.scripts import dim_unfocused 17 | 18 | @subscribe(Startup) 19 | def setup(api: DooitAPI, _): 20 | dim_unfocused(api, "50%") 21 | ``` 22 | 23 | Preview: 24 | 25 | ![Dim Unfocused Workspaces](./previews/dim_unfocused_workspaces.png) 26 | 27 | ![Dim Unfocused Todos](./previews/dim_unfocused_todos.png) 28 | -------------------------------------------------------------------------------- /site/docs/scripts/previews/custom_tree_borders.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dooit-org/dooit-extras/faab40ab94b1d2ba0a92cb951a6c5ea3926feb8b/site/docs/scripts/previews/custom_tree_borders.png -------------------------------------------------------------------------------- /site/docs/scripts/previews/dim_unfocused_todos.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dooit-org/dooit-extras/faab40ab94b1d2ba0a92cb951a6c5ea3926feb8b/site/docs/scripts/previews/dim_unfocused_todos.png -------------------------------------------------------------------------------- /site/docs/scripts/previews/dim_unfocused_workspaces.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dooit-org/dooit-extras/faab40ab94b1d2ba0a92cb951a6c5ea3926feb8b/site/docs/scripts/previews/dim_unfocused_workspaces.png -------------------------------------------------------------------------------- /site/docs/scripts/previews/toggle_workspaces.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dooit-org/dooit-extras/faab40ab94b1d2ba0a92cb951a6c5ea3926feb8b/site/docs/scripts/previews/toggle_workspaces.mp4 -------------------------------------------------------------------------------- /site/docs/scripts/toggle_workspaces.md: -------------------------------------------------------------------------------- 1 | # Toggle Workspaces 2 | 3 | This script can toggle the view for workspaces tree 4 | 5 | You can bind it to a keybinding and use that 6 | 7 | | Param|
Default
|Description| 8 | | ------------- | :----------------: | :----------------------------------------------------------------------------------------| 9 | | api | | The dooit api object | 10 | 11 | Example: 12 | 13 | ```py 14 | from dooit.ui.api import DooitAPI, subscribe 15 | from dooit.ui.api.events import Startup 16 | from dooit_extras.scripts import toggle_workspaces 17 | 18 | @subscribe(Startup) 19 | def setup(api: DooitAPI, _): 20 | api.keys.set("", toggle_workspaces(api)) 21 | ``` 22 | 23 | Preview: 24 | 25 |