{"id":42050285,"url":"https://github.com/m-xim/textcompose","last_synced_at":"2026-01-26T06:34:55.675Z","repository":{"id":292123142,"uuid":"975982830","full_name":"m-xim/textcompose","owner":"m-xim","description":"Python library for building dynamic, structured text templates using a declarative, compose-based approach","archived":false,"fork":false,"pushed_at":"2025-11-12T18:14:10.000Z","size":131,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-11-12T20:20:47.601Z","etag":null,"topics":["compose","declarative","structured-text","text","text-template"],"latest_commit_sha":null,"homepage":"https://pypi.org/project/textcompose","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/m-xim.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-05-01T09:03:29.000Z","updated_at":"2025-11-12T18:14:13.000Z","dependencies_parsed_at":"2025-06-07T14:33:11.167Z","dependency_job_id":null,"html_url":"https://github.com/m-xim/textcompose","commit_stats":null,"previous_names":["m-xim/textcompose"],"tags_count":22,"template":false,"template_full_name":null,"purl":"pkg:github/m-xim/textcompose","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/m-xim%2Ftextcompose","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/m-xim%2Ftextcompose/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/m-xim%2Ftextcompose/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/m-xim%2Ftextcompose/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/m-xim","download_url":"https://codeload.github.com/m-xim/textcompose/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/m-xim%2Ftextcompose/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28768441,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-26T03:54:34.369Z","status":"ssl_error","status_checked_at":"2026-01-26T03:54:33.031Z","response_time":59,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["compose","declarative","structured-text","text","text-template"],"created_at":"2026-01-26T06:34:54.822Z","updated_at":"2026-01-26T06:34:55.667Z","avatar_url":"https://github.com/m-xim.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# TextCompose\n\n[![PyPI version](https://img.shields.io/pypi/v/textcompose?color=blue)](https://pypi.org/project/textcompose)\n[![License](https://img.shields.io/github/license/m-xim/textcompose.svg)](/LICENSE)\n[![Tests Status](https://github.com/m-xim/textcompose/actions/workflows/tests.yml/badge.svg)](https://github.com/m-xim/textcompose/actions)\n[![Release Status](https://github.com/m-xim/textcompose/actions/workflows/release.yml/badge.svg)](https://github.com/m-xim/textcompose/actions)\n[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/m-xim/textcompose)\n\n**TextCompose** is a Python library for creating dynamic, structured text templates with an intuitive, component-based approach. Inspired by [aiogram-dialog](https://github.com/Tishka17/aiogram_dialog).\n\n\u003cbr\u003e\n\n## ✨ Features\n\n- 🧱 Flexible text composition from components\n- 🔀 Conditional rendering support (`when`)\n- 🔁 Grouping and repeating blocks\n- 🎨 Formatting via f-string and Jinja2\n- 🔌 Easily extensible with new components\n\n## 🚀 Installation\n\n```bash\nuv add textcompose\n# or\npip install textcompose\n```\n\n## 🧩 Components Overview\n\n### General\n\n- `Template` — main class for combining and rendering components\n\n\n### Elements\n`Element` — abstract base class for all element components\n\n- `Text` — static text.\n- `Format` — dynamic python f-string formatting\n- `Jinja` — Jinja2 template rendering\n- [`ProgressBar`](#progressbar) — show progress visually\n\n### Containers\n`Container` — abstract base class for all container components\n\n- `Group` — group children with custom separators.\n- `List` — repeat templates for each item in a collection.\n\n### Logic Components\n`Logic` — abstract base class for all container components\n\n- `If` — conditionally show different blocks (use `if_`, `then_`, `else_`)\n\n\u003e [!TIP]\n\u003e All components support the `when` parameter for conditional display (value, expression, function, or magic_filter).\n\n## ⚡️ How to Use\n\nAll usage examples can be found in the [`example`](./example) folder.\n\n### Quick Start\nSee how easy it is to build structured, interactive text blocks:\n\n```python\nfrom magic_filter import F\n\nfrom textcompose import Template\nfrom textcompose.containers import Group, List\nfrom textcompose.elements import Format, Jinja, Text\nfrom textcompose.logics import If\n\ntemplate = Template(\n    Format(\"Hello, {name}!\"),\n    Format(\"Status: {status}\"),  # or `lambda ctx: f\"Status: {ctx['status']}\"` with function\n    If(\n        F[\"notifications\"] \u003e 0,  # `if_`: condition to check if there are notifications\n        Format(\"You have {notifications} new notifications.\"),  # `then_`: content to render if condition is True\n        Format(\"You not have new notifications.\"),  # `else_`: content to render if condition is False\n    ),\n    Group(\n        Jinja(\"\\nTotal messages {{ messages|length }}:\"),\n        List(\n            Format(\"Time - {item[time]}:\"),\n            Format(\"-  {item[text]}\"),\n            sep=\"\\n\",  # `sep`: separator between list items\n            inner_sep=\"\\n\",  # `inner_sep`: separator between parts of a single item\n            getter=lambda ctx: ctx[\"messages\"],  # `getter`: function or F to extract the list of messages from context\n        ),\n        sep=\"\\n\",  # `sep`: separator between children of Group\n        when=F[\"messages\"].len() \u003e 0,  # `when`: show this block only if there are messages\n    ),\n    Text(\"\\nThank you for using our service!\"),  # or \"Recent messages:\" without class\n)\n\ncontext = {\n    \"name\": \"Alexey\",\n    \"status\": \"Online\",\n    \"notifications\": 2,\n    \"messages\": [\n        {\"text\": \"Your package has been delivered.\", \"time\": \"09:15\"},\n        {\"text\": \"Reminder: meeting tomorrow at 10:00.\", \"time\": \"18:42\"},\n    ],\n}\n\nprint(template.render(context))\n```\n\n**Output:**\n```\nHello, Alexey!\nStatus: Online\nYou have 2 new notifications.\n\nTotal messages 2:\nTime - 09:15:\n-  Your package has been delivered.\nTime - 18:42:\n-  Reminder: meeting tomorrow at 10:00.\n\nThank you for using our service!\n```\n\n### ProgressBar\n\nThe `ProgressBar` component renders a textual progress bar. It supports various styles, customizable width, templates, and dynamic values.\n\n#### Usage\n\n```python\nfrom textcompose.elements import ProgressBar\n\nbar = ProgressBar(\n    current=42,  # `current`: Current progress value\n    total=100,  # `total`: Total value for the progress bar\n    width=20,  # `width`: Number of characters in the bar.\n    style=\"symbol_square\",  # `style`: Style (string — built-in style name, or `ProgressBarStyle` object).\n)\nprint(bar.render({}))\n```\n\n**Output:**\n```\n[■■■■■■■■        ] 42%\n```\n\n#### Styles\n\nBuilt-in styles are listed in the `PROGRESS_BAR_STYLES` dictionary (see [`textcompose/styles/progress_bar.py`](./textcompose/styles/progress_bar.py)). Examples:\n\n- `\"symbol_square\"`: `[■■■■■     ]`\n- `\"symbol_classic\"`: `[#####-----]`\n- `\"emoj_square\"`: `🟩🟩🟩⬜⬜⬜`\n- `\"emoji_circle\"`: `🟢🟢⚪⚪⚪`\n\nYou can create a custom style using `ProgressBarStyle`:\n\n```python\nfrom textcompose.styles import ProgressBarStyle\nfrom textcompose.elements import ProgressBar\n\n\ncustom_style = ProgressBarStyle(\n    left=\"\u003c\", fill=\"*\", empty=\"-\", right=\"\u003e\", template=\"{percent} {left}{bar}{right}\"\n)\nbar = ProgressBar(current=7, total=10, width=10, style=custom_style)\nprint(bar.render({}))\n```\n\n**Output:**\n```\n70% \u003c*******---\u003e\n```\n\n##### Template\n\nThe `template` parameter in the style allows you to customize the output string. Available placeholders:\n- `{left}` — left border\n- `{bar}` — the bar itself (filled + empty part)\n- `{right}` — right border\n- `{percent}` — percent complete (e.g., `42%`)\n- `{total}` — maximum value\n- `{current}` — current value\n\n## 🤝 Contributing\n\n💡 Ideas? Issues? PRs are welcome!\u003cbr\u003e\nOpen an issue or pull request to help TextCompose get even better.\n\n\u003cbr\u003e\n\n\u003e **Ready to supercharge your text formatting?\u003cbr\u003e**\n\u003e **Try TextCompose today and make your bots, reports, and notifications shine! ✨**\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fm-xim%2Ftextcompose","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fm-xim%2Ftextcompose","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fm-xim%2Ftextcompose/lists"}