Compare commits

..

203 Commits

Author SHA1 Message Date
9688c20dd9 Added nsis to github actions' path 2026-01-13 00:15:19 -06:00
4d20d7744a Update workflow (again) 2026-01-13 00:04:00 -06:00
29e52288df Update release workflow to install nsis prior to generating the installer 2026-01-13 00:01:19 -06:00
9ed2f6771e Fix setup.py with cx_freeze 8 API changes 2026-01-12 23:44:21 -06:00
302c22ab9f Updated setuptools to attempt a build 2026-01-12 23:17:21 -06:00
da493a88ea change: Update python version to latest 3.13 2026-01-12 23:06:35 -06:00
04dca7681b Revert updater for a while 2026-01-12 20:42:15 -06:00
04843616b3 feat: update updates.json for version 2026.01.12 and remove win32 support 2026-01-12 20:35:07 -06:00
512e2e1684 docs: update changelog and release notes for upcoming release 2026-01-12 02:31:45 -06:00
7a9337c07a feat(mastodon): Add support for editing announcement templates
Updated the template editor and account settings to allow customization of announcement display:

- Added default announcement template to mastodon.defaults.

- Updated templateEditor to recognize announcement variables.

- Added 'Edit template for announcements' button to account configuration dialog.

- Implemented template saving logic in settings controller.
2026-01-12 02:04:20 -06:00
15a9df2ca9 feat(mastodon): Add support for server announcements
Implemented a new 'Announcements' buffer to view instance-wide news. Features include:

- New buffer and UI panel for announcements.

- Support for templates and rendering of announcement content.

- 'Dismiss' functionality (mapped to Enter/Return) to mark announcements as read.

- Integrated into account settings for buffer management.
2026-01-12 01:53:03 -06:00
4df58f0880 Updaed changelog 2026-01-12 01:05:16 -06:00
31bab4cf8a feat(mastodon): Add support for scheduled posts
- Added UI controls (checkbox, date/time pickers) to Post dialog

- Implemented validation logic (min 5 mins future)

- Updated session handler to pass scheduled_at to API
2026-01-11 02:49:46 -06:00
cb0bb4cf27 feat: implement mute conversation support for Mastodon
This feature allows users to visually hide muted conversations from the Home timeline. It includes:

- Automatic filtering of muted posts in Home.

- Immediate visual removal of posts when muting a conversation.

- New 'mute_conversation' action in context menu and keyboard shortcuts.

- Default shortcut: Alt+Win+Shift+Delete

- Win10/11 shortcut: Ctrl+Alt+Win+Backspace
2026-01-11 01:22:29 -06:00
ccaa0d98ff fix: make buffers visible on screen. Maximize window and apply visual fixes. Closes #886 2026-01-11 00:37:59 -06:00
dba26f735c Merge branch 'next-gen' of github.com:mcv-software/twblue into next-gen 2026-01-11 00:30:23 -06:00
José Manuel Delicado
d3a12b9016 Merge pull request #890 from MCV-Software/dependabot/pip/urllib3-2.6.3
Some checks failed
Update translation files / update_catalogs (push) Failing after 22s
Bump urllib3 from 2.6.2 to 2.6.3
2026-01-07 18:33:35 +01:00
dependabot[bot]
eacce7c197 Bump urllib3 from 2.6.2 to 2.6.3
Bumps [urllib3](https://github.com/urllib3/urllib3) from 2.6.2 to 2.6.3.
- [Release notes](https://github.com/urllib3/urllib3/releases)
- [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst)
- [Commits](https://github.com/urllib3/urllib3/compare/2.6.2...2.6.3)

---
updated-dependencies:
- dependency-name: urllib3
  dependency-version: 2.6.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-07 16:57:36 +00:00
José Manuel Delicado
1801d0ae73 Merge pull request #887 from MCV-Software/dependabot/pip/pillow-12.1.0 2026-01-06 00:53:22 +01:00
José Manuel Delicado
41e2fc0d10 Merge pull request #888 from MCV-Software/dependabot/pip/cx-freeze-8.5.3 2026-01-06 00:52:51 +01:00
José Manuel Delicado
aeac36b65d Merge pull request #889 from MCV-Software/dependabot/pip/certifi-2026.1.4 2026-01-06 00:52:28 +01:00
dependabot[bot]
0e4a1cbe65 Bump certifi from 2025.11.12 to 2026.1.4
Bumps [certifi](https://github.com/certifi/python-certifi) from 2025.11.12 to 2026.1.4.
- [Commits](https://github.com/certifi/python-certifi/compare/2025.11.12...2026.01.04)

---
updated-dependencies:
- dependency-name: certifi
  dependency-version: 2026.1.4
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-05 23:08:52 +00:00
dependabot[bot]
7bf8e1670f Bump cx-freeze from 8.5.2 to 8.5.3
Bumps [cx-freeze](https://github.com/marcelotduarte/cx_Freeze) from 8.5.2 to 8.5.3.
- [Release notes](https://github.com/marcelotduarte/cx_Freeze/releases)
- [Changelog](https://github.com/marcelotduarte/cx_Freeze/blob/main/CHANGELOG.md)
- [Commits](https://github.com/marcelotduarte/cx_Freeze/compare/8.5.2...8.5.3)

---
updated-dependencies:
- dependency-name: cx-freeze
  dependency-version: 8.5.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-05 23:08:47 +00:00
dependabot[bot]
2efed8d5c6 Bump pillow from 12.0.0 to 12.1.0
Bumps [pillow](https://github.com/python-pillow/Pillow) from 12.0.0 to 12.1.0.
- [Release notes](https://github.com/python-pillow/Pillow/releases)
- [Changelog](https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst)
- [Commits](https://github.com/python-pillow/Pillow/compare/12.0.0...12.1.0)

---
updated-dependencies:
- dependency-name: pillow
  dependency-version: 12.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-05 23:08:41 +00:00
9b60abb3a0 Merge pull request #860 from MCV-Software/quotes
Mastodon: Adds quotes support (read)
2026-01-04 02:25:31 -06:00
José Manuel Delicado
f4c2dd5239 Merge pull request #883 from MCV-Software/dependabot/pip/coverage-7.13.1
Some checks failed
Update translation files / update_catalogs (push) Failing after 42s
Bump coverage from 7.13.0 to 7.13.1
2025-12-30 07:45:27 +01:00
dependabot[bot]
067957edbf Bump coverage from 7.13.0 to 7.13.1
Bumps [coverage](https://github.com/coveragepy/coveragepy) from 7.13.0 to 7.13.1.
- [Release notes](https://github.com/coveragepy/coveragepy/releases)
- [Changelog](https://github.com/coveragepy/coveragepy/blob/main/CHANGES.rst)
- [Commits](https://github.com/coveragepy/coveragepy/compare/7.13.0...7.13.1)

---
updated-dependencies:
- dependency-name: coverage
  dependency-version: 7.13.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-30 06:44:45 +00:00
José Manuel Delicado
b2b98e38b5 Merge pull request #884 from MCV-Software/dependabot/pip/cx-freeze-8.5.2
Bump cx-freeze from 8.5.1 to 8.5.2
2025-12-30 07:43:26 +01:00
José Manuel Delicado
9f9ea2fec4 Merge pull request #885 from MCV-Software/dependabot/pip/psutil-7.2.1
Bump psutil from 7.1.3 to 7.2.1
2025-12-30 07:43:08 +01:00
dependabot[bot]
eac75d93fd Bump psutil from 7.1.3 to 7.2.1
Bumps [psutil](https://github.com/giampaolo/psutil) from 7.1.3 to 7.2.1.
- [Changelog](https://github.com/giampaolo/psutil/blob/master/HISTORY.rst)
- [Commits](https://github.com/giampaolo/psutil/compare/release-7.1.3...release-7.2.1)

---
updated-dependencies:
- dependency-name: psutil
  dependency-version: 7.2.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-29 23:07:45 +00:00
dependabot[bot]
39608774e7 Bump cx-freeze from 8.5.1 to 8.5.2
Bumps [cx-freeze](https://github.com/marcelotduarte/cx_Freeze) from 8.5.1 to 8.5.2.
- [Release notes](https://github.com/marcelotduarte/cx_Freeze/releases)
- [Changelog](https://github.com/marcelotduarte/cx_Freeze/blob/main/CHANGELOG.md)
- [Commits](https://github.com/marcelotduarte/cx_Freeze/compare/8.5.1...8.5.2)

---
updated-dependencies:
- dependency-name: cx-freeze
  dependency-version: 8.5.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-29 23:07:42 +00:00
José Manuel Delicado
c182b8517b Merge pull request #880 from MCV-Software/dependabot/pip/numpy-2.4.0
Some checks failed
Update translation files / update_catalogs (push) Failing after 24s
Bump numpy from 2.3.5 to 2.4.0
2025-12-23 07:56:55 +01:00
José Manuel Delicado
96b5acf4e9 Merge pull request #881 from MCV-Software/dependabot/pip/importlib-metadata-8.7.1
Bump importlib-metadata from 8.7.0 to 8.7.1
2025-12-23 07:56:33 +01:00
José Manuel Delicado
01109a6436 Merge pull request #882 from MCV-Software/dependabot/pip/cx-freeze-8.5.1
Bump cx-freeze from 8.5.0 to 8.5.1
2025-12-23 07:56:16 +01:00
dependabot[bot]
cb98f74ba8 Bump cx-freeze from 8.5.0 to 8.5.1
Bumps [cx-freeze](https://github.com/marcelotduarte/cx_Freeze) from 8.5.0 to 8.5.1.
- [Release notes](https://github.com/marcelotduarte/cx_Freeze/releases)
- [Changelog](https://github.com/marcelotduarte/cx_Freeze/blob/main/CHANGELOG.md)
- [Commits](https://github.com/marcelotduarte/cx_Freeze/compare/8.5.0...8.5.1)

---
updated-dependencies:
- dependency-name: cx-freeze
  dependency-version: 8.5.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-22 23:06:37 +00:00
dependabot[bot]
243dfdacb4 Bump importlib-metadata from 8.7.0 to 8.7.1
Bumps [importlib-metadata](https://github.com/python/importlib_metadata) from 8.7.0 to 8.7.1.
- [Release notes](https://github.com/python/importlib_metadata/releases)
- [Changelog](https://github.com/python/importlib_metadata/blob/main/NEWS.rst)
- [Commits](https://github.com/python/importlib_metadata/compare/v8.7.0...v8.7.1)

---
updated-dependencies:
- dependency-name: importlib-metadata
  dependency-version: 8.7.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-22 23:06:28 +00:00
dependabot[bot]
06732cf9cf Bump numpy from 2.3.5 to 2.4.0
Bumps [numpy](https://github.com/numpy/numpy) from 2.3.5 to 2.4.0.
- [Release notes](https://github.com/numpy/numpy/releases)
- [Changelog](https://github.com/numpy/numpy/blob/main/doc/RELEASE_WALKTHROUGH.rst)
- [Commits](https://github.com/numpy/numpy/compare/v2.3.5...v2.4.0)

---
updated-dependencies:
- dependency-name: numpy
  dependency-version: 2.4.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-22 23:06:22 +00:00
José Manuel Delicado
f39971a8d0 Merge pull request #879 from MCV-Software/dependabot/pip/deepl-1.27.0
Some checks failed
Update translation files / update_catalogs (push) Failing after 19s
Bump deepl from 1.26.0 to 1.27.0
2025-12-15 10:10:24 +01:00
José Manuel Delicado
5d7994a696 Merge pull request #878 from MCV-Software/dependabot/pip/urllib3-2.6.2
Bump urllib3 from 2.6.1 to 2.6.2
2025-12-15 10:10:00 +01:00
dependabot[bot]
5d400f9211 Bump deepl from 1.26.0 to 1.27.0
Bumps [deepl](https://github.com/DeepLcom/deepl-python) from 1.26.0 to 1.27.0.
- [Release notes](https://github.com/DeepLcom/deepl-python/releases)
- [Changelog](https://github.com/DeepLcom/deepl-python/blob/main/CHANGELOG.md)
- [Commits](https://github.com/DeepLcom/deepl-python/compare/v1.26.0...v1.27.0)

---
updated-dependencies:
- dependency-name: deepl
  dependency-version: 1.27.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-15 09:08:39 +00:00
dependabot[bot]
2d3590a2a0 Bump urllib3 from 2.6.1 to 2.6.2
Bumps [urllib3](https://github.com/urllib3/urllib3) from 2.6.1 to 2.6.2.
- [Release notes](https://github.com/urllib3/urllib3/releases)
- [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst)
- [Commits](https://github.com/urllib3/urllib3/compare/2.6.1...2.6.2)

---
updated-dependencies:
- dependency-name: urllib3
  dependency-version: 2.6.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-15 09:08:35 +00:00
José Manuel Delicado
318d0bcdf9 Merge pull request #877 from MCV-Software/dependabot/pip/deepl-1.26.0
Some checks failed
Update translation files / update_catalogs (push) Failing after 17s
Bump deepl from 1.25.0 to 1.26.0
2025-12-09 07:33:25 +01:00
José Manuel Delicado
9df29c8b51 Merge pull request #876 from MCV-Software/dependabot/pip/coverage-7.13.0
Bump coverage from 7.12.0 to 7.13.0
2025-12-09 07:33:08 +01:00
José Manuel Delicado
8abee93749 Merge pull request #875 from MCV-Software/dependabot/pip/urllib3-2.6.1
Bump urllib3 from 2.6.0 to 2.6.1
2025-12-09 07:32:49 +01:00
dependabot[bot]
81b1db072c Bump deepl from 1.25.0 to 1.26.0
Bumps [deepl](https://github.com/DeepLcom/deepl-python) from 1.25.0 to 1.26.0.
- [Release notes](https://github.com/DeepLcom/deepl-python/releases)
- [Changelog](https://github.com/DeepLcom/deepl-python/blob/main/CHANGELOG.md)
- [Commits](https://github.com/DeepLcom/deepl-python/compare/v1.25.0...v1.26.0)

---
updated-dependencies:
- dependency-name: deepl
  dependency-version: 1.26.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-08 23:07:44 +00:00
dependabot[bot]
d1e20fa776 Bump coverage from 7.12.0 to 7.13.0
Bumps [coverage](https://github.com/coveragepy/coveragepy) from 7.12.0 to 7.13.0.
- [Release notes](https://github.com/coveragepy/coveragepy/releases)
- [Changelog](https://github.com/coveragepy/coveragepy/blob/main/CHANGES.rst)
- [Commits](https://github.com/coveragepy/coveragepy/compare/7.12.0...7.13.0)

---
updated-dependencies:
- dependency-name: coverage
  dependency-version: 7.13.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-08 23:07:36 +00:00
dependabot[bot]
1f679e731d Bump urllib3 from 2.6.0 to 2.6.1
Bumps [urllib3](https://github.com/urllib3/urllib3) from 2.6.0 to 2.6.1.
- [Release notes](https://github.com/urllib3/urllib3/releases)
- [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst)
- [Commits](https://github.com/urllib3/urllib3/compare/2.6.0...2.6.1)

---
updated-dependencies:
- dependency-name: urllib3
  dependency-version: 2.6.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-08 23:07:32 +00:00
José Manuel Delicado
7659ee9a9e Merge pull request #874 from MCV-Software/dependabot/pip/pytest-9.0.2
Bump pytest from 9.0.1 to 9.0.2
2025-12-08 10:05:27 +01:00
José Manuel Delicado
9939713075 Merge pull request #873 from MCV-Software/dependabot/pip/urllib3-2.6.0
Bump urllib3 from 2.5.0 to 2.6.0
2025-12-08 10:05:09 +01:00
José Manuel Delicado
170d4ebbd5 Merge pull request #872 from MCV-Software/dependabot/pip/pypubsub-4.0.7
Bump pypubsub from 4.0.3 to 4.0.7
2025-12-08 10:04:53 +01:00
dependabot[bot]
196a71fbd9 Bump pytest from 9.0.1 to 9.0.2
Bumps [pytest](https://github.com/pytest-dev/pytest) from 9.0.1 to 9.0.2.
- [Release notes](https://github.com/pytest-dev/pytest/releases)
- [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest/compare/9.0.1...9.0.2)

---
updated-dependencies:
- dependency-name: pytest
  dependency-version: 9.0.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-08 09:03:43 +00:00
dependabot[bot]
e48ff3d642 Bump urllib3 from 2.5.0 to 2.6.0
Bumps [urllib3](https://github.com/urllib3/urllib3) from 2.5.0 to 2.6.0.
- [Release notes](https://github.com/urllib3/urllib3/releases)
- [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst)
- [Commits](https://github.com/urllib3/urllib3/compare/2.5.0...2.6.0)

---
updated-dependencies:
- dependency-name: urllib3
  dependency-version: 2.6.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-08 09:03:38 +00:00
dependabot[bot]
39e25532d7 Bump pypubsub from 4.0.3 to 4.0.7
Bumps [pypubsub](https://github.com/schollii/pypubsub) from 4.0.3 to 4.0.7.
- [Release notes](https://github.com/schollii/pypubsub/releases)
- [Changelog](https://github.com/schollii/pypubsub/blob/master/docs/changelog.rst)
- [Commits](https://github.com/schollii/pypubsub/compare/v4.0.3...v4.0.7)

---
updated-dependencies:
- dependency-name: pypubsub
  dependency-version: 4.0.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-08 09:03:28 +00:00
José Manuel Delicado
25f563464b Merge pull request #870 from MCV-Software/dependabot/pip/cx-freeze-8.5.0
Some checks failed
Update translation files / update_catalogs (push) Failing after 24s
Bump cx-freeze from 8.4.1 to 8.5.0
2025-11-24 21:16:13 +01:00
dependabot[bot]
08bb5b41c9 Bump cx-freeze from 8.4.1 to 8.5.0
Bumps [cx-freeze](https://github.com/marcelotduarte/cx_Freeze) from 8.4.1 to 8.5.0.
- [Release notes](https://github.com/marcelotduarte/cx_Freeze/releases)
- [Changelog](https://github.com/marcelotduarte/cx_Freeze/blob/main/CHANGELOG.md)
- [Commits](https://github.com/marcelotduarte/cx_Freeze/compare/8.4.1...8.5.0)

---
updated-dependencies:
- dependency-name: cx-freeze
  dependency-version: 8.5.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-24 20:15:03 +00:00
José Manuel Delicado
486001a46c Merge pull request #871 from MCV-Software/dependabot/pip/coverage-7.12.0
Bump coverage from 7.11.3 to 7.12.0
2025-11-24 20:53:32 +01:00
dependabot[bot]
4e255b5f6e Bump coverage from 7.11.3 to 7.12.0
Bumps [coverage](https://github.com/coveragepy/coveragepy) from 7.11.3 to 7.12.0.
- [Release notes](https://github.com/coveragepy/coveragepy/releases)
- [Changelog](https://github.com/coveragepy/coveragepy/blob/main/CHANGES.rst)
- [Commits](https://github.com/coveragepy/coveragepy/compare/7.11.3...7.12.0)

---
updated-dependencies:
- dependency-name: coverage
  dependency-version: 7.12.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-24 19:37:03 +00:00
José Manuel Delicado
877040b086 Merge pull request #867 from MCV-Software/dependabot/pip/pytest-9.0.1
Some checks failed
Update translation files / update_catalogs (push) Has been cancelled
Bump pytest from 9.0.0 to 9.0.1
2025-11-18 07:26:51 +01:00
José Manuel Delicado
c294b967ae Merge pull request #868 from MCV-Software/dependabot/pip/types-python-dateutil-2.9.0.20251115
Bump types-python-dateutil from 2.9.0.20251108 to 2.9.0.20251115
2025-11-18 07:26:31 +01:00
José Manuel Delicado
16cc1fec8b Merge pull request #869 from MCV-Software/dependabot/pip/deepl-1.25.0
Bump deepl from 1.24.0 to 1.25.0
2025-11-18 07:26:11 +01:00
dependabot[bot]
fed6f6da60 Bump deepl from 1.24.0 to 1.25.0
Bumps [deepl](https://github.com/DeepLcom/deepl-python) from 1.24.0 to 1.25.0.
- [Release notes](https://github.com/DeepLcom/deepl-python/releases)
- [Changelog](https://github.com/DeepLcom/deepl-python/blob/main/CHANGELOG.md)
- [Commits](https://github.com/DeepLcom/deepl-python/compare/v1.24.0...v1.25.0)

---
updated-dependencies:
- dependency-name: deepl
  dependency-version: 1.25.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-17 23:07:54 +00:00
dependabot[bot]
89160497e4 Bump types-python-dateutil from 2.9.0.20251108 to 2.9.0.20251115
Bumps [types-python-dateutil](https://github.com/typeshed-internal/stub_uploader) from 2.9.0.20251108 to 2.9.0.20251115.
- [Commits](https://github.com/typeshed-internal/stub_uploader/commits)

---
updated-dependencies:
- dependency-name: types-python-dateutil
  dependency-version: 2.9.0.20251115
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-17 23:07:49 +00:00
dependabot[bot]
beb621de97 Bump pytest from 9.0.0 to 9.0.1
Bumps [pytest](https://github.com/pytest-dev/pytest) from 9.0.0 to 9.0.1.
- [Release notes](https://github.com/pytest-dev/pytest/releases)
- [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest/compare/9.0.0...9.0.1)

---
updated-dependencies:
- dependency-name: pytest
  dependency-version: 9.0.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-17 23:07:43 +00:00
José Manuel Delicado
b46fadd375 Merge pull request #864 from MCV-Software/dependabot/pip/numpy-2.3.5
Bump numpy from 2.3.4 to 2.3.5
2025-11-17 22:40:11 +01:00
José Manuel Delicado
f0fbfab3b3 Merge pull request #865 from MCV-Software/dependabot/pip/certifi-2025.11.12
Bump certifi from 2025.10.5 to 2025.11.12
2025-11-17 22:39:52 +01:00
José Manuel Delicado
f83a3e16ce Merge pull request #866 from MCV-Software/dependabot/pip/coverage-7.11.3
Bump coverage from 7.11.0 to 7.11.3
2025-11-17 22:39:34 +01:00
dependabot[bot]
b309d23c8b Bump coverage from 7.11.0 to 7.11.3
Bumps [coverage](https://github.com/coveragepy/coveragepy) from 7.11.0 to 7.11.3.
- [Release notes](https://github.com/coveragepy/coveragepy/releases)
- [Changelog](https://github.com/coveragepy/coveragepy/blob/main/CHANGES.rst)
- [Commits](https://github.com/coveragepy/coveragepy/compare/7.11.0...7.11.3)

---
updated-dependencies:
- dependency-name: coverage
  dependency-version: 7.11.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-17 21:38:43 +00:00
dependabot[bot]
d879a9de5c Bump certifi from 2025.10.5 to 2025.11.12
Bumps [certifi](https://github.com/certifi/python-certifi) from 2025.10.5 to 2025.11.12.
- [Commits](https://github.com/certifi/python-certifi/compare/2025.10.05...2025.11.12)

---
updated-dependencies:
- dependency-name: certifi
  dependency-version: 2025.11.12
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-17 21:38:38 +00:00
dependabot[bot]
b419f8fb23 Bump numpy from 2.3.4 to 2.3.5
Bumps [numpy](https://github.com/numpy/numpy) from 2.3.4 to 2.3.5.
- [Release notes](https://github.com/numpy/numpy/releases)
- [Changelog](https://github.com/numpy/numpy/blob/main/doc/RELEASE_WALKTHROUGH.rst)
- [Commits](https://github.com/numpy/numpy/compare/v2.3.4...v2.3.5)

---
updated-dependencies:
- dependency-name: numpy
  dependency-version: 2.3.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-17 21:38:36 +00:00
José Manuel Delicado
7a4a454376 Merge pull request #861 from MCV-Software/dependabot/pip/pytest-9.0.0
Some checks failed
Update translation files / update_catalogs (push) Has been cancelled
Bump pytest from 8.4.2 to 9.0.0
2025-11-11 07:33:08 +01:00
José Manuel Delicado
e94ea49142 Merge pull request #862 from MCV-Software/dependabot/pip/types-python-dateutil-2.9.0.20251108
Bump types-python-dateutil from 2.9.0.20251008 to 2.9.0.20251108
2025-11-11 07:32:31 +01:00
José Manuel Delicado
56415c7506 Merge pull request #863 from MCV-Software/dependabot/pip/deepl-1.24.0
Bump deepl from 1.23.0 to 1.24.0
2025-11-11 07:32:06 +01:00
dependabot[bot]
c8879e7998 Bump deepl from 1.23.0 to 1.24.0
Bumps [deepl](https://github.com/DeepLcom/deepl-python) from 1.23.0 to 1.24.0.
- [Release notes](https://github.com/DeepLcom/deepl-python/releases)
- [Changelog](https://github.com/DeepLcom/deepl-python/blob/main/CHANGELOG.md)
- [Commits](https://github.com/DeepLcom/deepl-python/compare/v1.23.0...v1.24.0)

---
updated-dependencies:
- dependency-name: deepl
  dependency-version: 1.24.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-10 23:08:42 +00:00
dependabot[bot]
8fb30edf31 Bump types-python-dateutil from 2.9.0.20251008 to 2.9.0.20251108
Bumps [types-python-dateutil](https://github.com/typeshed-internal/stub_uploader) from 2.9.0.20251008 to 2.9.0.20251108.
- [Commits](https://github.com/typeshed-internal/stub_uploader/commits)

---
updated-dependencies:
- dependency-name: types-python-dateutil
  dependency-version: 2.9.0.20251108
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-10 23:08:36 +00:00
dependabot[bot]
3db6ee3a17 Bump pytest from 8.4.2 to 9.0.0
Bumps [pytest](https://github.com/pytest-dev/pytest) from 8.4.2 to 9.0.0.
- [Release notes](https://github.com/pytest-dev/pytest/releases)
- [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest/compare/8.4.2...9.0.0)

---
updated-dependencies:
- dependency-name: pytest
  dependency-version: 9.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-10 23:08:33 +00:00
e4520a14e8 Merge pull request #842 from Mohamed00/evenMoreShortcuts
Some checks failed
Update translation files / update_catalogs (push) Has been cancelled
Added and improved more shortcuts
2025-11-07 09:54:14 -06:00
a13e1f1f10 Added support for reading quoted posts properly 2025-11-07 09:08:39 -06:00
377578dbe2 Added a better HTML filter to remove elements with certain classes 2025-11-07 09:01:11 -06:00
c436fbc944 Merge pull request #859 from MCV-Software/claude/init-project-011CUrnQFTRmB7UfoFRcsihk
mastodon: Adds support to edit posts
2025-11-07 07:56:28 -06:00
3af372973d fix: avoid passing params that are not editable to API Calls; display polls as attachments in post edit dialog, warns user about vote resetting when editing a post with poll 2025-11-06 17:39:55 -06:00
de837e15b9 fix: Remove parameters that are not editable from post edit function 2025-11-06 17:37:05 -06:00
49eaa57027 Merge branch 'next-gen' of github.com:mcv-software/twblue into next-gen 2025-11-06 10:01:09 -06:00
Claude
977de1332a Implement post editing functionality for Mastodon
Add ability to edit posts in Mastodon with full support for:
- Editing post text and content warnings
- Re-uploading or keeping existing media attachments
- Editing poll options (for posts with polls)
- Modifying visibility and language settings
- All features available through web interface

Changes:
- Add edit_post() method in Mastodon session to handle API calls
- Create editPost dialog class that loads existing post data
- Add edit_status() method to buffer controllers
- Add Edit menu item to base and notification menus
- Register edit_post action in all keymaps (no default key assigned)
- Add edit_post() action handler in main controller

The edit option is only enabled for the user's own posts (not boosts).
Users can access the feature through the context menu or by assigning
a keyboard shortcut in the keymap editor.
2025-11-06 14:37:12 +00:00
Claude
cbafb7da69 Add CLAUDE.md documentation for AI-assisted development
This file provides guidance to Claude Code when working in this repository,
including:
- Development commands (run, build, test, translate)
- High-level architecture overview (MVC pattern, sessions, buffers, controllers)
- Key design patterns (compose functions, decorators, pub/sub events)
- Important conventions and caveats for working with the codebase
2025-11-06 14:25:58 +00:00
José Manuel Delicado
966d43ca29 Merge pull request #855 from MCV-Software/dependabot/pip/markdown-3.10
Bump markdown from 3.9 to 3.10
2025-11-04 07:25:47 +01:00
José Manuel Delicado
193f9bfb66 Merge pull request #856 from MCV-Software/dependabot/pip/wxpython-4.2.4
Bump wxpython from 4.2.3 to 4.2.4
2025-11-04 07:25:32 +01:00
José Manuel Delicado
2f77675562 Merge pull request #857 from MCV-Software/dependabot/pip/psutil-7.1.3
Bump psutil from 7.1.0 to 7.1.3
2025-11-04 07:25:12 +01:00
dependabot[bot]
b13dd986fc Bump psutil from 7.1.0 to 7.1.3
Bumps [psutil](https://github.com/giampaolo/psutil) from 7.1.0 to 7.1.3.
- [Changelog](https://github.com/giampaolo/psutil/blob/master/HISTORY.rst)
- [Commits](https://github.com/giampaolo/psutil/compare/release-7.1.0...release-7.1.3)

---
updated-dependencies:
- dependency-name: psutil
  dependency-version: 7.1.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-03 23:07:55 +00:00
dependabot[bot]
4269124758 Bump wxpython from 4.2.3 to 4.2.4
Bumps [wxpython](https://github.com/wxWidgets/Phoenix) from 4.2.3 to 4.2.4.
- [Release notes](https://github.com/wxWidgets/Phoenix/releases)
- [Changelog](https://github.com/wxWidgets/Phoenix/blob/master/CHANGES.rst)
- [Commits](https://github.com/wxWidgets/Phoenix/compare/wxPython-4.2.3...wxPython-4.2.4)

---
updated-dependencies:
- dependency-name: wxpython
  dependency-version: 4.2.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-03 23:07:49 +00:00
dependabot[bot]
6be0e82610 Bump markdown from 3.9 to 3.10
Bumps [markdown](https://github.com/Python-Markdown/markdown) from 3.9 to 3.10.
- [Release notes](https://github.com/Python-Markdown/markdown/releases)
- [Changelog](https://github.com/Python-Markdown/markdown/blob/master/docs/changelog.md)
- [Commits](https://github.com/Python-Markdown/markdown/compare/3.9.0...3.10.0)

---
updated-dependencies:
- dependency-name: markdown
  dependency-version: '3.10'
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-03 23:07:44 +00:00
José Manuel Delicado
a6e8f2969a Merge pull request #852 from MCV-Software/dependabot/pip/iniconfig-2.3.0
Some checks failed
Update translation files / update_catalogs (push) Failing after 1m10s
Bump iniconfig from 2.1.0 to 2.3.0
2025-10-28 07:33:39 +01:00
José Manuel Delicado
eb3a038a60 Merge pull request #853 from MCV-Software/dependabot/pip/charset-normalizer-3.4.4
Bump charset-normalizer from 3.4.3 to 3.4.4
2025-10-28 07:33:18 +01:00
José Manuel Delicado
bb60eaa2fc Merge pull request #854 from MCV-Software/dependabot/pip/numpy-2.3.4
Bump numpy from 2.3.3 to 2.3.4
2025-10-28 07:33:00 +01:00
dependabot[bot]
46654ef054 Bump numpy from 2.3.3 to 2.3.4
Bumps [numpy](https://github.com/numpy/numpy) from 2.3.3 to 2.3.4.
- [Release notes](https://github.com/numpy/numpy/releases)
- [Changelog](https://github.com/numpy/numpy/blob/main/doc/RELEASE_WALKTHROUGH.rst)
- [Commits](https://github.com/numpy/numpy/compare/v2.3.3...v2.3.4)

---
updated-dependencies:
- dependency-name: numpy
  dependency-version: 2.3.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-28 00:12:41 +00:00
dependabot[bot]
981d98e5e2 Bump charset-normalizer from 3.4.3 to 3.4.4
Bumps [charset-normalizer](https://github.com/jawah/charset_normalizer) from 3.4.3 to 3.4.4.
- [Release notes](https://github.com/jawah/charset_normalizer/releases)
- [Changelog](https://github.com/jawah/charset_normalizer/blob/master/CHANGELOG.md)
- [Commits](https://github.com/jawah/charset_normalizer/compare/3.4.3...3.4.4)

---
updated-dependencies:
- dependency-name: charset-normalizer
  dependency-version: 3.4.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-27 23:34:00 +00:00
dependabot[bot]
7eda523520 Bump iniconfig from 2.1.0 to 2.3.0
Bumps [iniconfig](https://github.com/pytest-dev/iniconfig) from 2.1.0 to 2.3.0.
- [Release notes](https://github.com/pytest-dev/iniconfig/releases)
- [Changelog](https://github.com/pytest-dev/iniconfig/blob/main/CHANGELOG)
- [Commits](https://github.com/pytest-dev/iniconfig/compare/v2.1.0...v2.3.0)

---
updated-dependencies:
- dependency-name: iniconfig
  dependency-version: 2.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-27 23:30:59 +00:00
José Manuel Delicado
ea54406127 Merge pull request #849 from MCV-Software/dependabot/pip/arrow-1.4.0
Some checks failed
Update translation files / update_catalogs (push) Failing after 34s
Bump arrow from 1.3.0 to 1.4.0
2025-10-21 10:30:54 +02:00
José Manuel Delicado
1db78da82f Merge pull request #850 from MCV-Software/dependabot/pip/pillow-12.0.0
Bump pillow from 11.3.0 to 12.0.0
2025-10-21 10:30:11 +02:00
José Manuel Delicado
9c0b7f0c78 Merge pull request #851 from MCV-Software/dependabot/pip/coverage-7.11.0
Bump coverage from 7.10.7 to 7.11.0
2025-10-21 10:29:42 +02:00
dependabot[bot]
e62b4d9d77 Bump coverage from 7.10.7 to 7.11.0
Bumps [coverage](https://github.com/nedbat/coveragepy) from 7.10.7 to 7.11.0.
- [Release notes](https://github.com/nedbat/coveragepy/releases)
- [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst)
- [Commits](https://github.com/nedbat/coveragepy/compare/7.10.7...7.11.0)

---
updated-dependencies:
- dependency-name: coverage
  dependency-version: 7.11.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-20 23:08:43 +00:00
dependabot[bot]
a99f2e9e64 Bump pillow from 11.3.0 to 12.0.0
Bumps [pillow](https://github.com/python-pillow/Pillow) from 11.3.0 to 12.0.0.
- [Release notes](https://github.com/python-pillow/Pillow/releases)
- [Changelog](https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst)
- [Commits](https://github.com/python-pillow/Pillow/compare/11.3.0...12.0.0)

---
updated-dependencies:
- dependency-name: pillow
  dependency-version: 12.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-20 23:08:36 +00:00
dependabot[bot]
0ad32886e1 Bump arrow from 1.3.0 to 1.4.0
Bumps [arrow](https://github.com/arrow-py/arrow) from 1.3.0 to 1.4.0.
- [Release notes](https://github.com/arrow-py/arrow/releases)
- [Changelog](https://github.com/arrow-py/arrow/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/arrow-py/arrow/compare/1.3.0...1.4.0)

---
updated-dependencies:
- dependency-name: arrow
  dependency-version: 1.4.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-20 23:08:30 +00:00
Mohamed
b1bf2ea95f Updated shortcut for Bot account so it's not the same as bio. Also updated shortcut for posts 2025-10-20 17:18:12 -04:00
José Manuel Delicado
b62b83fa00 Merge pull request #846 from MCV-Software/dependabot/pip/types-python-dateutil-2.9.0.20251008
Some checks failed
Update translation files / update_catalogs (push) Has been cancelled
Bump types-python-dateutil from 2.9.0.20250822 to 2.9.0.20251008
2025-10-14 07:31:39 +02:00
José Manuel Delicado
bfd0d938e1 Merge pull request #847 from MCV-Software/dependabot/pip/deepl-1.23.0
Bump deepl from 1.22.0 to 1.23.0
2025-10-14 07:31:24 +02:00
José Manuel Delicado
4d2d044aa9 Merge pull request #848 from MCV-Software/dependabot/pip/idna-3.11
Bump idna from 3.10 to 3.11
2025-10-14 07:31:04 +02:00
dependabot[bot]
e8e1c4dedf Bump idna from 3.10 to 3.11
Bumps [idna](https://github.com/kjd/idna) from 3.10 to 3.11.
- [Release notes](https://github.com/kjd/idna/releases)
- [Changelog](https://github.com/kjd/idna/blob/master/HISTORY.rst)
- [Commits](https://github.com/kjd/idna/compare/v3.10...v3.11)

---
updated-dependencies:
- dependency-name: idna
  dependency-version: '3.11'
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-13 23:07:03 +00:00
dependabot[bot]
dd23074177 Bump deepl from 1.22.0 to 1.23.0
Bumps [deepl](https://github.com/DeepLcom/deepl-python) from 1.22.0 to 1.23.0.
- [Release notes](https://github.com/DeepLcom/deepl-python/releases)
- [Changelog](https://github.com/DeepLcom/deepl-python/blob/main/CHANGELOG.md)
- [Commits](https://github.com/DeepLcom/deepl-python/compare/v1.22.0...v1.23.0)

---
updated-dependencies:
- dependency-name: deepl
  dependency-version: 1.23.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-13 23:06:57 +00:00
dependabot[bot]
127cd5ddf3 Bump types-python-dateutil from 2.9.0.20250822 to 2.9.0.20251008
Bumps [types-python-dateutil](https://github.com/typeshed-internal/stub_uploader) from 2.9.0.20250822 to 2.9.0.20251008.
- [Commits](https://github.com/typeshed-internal/stub_uploader/commits)

---
updated-dependencies:
- dependency-name: types-python-dateutil
  dependency-version: 2.9.0.20251008
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-13 23:06:48 +00:00
José Manuel Delicado
cdeab8ffc6 Merge pull request #845 from MCV-Software/dependabot/pip/attrs-25.4.0
Some checks failed
Update translation files / update_catalogs (push) Failing after 5s
Bump attrs from 25.3.0 to 25.4.0
2025-10-07 07:40:59 +02:00
José Manuel Delicado
d2fc571100 Merge pull request #844 from MCV-Software/dependabot/pip/numpy-2.3.3
Bump numpy from 2.2.3 to 2.3.3
2025-10-07 07:40:46 +02:00
dependabot[bot]
4a27571e04 Bump attrs from 25.3.0 to 25.4.0
Bumps [attrs](https://github.com/sponsors/hynek) from 25.3.0 to 25.4.0.
- [Commits](https://github.com/sponsors/hynek/commits)

---
updated-dependencies:
- dependency-name: attrs
  dependency-version: 25.4.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-06 23:09:21 +00:00
dependabot[bot]
f3296d646e Bump numpy from 2.2.3 to 2.3.3
Bumps [numpy](https://github.com/numpy/numpy) from 2.2.3 to 2.3.3.
- [Release notes](https://github.com/numpy/numpy/releases)
- [Changelog](https://github.com/numpy/numpy/blob/main/doc/RELEASE_WALKTHROUGH.rst)
- [Commits](https://github.com/numpy/numpy/compare/v2.2.3...v2.3.3)

---
updated-dependencies:
- dependency-name: numpy
  dependency-version: 2.3.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-06 23:09:10 +00:00
José Manuel Delicado
8c0a863770 Merge pull request #843 from MCV-Software/dependabot/pip/certifi-2025.10.5
Bump certifi from 2025.8.3 to 2025.10.5
2025-10-06 07:58:31 +02:00
dependabot[bot]
2b8370a377 Bump certifi from 2025.8.3 to 2025.10.5
Bumps [certifi](https://github.com/certifi/python-certifi) from 2025.8.3 to 2025.10.5.
- [Commits](https://github.com/certifi/python-certifi/compare/2025.08.03...2025.10.05)

---
updated-dependencies:
- dependency-name: certifi
  dependency-version: 2025.10.5
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-06 05:56:53 +00:00
Mohamed
a4d5066156 Added shortcuts to the database manager and some more to the menu bar. Also fixed shortcuts for seeking so they're not both s 2025-10-02 16:26:50 -04:00
ebabace52d Merge pull request #841 from Mohamed00/keyboardShortcuts
Some checks failed
Update translation files / update_catalogs (push) Failing after 11s
Added more shortcuts
2025-10-02 13:26:23 -06:00
Mohamed
ed0cc67ba5 Added shortcut for restore template button 2025-10-02 13:31:21 -04:00
Mohamed
dce625547d Added keyboard shortcuts for user list and poll dialogs 2025-10-02 13:17:43 -04:00
José Manuel Delicado
06c370dfd6 Merge pull request #840 from MCV-Software/dependabot/pip/mastodon-py-2.1.4 2025-09-30 06:24:37 +02:00
dependabot[bot]
db83abdbd3 Bump mastodon-py from 2.1.3 to 2.1.4
Bumps [mastodon-py](https://github.com/halcy/Mastodon.py) from 2.1.3 to 2.1.4.
- [Release notes](https://github.com/halcy/Mastodon.py/releases)
- [Changelog](https://github.com/halcy/Mastodon.py/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/halcy/Mastodon.py/compare/v2.1.3...v2.1.4)

---
updated-dependencies:
- dependency-name: mastodon-py
  dependency-version: 2.1.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-30 01:11:46 +00:00
José Manuel Delicado
11a60c52f3 Merge pull request #839 from MCV-Software/dependabot/pip/cx-freeze-8.4.1
Some checks failed
Update translation files / update_catalogs (push) Failing after 7s
Bump cx-freeze from 8.4.0 to 8.4.1
2025-09-24 07:43:33 +02:00
dependabot[bot]
59f08b991e Bump cx-freeze from 8.4.0 to 8.4.1
Bumps [cx-freeze](https://github.com/marcelotduarte/cx_Freeze) from 8.4.0 to 8.4.1.
- [Release notes](https://github.com/marcelotduarte/cx_Freeze/releases)
- [Changelog](https://github.com/marcelotduarte/cx_Freeze/blob/main/CHANGELOG.md)
- [Commits](https://github.com/marcelotduarte/cx_Freeze/compare/8.4.0...8.4.1)

---
updated-dependencies:
- dependency-name: cx-freeze
  dependency-version: 8.4.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-24 05:42:34 +00:00
José Manuel Delicado
3c4e9a8fa7 Merge pull request #838 from MCV-Software/dependabot/pip/coverage-7.10.7
Bump coverage from 7.10.6 to 7.10.7
2025-09-23 07:29:52 +02:00
José Manuel Delicado
c632743db3 Merge pull request #837 from MCV-Software/dependabot/pip/psutil-7.1.0
Bump psutil from 7.0.0 to 7.1.0
2025-09-23 07:29:33 +02:00
dependabot[bot]
6511a3a889 Bump coverage from 7.10.6 to 7.10.7
Bumps [coverage](https://github.com/nedbat/coveragepy) from 7.10.6 to 7.10.7.
- [Release notes](https://github.com/nedbat/coveragepy/releases)
- [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst)
- [Commits](https://github.com/nedbat/coveragepy/compare/7.10.6...7.10.7)

---
updated-dependencies:
- dependency-name: coverage
  dependency-version: 7.10.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-22 23:07:26 +00:00
dependabot[bot]
66383b8bda Bump psutil from 7.0.0 to 7.1.0
Bumps [psutil](https://github.com/giampaolo/psutil) from 7.0.0 to 7.1.0.
- [Changelog](https://github.com/giampaolo/psutil/blob/master/HISTORY.rst)
- [Commits](https://github.com/giampaolo/psutil/compare/release-7.0.0...release-7.1.0)

---
updated-dependencies:
- dependency-name: psutil
  dependency-version: 7.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-22 23:07:18 +00:00
José Manuel Delicado
d24f89947e Merge pull request #836 from MCV-Software/dependabot/pip/pyenchant-3.3.0
Some checks failed
Update translation files / update_catalogs (push) Failing after 14s
Bump pyenchant from 3.2.2 to 3.3.0
2025-09-16 07:30:09 +02:00
dependabot[bot]
8a2d6631da Bump pyenchant from 3.2.2 to 3.3.0
Bumps [pyenchant](https://github.com/pyenchant/pyenchant) from 3.2.2 to 3.3.0.
- [Release notes](https://github.com/pyenchant/pyenchant/releases)
- [Commits](https://github.com/pyenchant/pyenchant/compare/v3.2.2...v3.3.0)

---
updated-dependencies:
- dependency-name: pyenchant
  dependency-version: 3.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-15 23:13:08 +00:00
José Manuel Delicado
95d0b575c8 Merge pull request #834 from MCV-Software/dependabot/pip/markdown-3.9
Some checks failed
Update translation files / update_catalogs (push) Failing after 11s
Bump markdown from 3.8.2 to 3.9
2025-09-09 07:31:16 +02:00
José Manuel Delicado
1e39ce69ef Merge pull request #835 from MCV-Software/dependabot/pip/pytest-8.4.2
Bump pytest from 8.4.1 to 8.4.2
2025-09-09 07:30:59 +02:00
dependabot[bot]
905b0fc255 Bump pytest from 8.4.1 to 8.4.2
Bumps [pytest](https://github.com/pytest-dev/pytest) from 8.4.1 to 8.4.2.
- [Release notes](https://github.com/pytest-dev/pytest/releases)
- [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest/compare/8.4.1...8.4.2)

---
updated-dependencies:
- dependency-name: pytest
  dependency-version: 8.4.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-08 23:08:17 +00:00
dependabot[bot]
3bf39d2349 Bump markdown from 3.8.2 to 3.9
Bumps [markdown](https://github.com/Python-Markdown/markdown) from 3.8.2 to 3.9.
- [Release notes](https://github.com/Python-Markdown/markdown/releases)
- [Changelog](https://github.com/Python-Markdown/markdown/blob/master/docs/changelog.md)
- [Commits](https://github.com/Python-Markdown/markdown/compare/3.8.2...3.9.0)

---
updated-dependencies:
- dependency-name: markdown
  dependency-version: '3.9'
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-08 23:08:09 +00:00
José Manuel Delicado
04b43993eb Merge pull request #833 from MCV-Software/dependabot/pip/mastodon-py-2.1.3
Some checks failed
Update translation files / update_catalogs (push) Failing after 59s
Bump mastodon-py from 2.1.2 to 2.1.3
2025-09-02 18:31:19 +02:00
dependabot[bot]
321b358a62 Bump mastodon-py from 2.1.2 to 2.1.3
Bumps [mastodon-py](https://github.com/halcy/Mastodon.py) from 2.1.2 to 2.1.3.
- [Release notes](https://github.com/halcy/Mastodon.py/releases)
- [Changelog](https://github.com/halcy/Mastodon.py/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/halcy/Mastodon.py/compare/v2.1.2...v2.1.3)

---
updated-dependencies:
- dependency-name: mastodon-py
  dependency-version: 2.1.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-02 16:28:11 +00:00
José Manuel Delicado
83d752c24d Merge pull request #832 from MCV-Software/dependabot/pip/coverage-7.10.6
Bump coverage from 7.10.5 to 7.10.6
2025-09-01 21:41:51 +02:00
dependabot[bot]
d8c0094003 Bump coverage from 7.10.5 to 7.10.6
Bumps [coverage](https://github.com/nedbat/coveragepy) from 7.10.5 to 7.10.6.
- [Release notes](https://github.com/nedbat/coveragepy/releases)
- [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst)
- [Commits](https://github.com/nedbat/coveragepy/compare/7.10.5...7.10.6)

---
updated-dependencies:
- dependency-name: coverage
  dependency-version: 7.10.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-01 19:40:48 +00:00
José Manuel Delicado
148e831624 Merge pull request #831 from MCV-Software/dependabot/pip/coverage-7.10.5
Some checks failed
Update translation files / update_catalogs (push) Failing after 13s
Bump coverage from 7.10.4 to 7.10.5
2025-08-26 19:49:34 +02:00
dependabot[bot]
5112c309ea Bump coverage from 7.10.4 to 7.10.5
Bumps [coverage](https://github.com/nedbat/coveragepy) from 7.10.4 to 7.10.5.
- [Release notes](https://github.com/nedbat/coveragepy/releases)
- [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst)
- [Commits](https://github.com/nedbat/coveragepy/compare/7.10.4...7.10.5)

---
updated-dependencies:
- dependency-name: coverage
  dependency-version: 7.10.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-26 17:36:21 +00:00
José Manuel Delicado
a0642853e8 Merge pull request #830 from MCV-Software/dependabot/pip/mastodon-py-2.1.2
Bump mastodon-py from 2.1.0 to 2.1.2
2025-08-26 18:36:59 +02:00
dependabot[bot]
0a4f2e1936 Bump mastodon-py from 2.1.0 to 2.1.2
Bumps [mastodon-py](https://github.com/halcy/Mastodon.py) from 2.1.0 to 2.1.2.
- [Release notes](https://github.com/halcy/Mastodon.py/releases)
- [Changelog](https://github.com/halcy/Mastodon.py/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/halcy/Mastodon.py/compare/v2.1.0...v2.1.2)

---
updated-dependencies:
- dependency-name: mastodon-py
  dependency-version: 2.1.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-26 16:17:36 +00:00
José Manuel Delicado
3ae3a304a1 Merge pull request #829 from MCV-Software/dependabot/pip/types-python-dateutil-2.9.0.20250822
Bump types-python-dateutil from 2.9.0.20250809 to 2.9.0.20250822
2025-08-26 17:36:34 +02:00
dependabot[bot]
23e3327029 Bump types-python-dateutil from 2.9.0.20250809 to 2.9.0.20250822
Bumps [types-python-dateutil](https://github.com/typeshed-internal/stub_uploader) from 2.9.0.20250809 to 2.9.0.20250822.
- [Commits](https://github.com/typeshed-internal/stub_uploader/commits)

---
updated-dependencies:
- dependency-name: types-python-dateutil
  dependency-version: 2.9.0.20250822
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-26 15:31:52 +00:00
José Manuel Delicado
5fd45b5343 Merge pull request #828 from MCV-Software/dependabot/pip/coverage-7.10.4
Some checks failed
Update translation files / update_catalogs (push) Failing after 1m39s
Bump coverage from 7.10.3 to 7.10.4
2025-08-19 12:24:59 +02:00
dependabot[bot]
706c863cd8 Bump coverage from 7.10.3 to 7.10.4
Bumps [coverage](https://github.com/nedbat/coveragepy) from 7.10.3 to 7.10.4.
- [Release notes](https://github.com/nedbat/coveragepy/releases)
- [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst)
- [Commits](https://github.com/nedbat/coveragepy/compare/7.10.3...7.10.4)

---
updated-dependencies:
- dependency-name: coverage
  dependency-version: 7.10.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-19 10:23:04 +00:00
José Manuel Delicado
a7942c4ffe Merge pull request #827 from MCV-Software/dependabot/pip/mastodon-py-2.1.0
Bump mastodon-py from 2.0.1 to 2.1.0
2025-08-19 12:16:12 +02:00
dependabot[bot]
7a4d0b9bf1 Bump mastodon-py from 2.0.1 to 2.1.0
Bumps [mastodon-py](https://github.com/halcy/Mastodon.py) from 2.0.1 to 2.1.0.
- [Release notes](https://github.com/halcy/Mastodon.py/releases)
- [Changelog](https://github.com/halcy/Mastodon.py/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/halcy/Mastodon.py/compare/v2.0.1...v2.1.0)

---
updated-dependencies:
- dependency-name: mastodon-py
  dependency-version: 2.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-19 10:14:24 +00:00
José Manuel Delicado
db8983e469 Merge pull request #825 from MCV-Software/dependabot/pip/requests-2.32.5
Bump requests from 2.32.4 to 2.32.5
2025-08-19 12:05:20 +02:00
José Manuel Delicado
1393813c4e Merge pull request #826 from MCV-Software/dependabot/pip/blurhash-1.1.5
Bump blurhash from 1.1.4 to 1.1.5
2025-08-19 12:05:02 +02:00
dependabot[bot]
4ee3363140 Bump blurhash from 1.1.4 to 1.1.5
Bumps [blurhash](https://github.com/halcy/blurhash-python) from 1.1.4 to 1.1.5.
- [Release notes](https://github.com/halcy/blurhash-python/releases)
- [Commits](https://github.com/halcy/blurhash-python/commits/v1.1.5)

---
updated-dependencies:
- dependency-name: blurhash
  dependency-version: 1.1.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-19 09:20:17 +00:00
dependabot[bot]
5dad01130f Bump requests from 2.32.4 to 2.32.5
Bumps [requests](https://github.com/psf/requests) from 2.32.4 to 2.32.5.
- [Release notes](https://github.com/psf/requests/releases)
- [Changelog](https://github.com/psf/requests/blob/main/HISTORY.md)
- [Commits](https://github.com/psf/requests/compare/v2.32.4...v2.32.5)

---
updated-dependencies:
- dependency-name: requests
  dependency-version: 2.32.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-19 09:13:21 +00:00
José Manuel Delicado
a5ba726230 Merge pull request #824 from MCV-Software/dependabot/pip/cx-freeze-8.4.0
Some checks failed
Update translation files / update_catalogs (push) Has been cancelled
Bump cx-freeze from 8.3.0 to 8.4.0
2025-08-12 17:05:46 +02:00
dependabot[bot]
4070c1bb43 Bump cx-freeze from 8.3.0 to 8.4.0
Bumps [cx-freeze](https://github.com/marcelotduarte/cx_Freeze) from 8.3.0 to 8.4.0.
- [Release notes](https://github.com/marcelotduarte/cx_Freeze/releases)
- [Changelog](https://github.com/marcelotduarte/cx_Freeze/blob/main/CHANGELOG.md)
- [Commits](https://github.com/marcelotduarte/cx_Freeze/compare/8.3.0...8.4.0)

---
updated-dependencies:
- dependency-name: cx-freeze
  dependency-version: 8.4.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-12 15:04:43 +00:00
José Manuel Delicado
73d9474679 Merge pull request #823 from MCV-Software/dependabot/pip/charset-normalizer-3.4.3
Bump charset-normalizer from 3.4.2 to 3.4.3
2025-08-12 12:39:13 +02:00
dependabot[bot]
2bf1c68492 Bump charset-normalizer from 3.4.2 to 3.4.3
Bumps [charset-normalizer](https://github.com/jawah/charset_normalizer) from 3.4.2 to 3.4.3.
- [Release notes](https://github.com/jawah/charset_normalizer/releases)
- [Changelog](https://github.com/jawah/charset_normalizer/blob/master/CHANGELOG.md)
- [Commits](https://github.com/jawah/charset_normalizer/compare/3.4.2...3.4.3)

---
updated-dependencies:
- dependency-name: charset-normalizer
  dependency-version: 3.4.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-12 10:04:56 +00:00
José Manuel Delicado
f60beb3eab Merge pull request #821 from MCV-Software/dependabot/pip/coverage-7.10.3
Bump coverage from 7.10.2 to 7.10.3
2025-08-12 12:02:00 +02:00
José Manuel Delicado
f00029a154 Merge pull request #822 from MCV-Software/dependabot/pip/types-python-dateutil-2.9.0.20250809
Bump types-python-dateutil from 2.9.0.20250708 to 2.9.0.20250809
2025-08-12 12:01:26 +02:00
dependabot[bot]
7ed053c929 Bump types-python-dateutil from 2.9.0.20250708 to 2.9.0.20250809
Bumps [types-python-dateutil](https://github.com/typeshed-internal/stub_uploader) from 2.9.0.20250708 to 2.9.0.20250809.
- [Commits](https://github.com/typeshed-internal/stub_uploader/commits)

---
updated-dependencies:
- dependency-name: types-python-dateutil
  dependency-version: 2.9.0.20250809
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-12 09:52:14 +00:00
dependabot[bot]
0de60d085a Bump coverage from 7.10.2 to 7.10.3
Bumps [coverage](https://github.com/nedbat/coveragepy) from 7.10.2 to 7.10.3.
- [Release notes](https://github.com/nedbat/coveragepy/releases)
- [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst)
- [Commits](https://github.com/nedbat/coveragepy/compare/7.10.2...7.10.3)

---
updated-dependencies:
- dependency-name: coverage
  dependency-version: 7.10.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-12 08:38:36 +00:00
zvonimir stanecic
e8443cd526 Translated using Weblate (Polish)
Currently translated at 76.6% (543 of 708 strings)

Translation: TWBlue/TWBlue
Translate-URL: https://weblate.mcvsoftware.com/projects/twblue/twblue/pl/
2025-08-10 16:08:18 +00:00
José Manuel Delicado
12981ee707 Merge pull request #820 from MCV-Software/dependabot/pip/coverage-7.10.2
Some checks failed
Update translation files / update_catalogs (push) Has been cancelled
Bump coverage from 7.10.1 to 7.10.2
2025-08-05 13:12:44 +02:00
dependabot[bot]
3d1b9b9c5e Bump coverage from 7.10.1 to 7.10.2
Bumps [coverage](https://github.com/nedbat/coveragepy) from 7.10.1 to 7.10.2.
- [Release notes](https://github.com/nedbat/coveragepy/releases)
- [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst)
- [Commits](https://github.com/nedbat/coveragepy/compare/7.10.1...7.10.2)

---
updated-dependencies:
- dependency-name: coverage
  dependency-version: 7.10.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-05 10:51:40 +00:00
José Manuel Delicado
dd3d1308b7 Merge pull request #819 from MCV-Software/dependabot/pip/certifi-2025.8.3
Bump certifi from 2025.7.14 to 2025.8.3
2025-08-05 09:20:06 +02:00
dependabot[bot]
0fdca4d842 Bump certifi from 2025.7.14 to 2025.8.3
Bumps [certifi](https://github.com/certifi/python-certifi) from 2025.7.14 to 2025.8.3.
- [Commits](https://github.com/certifi/python-certifi/compare/2025.07.14...2025.08.03)

---
updated-dependencies:
- dependency-name: certifi
  dependency-version: 2025.8.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-05 07:14:56 +00:00
José Manuel Delicado
985e5d42ea Merge pull request #818 from MCV-Software/dependabot/pip/coverage-7.10.1
Some checks failed
Update translation files / update_catalogs (push) Failing after 54s
Bump coverage from 7.9.2 to 7.10.1
2025-07-29 08:42:29 +02:00
dependabot[bot]
80f44a99ca Bump coverage from 7.9.2 to 7.10.1
Bumps [coverage](https://github.com/nedbat/coveragepy) from 7.9.2 to 7.10.1.
- [Release notes](https://github.com/nedbat/coveragepy/releases)
- [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst)
- [Commits](https://github.com/nedbat/coveragepy/compare/7.9.2...7.10.1)

---
updated-dependencies:
- dependency-name: coverage
  dependency-version: 7.10.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-29 01:50:50 +00:00
José Manuel Delicado
04692fb708 Merge pull request #815 from MCV-Software/dependabot/pip/types-python-dateutil-2.9.0.20250708
Some checks failed
Update translation files / update_catalogs (push) Failing after 1m40s
Bump types-python-dateutil from 2.9.0.20250516 to 2.9.0.20250708
2025-07-15 07:35:42 +02:00
José Manuel Delicado
3cb9a8983d Merge pull request #816 from MCV-Software/dependabot/pip/certifi-2025.7.14
Bump certifi from 2025.6.15 to 2025.7.14
2025-07-15 07:35:22 +02:00
José Manuel Delicado
29dfa709ef Merge pull request #817 from MCV-Software/dependabot/pip/pywin32-311
Bump pywin32 from 310 to 311
2025-07-15 07:35:05 +02:00
dependabot[bot]
7dbfeeced9 Bump pywin32 from 310 to 311
---
updated-dependencies:
- dependency-name: pywin32
  dependency-version: '311'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-15 05:16:19 +00:00
dependabot[bot]
655828946c Bump certifi from 2025.6.15 to 2025.7.14
---
updated-dependencies:
- dependency-name: certifi
  dependency-version: 2025.7.14
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-15 05:06:24 +00:00
dependabot[bot]
ca91fe1cbe Bump types-python-dateutil from 2.9.0.20250516 to 2.9.0.20250708
---
updated-dependencies:
- dependency-name: types-python-dateutil
  dependency-version: 2.9.0.20250708
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-15 04:46:28 +00:00
José Manuel Delicado
c2ce5bde82 Merge pull request #814 from MCV-Software/dependabot/pip/coverage-7.9.2
Some checks failed
Update translation files / update_catalogs (push) Failing after 22s
Bump coverage from 7.9.1 to 7.9.2
2025-07-07 17:55:54 +02:00
dependabot[bot]
4bb65fe624 Bump coverage from 7.9.1 to 7.9.2
---
updated-dependencies:
- dependency-name: coverage
  dependency-version: 7.9.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-07 15:55:02 +00:00
José Manuel Delicado
28bcb0f52f Merge pull request #813 from MCV-Software/dependabot/pip/pillow-11.3.0
Some checks failed
Update translation files / update_catalogs (push) Failing after 10m55s
Bump pillow from 11.2.1 to 11.3.0
2025-07-01 21:35:45 +02:00
dependabot[bot]
49a8cd4e65 Bump pillow from 11.2.1 to 11.3.0
Bumps [pillow](https://github.com/python-pillow/Pillow) from 11.2.1 to 11.3.0.
- [Release notes](https://github.com/python-pillow/Pillow/releases)
- [Changelog](https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst)
- [Commits](https://github.com/python-pillow/Pillow/compare/11.2.1...11.3.0)

---
updated-dependencies:
- dependency-name: pillow
  dependency-version: 11.3.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-01 17:31:41 +00:00
José Manuel Delicado
e428b41d85 Merge pull request #810 from MCV-Software/dependabot/pip/pytest-8.4.1
Some checks failed
Update translation files / update_catalogs (push) Failing after 21s
Bump pytest from 8.4.0 to 8.4.1
2025-06-24 07:31:15 +02:00
José Manuel Delicado
a929db05d2 Merge pull request #811 from MCV-Software/dependabot/pip/oauthlib-3.3.1
Bump oauthlib from 3.2.2 to 3.3.1
2025-06-24 07:30:56 +02:00
José Manuel Delicado
a624a97d14 Merge pull request #812 from MCV-Software/dependabot/pip/markdown-3.8.2
Bump markdown from 3.8 to 3.8.2
2025-06-24 07:30:35 +02:00
dependabot[bot]
60ac77adf5 Bump markdown from 3.8 to 3.8.2
Bumps [markdown](https://github.com/Python-Markdown/markdown) from 3.8 to 3.8.2.
- [Release notes](https://github.com/Python-Markdown/markdown/releases)
- [Changelog](https://github.com/Python-Markdown/markdown/blob/master/docs/changelog.md)
- [Commits](https://github.com/Python-Markdown/markdown/compare/3.8...3.8.2)

---
updated-dependencies:
- dependency-name: markdown
  dependency-version: 3.8.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-24 02:04:17 +00:00
dependabot[bot]
5cd5179c3e Bump oauthlib from 3.2.2 to 3.3.1
Bumps [oauthlib](https://github.com/oauthlib/oauthlib) from 3.2.2 to 3.3.1.
- [Release notes](https://github.com/oauthlib/oauthlib/releases)
- [Changelog](https://github.com/oauthlib/oauthlib/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/oauthlib/oauthlib/compare/v3.2.2...v3.3.1)

---
updated-dependencies:
- dependency-name: oauthlib
  dependency-version: 3.3.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-24 01:20:40 +00:00
dependabot[bot]
6c9ef7ea54 Bump pytest from 8.4.0 to 8.4.1
Bumps [pytest](https://github.com/pytest-dev/pytest) from 8.4.0 to 8.4.1.
- [Release notes](https://github.com/pytest-dev/pytest/releases)
- [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest/compare/8.4.0...8.4.1)

---
updated-dependencies:
- dependency-name: pytest
  dependency-version: 8.4.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-24 01:11:24 +00:00
José Manuel Delicado
6872f4fd1c Merge pull request #809 from MCV-Software/dependabot/pip/urllib3-2.5.0
Some checks failed
Update translation files / update_catalogs (push) Failing after 22s
Bump urllib3 from 2.4.0 to 2.5.0
2025-06-19 21:12:18 +02:00
dependabot[bot]
a45e7dd7eb Bump urllib3 from 2.4.0 to 2.5.0
Bumps [urllib3](https://github.com/urllib3/urllib3) from 2.4.0 to 2.5.0.
- [Release notes](https://github.com/urllib3/urllib3/releases)
- [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst)
- [Commits](https://github.com/urllib3/urllib3/compare/2.4.0...2.5.0)

---
updated-dependencies:
- dependency-name: urllib3
  dependency-version: 2.5.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-19 05:27:02 +00:00
José Manuel Delicado
ea8d8a9296 Merge pull request #808 from MCV-Software/dependabot/pip/coverage-7.9.1
Bump coverage from 7.8.2 to 7.9.1
2025-06-16 21:55:40 +02:00
José Manuel Delicado
66bc366da3 Merge pull request #807 from MCV-Software/dependabot/pip/certifi-2025.6.15
Bump certifi from 2025.4.26 to 2025.6.15
2025-06-16 21:55:16 +02:00
dependabot[bot]
267875c70a Bump coverage from 7.8.2 to 7.9.1
Bumps [coverage](https://github.com/nedbat/coveragepy) from 7.8.2 to 7.9.1.
- [Release notes](https://github.com/nedbat/coveragepy/releases)
- [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst)
- [Commits](https://github.com/nedbat/coveragepy/compare/7.8.2...7.9.1)

---
updated-dependencies:
- dependency-name: coverage
  dependency-version: 7.9.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-16 19:54:39 +00:00
dependabot[bot]
e68ba5778a Bump certifi from 2025.4.26 to 2025.6.15
Bumps [certifi](https://github.com/certifi/python-certifi) from 2025.4.26 to 2025.6.15.
- [Commits](https://github.com/certifi/python-certifi/compare/2025.04.26...2025.06.15)

---
updated-dependencies:
- dependency-name: certifi
  dependency-version: 2025.6.15
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-16 19:54:30 +00:00
José Manuel Delicado
82bcf1dbbf Merge pull request #806 from MCV-Software/dependabot/pip/requests-2.32.4
Some checks failed
Update translation files / update_catalogs (push) Failing after 23s
Bump requests from 2.32.3 to 2.32.4
2025-06-10 07:31:32 +02:00
José Manuel Delicado
4779c1d004 Merge pull request #805 from MCV-Software/dependabot/pip/zipp-3.23.0
Bump zipp from 3.22.0 to 3.23.0
2025-06-10 07:31:14 +02:00
dependabot[bot]
e295e60fc0 Bump requests from 2.32.3 to 2.32.4
Bumps [requests](https://github.com/psf/requests) from 2.32.3 to 2.32.4.
- [Release notes](https://github.com/psf/requests/releases)
- [Changelog](https://github.com/psf/requests/blob/main/HISTORY.md)
- [Commits](https://github.com/psf/requests/compare/v2.32.3...v2.32.4)

---
updated-dependencies:
- dependency-name: requests
  dependency-version: 2.32.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-09 23:34:45 +00:00
dependabot[bot]
e383f15ee8 Bump zipp from 3.22.0 to 3.23.0
Bumps [zipp](https://github.com/jaraco/zipp) from 3.22.0 to 3.23.0.
- [Release notes](https://github.com/jaraco/zipp/releases)
- [Changelog](https://github.com/jaraco/zipp/blob/main/NEWS.rst)
- [Commits](https://github.com/jaraco/zipp/compare/v3.22.0...v3.23.0)

---
updated-dependencies:
- dependency-name: zipp
  dependency-version: 3.23.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-09 23:29:40 +00:00
José Manuel Delicado
aacbdabee3 Merge pull request #803 from MCV-Software/dependabot/pip/pytest-8.4.0
Some checks failed
Update translation files / update_catalogs (push) Failing after 23s
Bump pytest from 8.3.5 to 8.4.0
2025-06-03 07:33:03 +02:00
dependabot[bot]
f290bd80f1 Bump pytest from 8.3.5 to 8.4.0
Bumps [pytest](https://github.com/pytest-dev/pytest) from 8.3.5 to 8.4.0.
- [Release notes](https://github.com/pytest-dev/pytest/releases)
- [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest/compare/8.3.5...8.4.0)

---
updated-dependencies:
- dependency-name: pytest
  dependency-version: 8.4.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-02 23:42:11 +00:00
José Manuel Delicado
4a0dbd2140 Merge pull request #801 from MCV-Software/dependabot/pip/zipp-3.22.0
Some checks failed
Update translation files / update_catalogs (push) Failing after 21s
Bump zipp from 3.21.0 to 3.22.0
2025-05-27 07:27:50 +02:00
José Manuel Delicado
4de969d9cd Merge pull request #802 from MCV-Software/dependabot/pip/coverage-7.8.2
Bump coverage from 7.8.0 to 7.8.2
2025-05-27 07:27:32 +02:00
dependabot[bot]
b1ad31b2ee Bump coverage from 7.8.0 to 7.8.2
Bumps [coverage](https://github.com/nedbat/coveragepy) from 7.8.0 to 7.8.2.
- [Release notes](https://github.com/nedbat/coveragepy/releases)
- [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst)
- [Commits](https://github.com/nedbat/coveragepy/compare/7.8.0...7.8.2)

---
updated-dependencies:
- dependency-name: coverage
  dependency-version: 7.8.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-26 23:14:32 +00:00
dependabot[bot]
92f7bfeebd Bump zipp from 3.21.0 to 3.22.0
Bumps [zipp](https://github.com/jaraco/zipp) from 3.21.0 to 3.22.0.
- [Release notes](https://github.com/jaraco/zipp/releases)
- [Changelog](https://github.com/jaraco/zipp/blob/main/NEWS.rst)
- [Commits](https://github.com/jaraco/zipp/compare/v3.21.0...v3.22.0)

---
updated-dependencies:
- dependency-name: zipp
  dependency-version: 3.22.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-26 23:14:27 +00:00
José Manuel Delicado
b4288ce51e Merge pull request #797 from MCV-Software/dependabot/pip/types-python-dateutil-2.9.0.20250516
Some checks failed
Update translation files / update_catalogs (push) Failing after 22s
Bump types-python-dateutil from 2.9.0.20241206 to 2.9.0.20250516
2025-05-20 07:32:48 +02:00
José Manuel Delicado
345ec6ffb0 Merge pull request #798 from MCV-Software/dependabot/pip/pluggy-1.6.0
Bump pluggy from 1.5.0 to 1.6.0
2025-05-20 07:32:23 +02:00
dependabot[bot]
436e58dae0 Bump types-python-dateutil from 2.9.0.20241206 to 2.9.0.20250516
Bumps [types-python-dateutil](https://github.com/typeshed-internal/stub_uploader) from 2.9.0.20241206 to 2.9.0.20250516.
- [Commits](https://github.com/typeshed-internal/stub_uploader/commits)

---
updated-dependencies:
- dependency-name: types-python-dateutil
  dependency-version: 2.9.0.20250516
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-19 23:44:18 +00:00
dependabot[bot]
48d55de460 Bump pluggy from 1.5.0 to 1.6.0
Bumps [pluggy](https://github.com/pytest-dev/pluggy) from 1.5.0 to 1.6.0.
- [Changelog](https://github.com/pytest-dev/pluggy/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pluggy/compare/1.5.0...1.6.0)

---
updated-dependencies:
- dependency-name: pluggy
  dependency-version: 1.6.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-19 23:44:18 +00:00
bbef9d988b Fixed a string 2025-03-08 20:50:43 -06:00
46 changed files with 1205 additions and 125 deletions

View File

@@ -19,7 +19,7 @@ jobs:
- name: Get python interpreter
uses: actions/setup-python@v4
with:
python-version: '3.10'
python-version: '3.13'
- name: Install python packages
run: python -m pip install -r requirements.txt
@@ -29,6 +29,12 @@ jobs:
.\scripts\build.ps1
mv src/dist scripts\TWBlue64
- name: Install NSIS
run: choco install nsis
- name: Add NSIS to PATH
run: echo "C:\Program Files (x86)\NSIS" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
- name: make installer
run: |
cd scripts

342
CLAUDE.md Normal file
View File

@@ -0,0 +1,342 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
TWBlue is an accessible desktop Mastodon client for Windows, built with Python 3.10 and wxPython. It provides two specialized interfaces optimized for screen reader users to interact with Mastodon instances. The application emphasizes accessibility-first design with keyboard navigation, audio feedback, and screen reader integration.
## Development Commands
### Running from Source
```bash
cd src
python main.py
```
For development from source, VLC dependencies are loaded from `../windows-dependencies/{arch}/` where arch is x86 or x64.
### Installing Dependencies
```bash
# Install all Python dependencies
pip install -r requirements.txt
# Initialize git submodules for Windows dependencies
git submodule init
git submodule update
```
### Building
```bash
# Build binary distribution (from src/ directory)
python setup.py build
# Output will be in src/dist/
```
### Testing
```bash
# Run tests using pytest
pytest
# Tests are located in src/test/
```
### Generating Documentation
```bash
cd doc
python documentation_importer.py
python generator.py
# Copy generated language folders to src/documentation/
# Copy license.txt to src/documentation/
```
### Translation Management
```bash
# Extract translation strings (from doc/ directory)
pybabel extract -o twblue.pot --msgid-bugs-address "manuel@manuelcortez.net" --copyright-holder "MCV software" --input-dirs ../src
# Note: Translations managed via Weblate at https://weblate.mcvsoftware.com
```
## Architecture Overview
TWBlue follows an MVC architecture with distinct separation between data access (Sessions), business logic (Controllers), and presentation (wxUI).
### Core Components
#### 1. Session Layer (`src/sessions/`)
Sessions represent authenticated connections to Mastodon instances. They manage API interactions, OAuth2 authentication, and persistent data storage.
- **Base Session** (`sessions/base.py`): Abstract base class with configuration management, SQLiteDict persistence, and decorators for login/configuration checks
- **Mastodon Session** (`sessions/mastodon/session.py`): Implements Mastodon.py API wrapper, OAuth2 flow, and account credential management
- **Streaming** (`sessions/mastodon/streaming.py`): Real-time event listener that publishes to pub/sub system
Key patterns:
- Sessions use `@_require_login` and `@_require_configuration` decorators
- Configuration files stored as INI format via configobj in `config/{session_id}/session.conf`
- Persistent data (caches, user lists) stored in `config/{session_id}/cache.db` using SQLiteDict
- Each session has its own sound system instance
#### 2. Buffer System (`src/controller/buffers/`)
Buffers are the primary data structures for displaying social media content (timelines, mentions, notifications, conversations, etc.).
**Base Buffer** (`controller/buffers/base/base.py`):
- Links buffer UI (wxPanel) with session (API access) and compose functions (data rendering)
- Handles keyboard events (F5/F6 for volume, Delete for item removal, Return for URLs)
- Manages periodic updates via `start_stream()` function
- Each buffer has a `compose_function` that formats API data for display
**Mastodon Buffers** (`controller/buffers/mastodon/`):
- `base.py`: Mastodon-specific base buffer with timeline pagination
- `users.py`: Home timeline, mentions buffer
- `community.py`: Local/federated timelines
- `notifications.py`: System notifications
- `conversations.py`: Direct message threads
- `search.py`: Search results
Buffer lifecycle:
1. Created by mainController when session initializes
2. Added to view (wx.Treebook)
3. Periodically updated via `start_stream()` or real-time via pub/sub events
4. Destroyed when session ends or buffer removed
#### 3. Controller Layer (`src/controller/`)
Controllers orchestrate application logic and coordinate between sessions, buffers, and UI.
**Main Controller** (`controller/mainController.py`):
- Manages all active buffers and sessions
- Binds keyboard shortcuts to actions
- Handles pub/sub event subscriptions
- Periodically calls `start_stream()` on visible buffers
- Provides buffer search methods: `search_buffer()`, `get_current_buffer()`, `get_best_buffer()`
**Specialized Controllers**:
- `settings.py`: Settings dialog management
- `userAlias.py` / `userList.py`: User management features
- `mastodon/handler.py`: Mastodon-specific operations (filters, etc.)
#### 4. GUI Layer (`src/wxUI/`)
wxPython-based interface with menu-driven navigation and list controls.
- **Main Frame** (`wxUI/view.py`): Primary window with wx.Treebook for buffers, menu system, system tray integration
- **Buffer Panels** (`wxUI/buffers/`): Panel implementations for each buffer type
- **Dialogs** (`wxUI/dialogs/`): Post composition, settings, user profiles, filters
#### 5. Pub/Sub Event System
Decoupled communication using PyPubSub 4.0.3.
Key events:
- `mastodon.status_received`: New post received via streaming
- `mastodon.status_updated`: Post edited
- `mastodon.notification_received`: New notification
- `mastodon.conversation_received`: New DM
Event flow:
1. Streaming listener receives API event
2. Publishes to topic via `pub.sendMessage()`
3. mainController subscribes to topics and routes to appropriate buffer
4. Buffer updates its display
#### 6. Session Manager (`src/sessionmanager/`)
Manages session lifecycle (creation, configuration, activation, deletion).
- `sessionManager.py`: UI for managing multiple accounts
- `manager.py`: Persists session list to global config
- Handles OAuth2 authorization flow for new accounts
- Loads saved sessions on startup
#### 7. Configuration System (`src/config.py`, `src/config_utils.py`)
Hierarchical configuration with defaults and user overrides.
- Global config: `config/app-configuration.conf` (defaults in `src/app-configuration.defaults`)
- Session configs: `config/{session_id}/session.conf` (defaults in `src/mastodon.defaults`)
- Keymaps in `src/keymaps/`
- Sound packs in `src/sounds/`
**Path Management** (`src/paths.py`):
- Portable mode: Config/logs in application directory
- Installed mode: Config/logs in AppData
- Detects installation by presence of `Uninstall.exe`
#### 8. Accessibility Features
Built for screen reader users from the ground up.
- `accessible_output2`: Multi-screen reader support (NVDA, JAWS, SAPI, etc.)
- `sound_lib`: Accessible audio playback with spatial audio
- `platform_utils`: OS-specific accessibility hooks
- `output.py`: Unified interface for speech output
- `sound.py`: Sound system with volume control and sound pack management
#### 9. Keyboard Handling (`src/keyboard_handler/`)
Cross-platform keyboard input with global hotkey support.
- `wx_handler.py`: wxPython integration
- `global_handler.py`: System-wide hotkeys
- Platform implementations: `windows.py`, `osx.py`, `linux.py`
- `keystrokeEditor/`: UI for customizing shortcuts
### Application Initialization Flow
From `src/main.py`:
1. Setup logging to temp directory, then move to permanent location
2. Initialize language handler
3. Load global configuration
4. Setup sound system
5. Setup accessibility output
6. Initialize session manager
7. Load saved sessions or prompt for account creation
8. Create main controller
9. Start main event loop
### Data Flow Patterns
#### Real-time Update Flow
```
Mastodon Streaming API
→ sessions/mastodon/streaming.py (StreamListener)
→ pub.sendMessage("mastodon.status_received", ...)
→ controller/mainController.py (subscriber)
→ buffer.add_new_item()
→ compose_function(item)
→ wxUI update
```
#### User Action Flow
```
Keyboard input
→ wx event handler
→ buffer.get_event()
→ buffer action method (e.g., open_status())
→ session.api_call()
→ UI update or pub/sub event
```
#### Periodic Update Flow
```
RepeatingTimer (every N seconds)
→ mainController calls buffer.start_stream()
→ session.get_timeline_data()
→ buffer.put_items_on_list()
→ compose_function for each item
→ wxUI list control update
```
## Key Design Patterns and Conventions
### Compose Functions
Buffers use compose functions to render API objects as user-readable strings. Located in `sessions/mastodon/compose.py`:
```python
compose_function(item, db, relative_times, show_screen_names=False, session=None)
# Returns a string representation of the item for display
```
### Session Decorators
Sessions use decorators to enforce prerequisites:
```python
@baseSession._require_login
def post_status(self, text):
# Only executes if self.logged == True
pass
@baseSession._require_configuration
def get_timeline(self):
# Only executes if self.settings != None
pass
```
### Buffer Naming Convention
Buffers have both a `name` (internal identifier) and `account` (associated username):
- `name`: e.g., "home_timeline", "mentions", "notifications"
- `account`: e.g., "user@mastodon.social"
- Buffers are uniquely identified by (name, account) tuple
### Configuration Hierarchy
1. Default values in `src/*.defaults` files
2. User overrides in `config/*.conf` files
3. Runtime modifications via settings dialogs
4. Written back to user config files on change
## Important Caveats
### Platform-Specific Code
- VLC paths must be set via environment variables when running from source (see `main.py`)
- Windows-specific: pywin32, win-inet-pton, winpaths dependencies
- Accessibility output works best on Windows with NVDA/JAWS
### Threading and Event Handling
- API calls often wrapped in `call_threaded()` to avoid blocking UI
- Streaming runs in background thread and publishes to main thread via pub/sub
- wx events must be handled on main thread
### Session Lifecycle
- Sessions must be logged in before buffer creation
- Buffers maintain references to sessions via `self.session`
- Destroying a session should destroy all associated buffers
- Session settings auto-save on write via `settings.write()`
### Buffer Visibility
- Buffers have `invisible` flag for internal/system buffers
- Main controller distinguishes between visible buffers (shown in tree) and invisible buffers (used for data access)
- Empty buffers serve as account placeholders in tree structure
### Logging and Debugging
- Logs written to temp directory on startup, then moved to permanent location
- Binary builds redirect stdout/stderr to `logs/` directory
- Source builds use console output
- Use `logging.getLogger("module.name")` pattern throughout
## Build System Details
### cx_Freeze Configuration (`src/setup.py`)
- Target: Win32GUI (suppresses console window)
- Includes: keymaps, locales, sounds, documentation, icon, config defaults
- Architecture-specific: Loads x86 or x64 dependencies from windows-dependencies submodule
- Special handling for enchant dictionaries, VLC plugins, VC++ redistributables
### NSIS Installer (`scripts/twblue.nsi`)
- Expects binary distribution in `scripts/twblue64/`
- Creates Start Menu shortcuts, Desktop shortcut (optional)
- Registers uninstaller
- Checks for running instances before install/uninstall
### CI/CD (`.github/workflows/release.yml`)
- Triggers on version tags (v20*)
- Builds on Windows-latest with Python 3.10
- Creates both installer (EXE) and portable (ZIP) distributions
- Uploads to GitHub releases
## Mastodon API Integration
### Authentication
OAuth2 flow implemented in `sessions/mastodon/session.py`:
1. Create application credentials for instance
2. Request OAuth authorization URL
3. User authorizes in browser
4. Exchange code for access token
5. Store credentials in session config
### API Client
Uses Mastodon.py 2.1.4 library:
- Instance created with base URL and access token
- Methods: `status_post()`, `timeline()`, `account()`, etc.
- Rate limiting handled by library
- Supports multiple instances simultaneously
### Streaming API
Real-time updates via `sessions/mastodon/streaming.py`:
- Inherits from `Mastodon.StreamListener`
- Connects to user, public, or hashtag streams
- Runs in background thread
- Events published to main thread via pub/sub
## Localization
TWBlue supports 23 languages:
- Translation files in `src/locales/{lang}/LC_MESSAGES/twblue.mo`
- Uses gettext with `_()` function throughout codebase
- Language selection in settings, stored in global config
- Babel for extraction and compilation
- Weblate for translation management

View File

@@ -2,6 +2,36 @@ TWBlue Changelog
## changes in this version
In this version, we have focused on expanding content management capabilities within Mastodon. It is now possible to edit sent posts and schedule them for future publication. Additionally, support for reading quoted posts has been implemented, and a new buffer for server announcements is available. On the Core side, visual stability has been prioritized to ensure proper window display, along with an expansion of keyboard shortcuts.
* Core:
* Fixed a critical issue where buffers were not visible on screen in certain configurations. Now the main window maximizes correctly and visual fixes have been applied to ensure content is accessible. ([#886](https://github.com/mcv-software/twblue/issues/886))
* Keyboard shortcut improvements: Several shortcuts have been added and fixed to improve efficiency and avoid conflicts:
* New shortcuts for the autocomplete users manager and menu bar items. ([#842](https://github.com/mcv-software/twblue/issues/842))
* Added a shortcut for the "Restore Template" button in the template editor dialog. ([#841](https://github.com/mcv-software/twblue/issues/841))
* New shortcuts for user list and poll dialogs.
* Resolved a conflict with the 's' key shortcut used for seeking media.
* Updated the shortcut for marking an account as a "Bot" to avoid conflict with the biography field.
* Mastodon:
* **Post Editing:** It is finally possible to edit Mastodon posts from TWBlue! You can now correct errors in your posts. ([#859](https://github.com/mcv-software/twblue/issues/859))
* Safety warning: if you edit a post containing a poll, votes will be reset.
* Polls are now correctly displayed as attachments within the edit dialog.
* **Scheduled Posts:** It is now possible to schedule your posts to be published at a later time!
* Added a "Schedule post" checkbox to the post dialog with date and time pickers.
* Implemented validation to ensure posts are scheduled at least 5 minutes in the future, as required by Mastodon.
* The default time is automatically set to 6 minutes in the future for convenience.
* **Quoted Posts:** Significantly improved the reading and display of quoted posts. TWBlue now structures and reads this content more clearly. ([#860](https://github.com/mcv-software/twblue/issues/860))
* **Content Cleaning:** Implemented a more robust HTML filter to remove junk elements or unnecessary CSS classes from post text, offering a cleaner reading experience.
* **Mute Conversation:** Enhanced the "Mute Conversation" feature.
* Posts from muted conversations will now be visually hidden from the Home timeline immediately upon muting, ensuring a cleaner experience.
* Added a new invisible shortcut to toggle mute on the focused conversation: `Alt+Windows+Shift+Delete` (Default) or `Control+Alt+Windows+Backspace` (Windows 10/11).
* The action is also available in the context menu of the post.
* **Announcements:** Added support for viewing server announcements.
* New dedicated buffer for "Announcements" where you can read instance-wide news.
* Added ability to dismiss (mark as read) announcements directly from the buffer.
## Changes in version 2025.3.8
In this version, we have focused on providing initial support for Mastodon filters and pinned posts. From TWBlue, it is now possible to initially use filters for posts in most buffers, as well as manage them (create, edit, and delete filters, in addition to adding keywords). A new variable has also been added for post templates in the invisible interface that allows displaying whether a post has been pinned by its author.
* Mastodon:

View File

@@ -1,10 +1,29 @@
## Changelog
In this version, we have focused on providing initial support for Mastodon filters and pinned posts. From TWBlue, it is now possible to initially use filters for posts in most buffers, as well as manage them (create, edit, and delete filters, in addition to adding keywords). A new variable has also been added for post templates in the invisible interface that allows displaying whether a post has been pinned by its author.
In this version, we have focused on expanding content management capabilities within Mastodon. It is now possible to edit sent posts and schedule them for future publication. Additionally, support for reading quoted posts has been implemented, and a new buffer for server announcements is available. On the Core side, visual stability has been prioritized to ensure proper window display, along with an expansion of keyboard shortcuts.
* Core:
* Fixed a critical issue where buffers were not visible on screen in certain configurations. Now the main window maximizes correctly and visual fixes have been applied to ensure content is accessible. ([#886](https://github.com/mcv-software/twblue/issues/886))
* Keyboard shortcut improvements: Several shortcuts have been added and fixed to improve efficiency and avoid conflicts:
* New shortcuts for the autocomplete users manager and menu bar items. ([#842](https://github.com/mcv-software/twblue/issues/842))
* Added a shortcut for the "Restore Template" button in the template editor dialog. ([#841](https://github.com/mcv-software/twblue/issues/841))
* New shortcuts for user list and poll dialogs.
* Resolved a conflict with the 's' key shortcut used for seeking media.
* Updated the shortcut for marking an account as a "Bot" to avoid conflict with the biography field.
* Mastodon:
* Added filters support to TWBlue. Filters are only implemented in posts for the moment. TWBlue will, depending in the selected settings, hide behind a content warning or completely ignore a post based on filters. Also it is possible to add, delete or edit filters from the buffer menu in the menu bar.
* A language selector has been added for posting in TWBlue. It is now possible to choose the language in which a post will be made, which will be useful for content filtering and other language-dependent features. The default language can be chosen based on your Mastodon accounts language, the language of the post youre replying to, or, if no automatic selection is possible, TWBlues own language will be used by default.
* TWBlue now supports announcing (via a new template variable for posts) pinned posts. You can edit your posts template and use the $pinned variable. When reading the post, TWBlue will indicate if the post is pinned. Also, when loading an user timeline, pinned posts will be loaded at the top or bottom of the buffer according to local settings.
* TWBlue should be able to display all posts in the post displayer dialog.
* reading long posts in the graphical user interface should work better.
* **Post Editing:** It is finally possible to edit Mastodon posts from TWBlue! You can now correct errors in your posts. ([#859](https://github.com/mcv-software/twblue/issues/859))
* Safety warning: if you edit a post containing a poll, votes will be reset.
* Polls are now correctly displayed as attachments within the edit dialog.
* **Scheduled Posts:** It is now possible to schedule your posts to be published at a later time!
* Added a "Schedule post" checkbox to the post dialog with date and time pickers.
* Implemented validation to ensure posts are scheduled at least 5 minutes in the future, as required by Mastodon.
* The default time is automatically set to 6 minutes in the future for convenience.
* **Quoted Posts:** Significantly improved the reading and display of quoted posts. TWBlue now structures and reads this content more clearly. ([#860](https://github.com/mcv-software/twblue/issues/860))
* **Content Cleaning:** Implemented a more robust HTML filter to remove junk elements or unnecessary CSS classes from post text, offering a cleaner reading experience.
* **Mute Conversation:** Enhanced the "Mute Conversation" feature.
* Posts from muted conversations will now be visually hidden from the Home timeline immediately upon muting, ensuring a cleaner experience.
* Added a new invisible shortcut to toggle mute on the focused conversation: `Alt+Windows+Shift+Delete` (Default) or `Control+Alt+Windows+Backspace` (Windows 10/11).
* The action is also available in the context menu of the post.
* **Announcements:** Added support for viewing server announcements.
* New dedicated buffer for "Announcements" where you can read instance-wide news.
* Added ability to dismiss (mark as read) announcements directly from the buffer.

View File

@@ -1,58 +1,58 @@
accessible_output2 @ git+https://github.com/accessibleapps/accessible_output2@57bda997d98e87dd78aa049e7021cf777871619b
arrow==1.3.0
attrs==25.3.0
arrow==1.4.0
attrs==25.4.0
backports.functools-lru-cache==2.0.0
blurhash==1.1.4
certifi==2025.4.26
blurhash==1.1.5
certifi==2026.1.4
chardet==5.2.0
charset-normalizer==3.4.2
charset-normalizer==3.4.4
colorama==0.4.6
configobj==5.0.9
coverage==7.8.0
cx-Freeze==8.3.0
coverage==7.13.1
cx-Freeze==8.5.3
cx-Logging==3.2.1
decorator==5.2.1
demoji==1.1.0
deepl==1.22.0
deepl==1.27.0
future==1.0.0
idna==3.10
importlib-metadata==8.7.0
iniconfig==2.1.0
idna==3.11
importlib-metadata==8.7.1
iniconfig==2.3.0
libloader @ git+https://github.com/accessibleapps/libloader@bc94811c095b2e57a036acd88660be9a33260267
libretranslatepy==2.1.4
lief==0.15.1
Markdown==3.8
Mastodon.py==2.0.1
numpy==2.2.3
oauthlib==3.2.2
Markdown==3.10
Mastodon.py==2.1.4
numpy==2.4.0
oauthlib==3.3.1
packaging==25.0
pillow==11.2.1
pillow==12.1.0
platform_utils @ git+https://github.com/accessibleapps/platform_utils@e0d79f7b399c4ea677a633d2dde9202350d62c38
pluggy==1.5.0
psutil==7.0.0
pyenchant==3.2.2
pluggy==1.6.0
psutil==7.2.1
pyenchant==3.3.0
pypiwin32==223
Pypubsub==4.0.3
Pypubsub==4.0.7
PySocks==1.7.1
pytest==8.3.5
pytest==9.0.2
python-dateutil==2.9.0.post0
python-magic-bin==0.4.14
python-vlc==3.0.21203
pywin32==310
requests==2.32.3
pywin32==311
requests==2.32.5
requests-oauthlib==2.0.0
requests-toolbelt==1.0.0
rfc3986==2.0.0
setuptools==78.1.1
setuptools==80.9.0
six==1.17.0
sniffio==1.3.1
sound_lib @ git+https://github.com/accessibleapps/sound_lib@a439f0943fb95ee7b6ba24f51a686f47c4ad66b2
sqlitedict==2.1.0
twitter-text-parser==3.0.0
types-python-dateutil==2.9.0.20241206
urllib3==2.4.0
types-python-dateutil==2.9.0.20251115
urllib3==2.6.3
win-inet-pton==1.1.0
winpaths==0.2
wxPython==4.2.3
wxPython==4.2.4
youtube-dl==2021.12.17
zipp==3.21.0
zipp==3.23.0

View File

@@ -6,3 +6,4 @@ from .users import UserBuffer
from .notifications import NotificationsBuffer
from .search import SearchBuffer
from .community import CommunityBuffer
from .announcements import AnnouncementsBuffer

View File

@@ -0,0 +1,165 @@
# -*- coding: utf-8 -*-
import time
import logging
import arrow
import widgetUtils
import wx
import output
import languageHandler
import config
from pubsub import pub
from controller.buffers.mastodon.base import BaseBuffer
from sessions.mastodon import compose, templates
from wxUI import buffers
from wxUI.dialogs.mastodon import menus
from mysc.thread_utils import call_threaded
log = logging.getLogger("controller.buffers.mastodon.announcements")
class AnnouncementsBuffer(BaseBuffer):
def __init__(self, *args, **kwargs):
# We enforce compose_func="compose_announcement"
kwargs["compose_func"] = "compose_announcement"
super(AnnouncementsBuffer, self).__init__(*args, **kwargs)
self.type = "announcementsBuffer"
def create_buffer(self, parent, name):
self.buffer = buffers.mastodon.announcementsPanel(parent, name)
def get_buffer_name(self):
return _("Announcements")
def bind_events(self):
self.buffer.set_focus_function(self.onFocus)
widgetUtils.connect_event(self.buffer.list.list, widgetUtils.KEYPRESS, self.get_event)
widgetUtils.connect_event(self.buffer, widgetUtils.BUTTON_PRESSED, self.dismiss_announcement, self.buffer.dismiss)
widgetUtils.connect_event(self.buffer.list.list, wx.EVT_LIST_ITEM_RIGHT_CLICK, self.show_menu)
widgetUtils.connect_event(self.buffer.list.list, wx.EVT_LIST_KEY_DOWN, self.show_menu_by_key)
def dismiss_announcement(self, event=None, item=None, *args, **kwargs):
index = self.buffer.list.get_selected()
if index == -1: return
item = self.session.db[self.name][index]
# Optimistic UI update or wait for API? Let's wait for API to be safe, but run threaded.
# We need a custom call because 'announcements_dismiss' returns None on success usually.
def _do_dismiss():
try:
self.session.api_call(call_name="announcements_dismiss", id=str(item.id))
# If success, update UI in main thread
wx.CallAfter(self._on_dismiss_success, index)
except Exception as e:
log.exception("Error dismissing announcement")
self.session.sound.play("error.ogg")
call_threaded(_do_dismiss)
def _on_dismiss_success(self, index):
if index < len(self.session.db[self.name]):
self.session.db[self.name].pop(index)
self.buffer.list.remove_item(index)
output.speak(_("Announcement dismissed."))
def show_menu(self, ev, pos=0, *args, **kwargs):
if self.buffer.list.get_count() == 0:
return
# Create a simple menu
menu = wx.Menu()
dismiss_item = menu.Append(wx.ID_ANY, _("Dismiss"))
copy_item = menu.Append(wx.ID_ANY, _("Copy text"))
self.buffer.Bind(wx.EVT_MENU, self.dismiss_announcement, dismiss_item)
self.buffer.Bind(wx.EVT_MENU, self.copy, copy_item)
if pos != 0:
self.buffer.PopupMenu(menu, pos)
else:
self.buffer.PopupMenu(menu, self.buffer.list.list.GetPosition())
def url(self, *args, **kwargs):
self.dismiss_announcement()
def get_more_items(self): output.speak(_("This buffer does not support loading more items."), True)
# Disable social interactions not applicable to announcements
def reply(self, *args, **kwargs):
pass
def share_item(self, *args, **kwargs):
pass
def toggle_favorite(self, *args, **kwargs):
pass
def add_to_favorites(self, *args, **kwargs):
pass
def remove_from_favorites(self, *args, **kwargs):
pass
def toggle_bookmark(self, *args, **kwargs):
pass
def mute_conversation(self, *args, **kwargs):
pass
def vote(self, *args, **kwargs):
pass
def send_message(self, *args, **kwargs):
pass
def user_details(self, *args, **kwargs):
pass
def view_item(self, *args, **kwargs):
# We could implement a specific viewer for announcements if needed,
# but the default one expects a status object structure.
pass
def copy(self, event=None):
item = self.get_item()
if item:
pub.sendMessage("execute-action", action="copy_to_clipboard")
def onFocus(self, *args, **kwargs):
# Similar logic to BaseBuffer but adapted if needed.
# BaseBuffer.onFocus handles reading long posts.
if config.app["app-settings"]["read_long_posts_in_gui"] == True and self.buffer.list.list.HasFocus():
wx.CallLater(40, output.speak, self.get_message(), interrupt=True)
def get_message(self):
# Override to use announcement template
announcement = self.get_item()
if announcement == None:
return
template = self.session.settings.get("templates", {}).get("announcement", templates.announcement_default_template)
t = templates.render_announcement(announcement, template, self.session.settings, relative_times=self.session.settings["general"]["relative_times"], offset_hours=self.session.db["utc_offset"])
return t
def start_stream(self, mandatory=False, play_sound=True, avoid_autoreading=False):
current_time = time.time()
if self.execution_time == 0 or current_time-self.execution_time >= 300 or mandatory==True:
self.execution_time = current_time
log.debug("Starting stream for announcements buffer")
try:
# The announcements API does not accept min_id or limit parameters
results = self.session.api.announcements()
# Reverse the list so order_buffer processes them according to user preference
results.reverse()
except Exception as e:
log.exception("Error retrieving announcements: %s" % (str(e)))
return 0
# order_buffer handles duplication filtering by ID internally
number_of_items = self.session.order_buffer(self.name, results)
log.debug("Number of new announcements retrieved: %d" % (number_of_items,))
self.put_items_on_list(number_of_items)
if number_of_items > 0 and play_sound == True and self.sound != None and self.session.settings["sound"]["session_mute"] == False:
self.session.sound.play(self.sound)
return number_of_items
return 0

View File

@@ -40,9 +40,31 @@ class BaseBuffer(base.Buffer):
self.buffer.account = account
self.bind_events()
self.sound = sound
pub.subscribe(self.on_mute_cleanup, "mastodon.mute_cleanup")
if "-timeline" in self.name or "-followers" in self.name or "-following" in self.name or "searchterm" in self.name:
self.finished_timeline = False
def on_mute_cleanup(self, conversation_id, session_name):
if self.name != "home_timeline":
return
if session_name != self.session.get_name():
return
items_to_remove = []
for index, item in enumerate(self.session.db[self.name]):
c_id = None
if hasattr(item, "conversation_id"):
c_id = item.conversation_id
elif isinstance(item, dict):
c_id = item.get("conversation_id")
if c_id == conversation_id:
items_to_remove.append(index)
items_to_remove.sort(reverse=True)
for index in items_to_remove:
self.session.db[self.name].pop(index)
self.buffer.list.remove_item(index)
def create_buffer(self, parent, name):
self.buffer = buffers.mastodon.basePanel(parent, name)
@@ -280,6 +302,12 @@ class BaseBuffer(base.Buffer):
return
menu = menus.base()
widgetUtils.connect_event(menu, widgetUtils.MENU, self.reply, menuitem=menu.reply)
# Enable/disable edit based on whether the post belongs to the user
item = self.get_item()
if item and item.account.id == self.session.db["user_id"] and item.reblog == None:
widgetUtils.connect_event(menu, widgetUtils.MENU, self.edit_status, menuitem=menu.edit)
else:
menu.edit.Enable(False)
widgetUtils.connect_event(menu, widgetUtils.MENU, self.user_actions, menuitem=menu.userActions)
if self.can_share() == True:
widgetUtils.connect_event(menu, widgetUtils.MENU, self.share_item, menuitem=menu.boost)
@@ -287,6 +315,7 @@ class BaseBuffer(base.Buffer):
menu.boost.Enable(False)
widgetUtils.connect_event(menu, widgetUtils.MENU, self.fav, menuitem=menu.fav)
widgetUtils.connect_event(menu, widgetUtils.MENU, self.unfav, menuitem=menu.unfav)
widgetUtils.connect_event(menu, widgetUtils.MENU, self.mute_conversation, menuitem=menu.mute)
widgetUtils.connect_event(menu, widgetUtils.MENU, self.url_, menuitem=menu.openUrl)
widgetUtils.connect_event(menu, widgetUtils.MENU, self.audio, menuitem=menu.play)
widgetUtils.connect_event(menu, widgetUtils.MENU, self.view, menuitem=menu.view)
@@ -501,6 +530,49 @@ class BaseBuffer(base.Buffer):
log.exception("")
self.session.db[self.name] = items
def edit_status(self, event=None, item=None, *args, **kwargs):
if item == None:
item = self.get_item()
# Check if the post belongs to the current user
if item.account.id != self.session.db["user_id"] or item.reblog != None:
output.speak(_("You can only edit your own posts."))
return
# Check if post has a poll with votes - warn user before proceeding
if hasattr(item, 'poll') and item.poll is not None:
votes_count = item.poll.votes_count if hasattr(item.poll, 'votes_count') else 0
if votes_count > 0:
# Show confirmation dialog
warning_title = _("Warning: Poll with votes")
warning_message = _("This post contains a poll with {votes} votes.\n\n"
"According to Mastodon's API, editing this post will reset ALL votes to zero, "
"even if you don't modify the poll itself.\n\n"
"Do you want to continue editing?").format(votes=votes_count)
dialog = wx.MessageDialog(self.buffer, warning_message, warning_title,
wx.YES_NO | wx.NO_DEFAULT | wx.ICON_WARNING)
result = dialog.ShowModal()
dialog.Destroy()
if result != wx.ID_YES:
output.speak(_("Edit cancelled"))
return
# Log item info for debugging
log.debug("Editing status: id={}, has_media_attachments={}, media_count={}".format(
item.id,
hasattr(item, 'media_attachments'),
len(item.media_attachments) if hasattr(item, 'media_attachments') else 0
))
# Create edit dialog with existing post data
title = _("Edit post")
caption = _("Edit your post here")
post = messages.editPost(session=self.session, item=item, title=title, caption=caption)
response = post.message.ShowModal()
if response == wx.ID_OK:
post_data = post.get_data()
# Call edit_post method in session
# Note: visibility and language cannot be changed when editing per Mastodon API
call_threaded(self.session.edit_post, post_id=post.post_id, posts=post_data)
if hasattr(post.message, "destroy"):
post.message.destroy()
def user_details(self):
item = self.get_item()
pass
@@ -563,6 +635,22 @@ class BaseBuffer(base.Buffer):
else:
call_threaded(self.session.api_call, call_name="status_unbookmark", preexec_message=_("Removing from bookmarks..."), _sound="favourite.ogg", id=item.id)
def mute_conversation(self, event=None, item=None, *args, **kwargs):
if item == None:
item = self.get_item()
if item.reblog != None:
item = item.reblog
try:
item = self.session.api.status(item.id)
except MastodonNotFoundError:
output.speak(_("No status found with that ID"))
return
if item.muted == False:
call_threaded(self.session.api_call, call_name="status_mute", preexec_message=_("Muting conversation..."), _sound="favourite.ogg", id=item.id)
pub.sendMessage("mastodon.mute_cleanup", conversation_id=item.conversation_id, session_name=self.session.get_name())
else:
call_threaded(self.session.api_call, call_name="status_unmute", preexec_message=_("Unmuting conversation..."), _sound="favourite.ogg", id=item.id)
def view_item(self, item=None):
if item == None:
item = self.get_item()

View File

@@ -161,6 +161,13 @@ class NotificationsBuffer(BaseBuffer):
menu = menus.notification(notification.type)
if self.is_post():
widgetUtils.connect_event(menu, widgetUtils.MENU, self.reply, menuitem=menu.reply)
# Enable/disable edit based on whether the post belongs to the user
if hasattr(menu, 'edit'):
status = self.get_post()
if status and status.account.id == self.session.db["user_id"] and status.reblog == None:
widgetUtils.connect_event(menu, widgetUtils.MENU, self.edit_status, menuitem=menu.edit)
else:
menu.edit.Enable(False)
widgetUtils.connect_event(menu, widgetUtils.MENU, self.user_actions, menuitem=menu.userActions)
if self.can_share() == True:
widgetUtils.connect_event(menu, widgetUtils.MENU, self.share_item, menuitem=menu.boost)

View File

@@ -449,6 +449,15 @@ class Controller(object):
buffer = self.search_buffer(buffer.name, buffer.account)
buffer.destroy_status()
def edit_post(self, *args, **kwargs):
""" Edits a post in the current buffer.
Users can only edit their own posts."""
buffer = self.view.get_current_buffer()
if hasattr(buffer, "account"):
buffer = self.search_buffer(buffer.name, buffer.account)
if hasattr(buffer, "edit_status"):
buffer.edit_status()
def exit(self, *args, **kwargs):
if config.app["app-settings"]["ask_at_exit"] == True:
answer = commonMessageDialogs.exit_dialog(self.view)

View File

@@ -48,7 +48,7 @@ class Handler(object):
addAlias=_("Add a&lias"),
addToList=None,
removeFromList=None,
details=_("Show user profile"),
details=_("S&how user profile"),
favs=None,
# In buffer Menu.
community_timeline =_("Create c&ommunity timeline"),
@@ -92,6 +92,8 @@ class Handler(object):
pub.sendMessage("createBuffer", buffer_type="UserBuffer", session_type=session.type, buffer_title=_("Blocked users"), parent_tab=root_position, start=False, kwargs=dict(parent=controller.view.nb, compose_func="compose_user", function="blocks", name="blocked", sessionObject=session, account=name))
elif i == 'notifications':
pub.sendMessage("createBuffer", buffer_type="NotificationsBuffer", session_type=session.type, buffer_title=_("Notifications"), parent_tab=root_position, start=False, kwargs=dict(parent=controller.view.nb, compose_func="compose_notification", function="notifications", name="notifications", sessionObject=session, account=name))
elif i == 'announcements':
pub.sendMessage("createBuffer", buffer_type="AnnouncementsBuffer", session_type=session.type, buffer_title=_("Announcements"), parent_tab=root_position, start=False, kwargs=dict(parent=controller.view.nb, function="announcements", name="announcements", sessionObject=session, account=name, sound="new_event.ogg"))
pub.sendMessage("createBuffer", buffer_type="EmptyBuffer", session_type="base", buffer_title=_("Timelines"), parent_tab=root_position, start=False, kwargs=dict(parent=controller.view.nb, name="timelines", account=name))
timelines_position =controller.view.search("timelines", name)
for i in session.settings["other_buffers"]["timelines"]:

View File

@@ -2,6 +2,7 @@
import os
import re
import wx
import logging
import widgetUtils
import config
import output
@@ -14,6 +15,8 @@ from wxUI.dialogs.mastodon import postDialogs
from extra.autocompletionUsers import completion
from . import userList
log = logging.getLogger("controller.mastodon.messages")
def character_count(post_text, post_cw, character_limit=500):
# We will use text for counting character limit only.
full_text = post_text+post_cw
@@ -62,6 +65,13 @@ class post(messages.basicMessage):
postdata = dict(text=text, attachments=attachments, sensitive=self.message.sensitive.GetValue(), spoiler_text=None)
if postdata.get("sensitive") == True:
postdata.update(spoiler_text=self.message.spoiler.GetValue())
# Check for scheduled post
if hasattr(self.message, 'get_scheduled_at'):
scheduled_at = self.message.get_scheduled_at()
if scheduled_at:
postdata['scheduled_at'] = scheduled_at
self.thread.append(postdata)
self.attachments = []
if update_gui:
@@ -262,6 +272,108 @@ class post(messages.basicMessage):
visibility_setting = visibility_settings.index(setting)
self.message.visibility.SetSelection(setting)
class editPost(post):
def __init__(self, session, item, title, caption, *args, **kwargs):
""" Initialize edit dialog with existing post data.
Note: Per Mastodon API, visibility and language cannot be changed when editing.
These fields will be displayed but disabled in the UI.
"""
# Extract text from post
if item.reblog != None:
item = item.reblog
text = item.content
# Remove HTML tags from content
import re
text = re.sub('<[^<]+?>', '', text)
# Initialize parent class
super(editPost, self).__init__(session, title, caption, text=text, *args, **kwargs)
# Store the post ID for editing
self.post_id = item.id
# Set visibility (read-only, cannot be changed)
visibility_settings = dict(public=0, unlisted=1, private=2, direct=3)
self.message.visibility.SetSelection(visibility_settings.get(item.visibility, 0))
self.message.visibility.Enable(False) # Disable as it cannot be edited
# Set language (read-only, cannot be changed)
if item.language:
self.set_language(item.language)
self.message.language.Enable(False) # Disable as it cannot be edited
# Set sensitive content and spoiler
if item.sensitive:
self.message.sensitive.SetValue(True)
if item.spoiler_text:
self.message.spoiler.ChangeValue(item.spoiler_text)
self.message.on_sensitivity_changed()
# Load existing poll (if any)
# Note: You cannot have both media and a poll, so check poll first
if hasattr(item, 'poll') and item.poll is not None:
log.debug("Loading existing poll for post {}".format(self.post_id))
poll = item.poll
# Extract poll options (just the text, not the votes)
poll_options = [option.title for option in poll.options]
# Calculate expires_in based on current time and expires_at
# For editing, we need to provide a new expiration time
# Since we can't get the original expires_in, use a default or let user configure
# For now, use 1 day (86400 seconds) as default
expires_in = 86400
if hasattr(poll, 'expires_at') and poll.expires_at and not poll.expired:
# Calculate remaining time if poll hasn't expired
from dateutil import parser as date_parser
import datetime
try:
expires_at = poll.expires_at
if isinstance(expires_at, str):
expires_at = date_parser.parse(expires_at)
now = datetime.datetime.now(datetime.timezone.utc)
remaining = (expires_at - now).total_seconds()
if remaining > 0:
expires_in = int(remaining)
except Exception as e:
log.warning("Could not calculate poll expiration: {}".format(e))
poll_info = {
"type": "poll",
"file": "",
"description": _("Poll with {} options").format(len(poll_options)),
"options": poll_options,
"expires_in": expires_in,
"multiple": poll.multiple if hasattr(poll, 'multiple') else False,
"hide_totals": poll.voters_count == 0 if hasattr(poll, 'voters_count') else False
}
self.attachments.append(poll_info)
self.message.add_item(item=[poll_info["file"], poll_info["type"], poll_info["description"]])
log.debug("Loaded poll with {} options. WARNING: Editing will reset all votes!".format(len(poll_options)))
# Load existing media attachments (only if no poll)
elif hasattr(item, 'media_attachments'):
log.debug("Loading existing media attachments for post {}".format(self.post_id))
log.debug("Item has media_attachments attribute, count: {}".format(len(item.media_attachments)))
if len(item.media_attachments) > 0:
for media in item.media_attachments:
log.debug("Processing media: id={}, type={}, url={}".format(media.id, media.type, media.url))
media_info = {
"id": media.id, # Keep the existing media ID
"type": media.type,
"file": media.url, # URL of existing media
"description": media.description or ""
}
# Include focus point if available
if hasattr(media, 'meta') and media.meta and 'focus' in media.meta:
focus = media.meta['focus']
media_info["focus"] = (focus.get('x'), focus.get('y'))
log.debug("Added focus point: {}".format(media_info["focus"]))
self.attachments.append(media_info)
# Display in the attachment list
display_name = media.url.split('/')[-1]
log.debug("Adding item to UI: name={}, type={}, desc={}".format(display_name, media.type, media.description or ""))
self.message.add_item(item=[display_name, media.type, media.description or ""])
log.debug("Total attachments loaded: {}".format(len(self.attachments)))
else:
log.debug("media_attachments list is empty")
else:
log.debug("Item has no poll or media attachments")
# Update text processor to reflect the loaded content
self.text_processor()
class viewPost(post):
def __init__(self, session, post, offset_hours=0, date="", item_url=""):
self.session = session

View File

@@ -52,10 +52,12 @@ class accountSettingsController(globalSettingsController):
post_template = self.config["templates"]["post"]
conversation_template = self.config["templates"]["conversation"]
person_template = self.config["templates"]["person"]
self.dialog.create_templates(post_template=post_template, conversation_template=conversation_template, person_template=person_template)
announcement_template = self.config.get("templates", {}).get("announcement", "$text. Published $published_at. $read")
self.dialog.create_templates(post_template=post_template, conversation_template=conversation_template, person_template=person_template, announcement_template=announcement_template)
widgetUtils.connect_event(self.dialog.templates.post, widgetUtils.BUTTON_PRESSED, self.edit_post_template)
widgetUtils.connect_event(self.dialog.templates.conversation, widgetUtils.BUTTON_PRESSED, self.edit_conversation_template)
widgetUtils.connect_event(self.dialog.templates.person, widgetUtils.BUTTON_PRESSED, self.edit_person_template)
widgetUtils.connect_event(self.dialog.templates.announcement, widgetUtils.BUTTON_PRESSED, self.edit_announcement_template)
self.dialog.create_other_buffers()
buffer_values = self.get_buffers_list()
self.dialog.buffers.insert_buffers(buffer_values)
@@ -109,6 +111,15 @@ class accountSettingsController(globalSettingsController):
self.config.write()
self.dialog.templates.person.SetLabel(_("Edit template for persons. Current template: {}").format(result))
def edit_announcement_template(self, *args, **kwargs):
template = self.config.get("templates", {}).get("announcement", "$text. Published $published_at. $read")
control = EditTemplate(template=template, type="announcement")
result = control.run_dialog()
if result != "": # Template has been saved.
self.config["templates"]["announcement"] = result
self.config.write()
self.dialog.templates.announcement.SetLabel(_("Edit template for announcements. Current template: {}").format(result))
def save_configuration(self):
if self.config["general"]["relative_times"] != self.dialog.get_value("general", "relative_time"):
self.needs_restart = True
@@ -204,6 +215,7 @@ class accountSettingsController(globalSettingsController):
all_buffers['blocked']=_("Blocked users")
all_buffers['muted']=_("Muted users")
all_buffers['notifications']=_("Notifications")
all_buffers['announcements']=_("Announcements")
list_buffers = []
hidden_buffers=[]
all_buffers_keys = list(all_buffers.keys())

View File

@@ -2,7 +2,7 @@
import re
import wx
from typing import List
from sessions.mastodon.templates import post_variables, conversation_variables, person_variables
from sessions.mastodon.templates import post_variables, conversation_variables, person_variables, announcement_variables
from wxUI.dialogs import templateDialogs
class EditTemplate(object):
@@ -13,6 +13,8 @@ class EditTemplate(object):
self.variables = post_variables
elif type == "conversation":
self.variables = conversation_variables
elif type == "announcement":
self.variables = announcement_variables
else:
self.variables = person_variables
self.template: str = template

View File

@@ -13,8 +13,8 @@ class autocompletionManageDialog(widgetUtils.BaseDialog):
self.users = widgets.list(panel, _(u"Username"), _(u"Name"), style=wx.LC_REPORT)
sizer.Add(label, 0, wx.ALL, 5)
sizer.Add(self.users.list, 0, wx.ALL, 5)
self.add = wx.Button(panel, -1, _(u"Add user"))
self.remove = wx.Button(panel, -1, _(u"Remove user"))
self.add = wx.Button(panel, -1, _(u"&Add user"))
self.remove = wx.Button(panel, -1, _(u"&Remove user"))
optionsBox = wx.BoxSizer(wx.HORIZONTAL)
optionsBox.Add(self.add, 0, wx.ALL, 5)
optionsBox.Add(self.remove, 0, wx.ALL, 5)

View File

@@ -23,6 +23,7 @@ url = string(default="control+win+b")
go_home = string(default="control+win+home")
go_end = string(default="control+win+end")
delete = string(default="control+win+delete")
edit_post = string(default="")
clear_buffer = string(default="control+win+shift+delete")
repeat_item = string(default="control+win+space")
copy_to_clipboard = string(default="control+win+shift+c")

View File

@@ -33,6 +33,7 @@ go_page_up = string(default="control+win+pageup")
go_page_down = string(default="control+win+pagedown")
update_profile = string(default="control+win+shift+p")
delete = string(default="control+win+delete")
edit_post = string(default="")
clear_buffer = string(default="control+win+shift+delete")
repeat_item = string(default="control+win+space")
copy_to_clipboard = string(default="control+win+shift+c")

View File

@@ -33,6 +33,7 @@ go_page_up = string(default="control+win+pageup")
go_page_down = string(default="control+win+pagedown")
update_profile = string(default="alt+win+p")
delete = string(default="alt+win+delete")
edit_post = string(default="")
clear_buffer = string(default="alt+win+shift+delete")
repeat_item = string(default="alt+win+space")
copy_to_clipboard = string(default="alt+win+shift+c")
@@ -56,5 +57,6 @@ update_buffer = string(default="control+alt+shift+u")
ocr_image = string(default="win+alt+o")
open_in_browser = string(default="alt+control+win+return")
add_alias=string(default="")
mute_conversation=string(default="control+alt+win+back")
find = string(default="control+win+{")
vote=string(default="alt+win+shift+v")

View File

@@ -33,6 +33,7 @@ go_page_up = string(default="control+win+pageup")
go_page_down = string(default="control+win+pagedown")
update_profile = string(default="alt+win+p")
delete = string(default="alt+win+delete")
edit_post = string(default="")
clear_buffer = string(default="alt+win+shift+delete")
repeat_item = string(default="control+alt+win+space")
copy_to_clipboard = string(default="alt+win+shift+c")
@@ -56,5 +57,6 @@ update_buffer = string(default="control+alt+shift+u")
ocr_image = string(default="win+alt+o")
open_in_browser = string(default="alt+control+win+return")
add_alias=string(default="")
mute_conversation=string(default="control+alt+win+back")
find = string(default="control+win+{")
vote=string(default="alt+win+shift+v")

View File

@@ -56,4 +56,6 @@ configuration = string(default="control+win+o")
accountConfiguration = string(default="control+win+shift+o")
update_buffer = string(default="control+win+shift+u")
open_in_browser = string(default="alt+control+win+return")
add_alias=string(default="")
add_alias=string(default="")
mute_conversation=string(default="alt+win+shift+delete")
vote=string(default="alt+win+shift+v")

View File

@@ -34,6 +34,7 @@ go_page_up = string(default="control+win+pageup")
go_page_down = string(default="control+win+pagedown")
update_profile = string(default="alt+win+p")
delete = string(default="control+win+delete")
edit_post = string(default="")
clear_buffer = string(default="control+win+shift+delete")
repeat_item = string(default="control+win+space")
copy_to_clipboard = string(default="control+win+shift+c")
@@ -58,4 +59,5 @@ update_buffer = string(default="control+win+shift+u")
ocr_image = string(default="win+alt+o")
open_in_browser = string(default="alt+control+win+return")
add_alias=string(default="")
mute_conversation=string(default="alt+win+shift+delete")
vote=string(default="alt+win+shift+v")

View File

@@ -54,4 +54,5 @@ actions = {
"update_buffer": _(u"Updates the buffer and retrieves possible lost items there."),
"ocr_image": _(u"Extracts the text from a picture and displays the result in a dialog."),
"add_alias": _("Adds an alias to an user"),
"mute_conversation": _("Mute/Unmute conversation"),
}

Binary file not shown.

View File

@@ -1,22 +1,23 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) 2019 ORGANIZATION
# FIRST AUTHOR <EMAIL@ADDRESS>, 2019.
# zvonimir stanecic <zvonimirek222@yandex.com>, 2023.
# zvonimir stanecic <zvonimirek222@yandex.com>, 2023, 2025.
msgid ""
msgstr ""
"Project-Id-Version: Tw Blue 0.80\n"
"Report-Msgid-Bugs-To: manuel@manuelcortez.net\n"
"POT-Creation-Date: 2025-04-13 01:18+0000\n"
"PO-Revision-Date: 2023-04-21 07:45+0000\n"
"PO-Revision-Date: 2025-08-10 16:08+0000\n"
"Last-Translator: zvonimir stanecic <zvonimirek222@yandex.com>\n"
"Language-Team: Polish <https://weblate.mcvsoftware.com/projects/twblue/"
"twblue/pl/>\n"
"Language: pl\n"
"Language-Team: Polish "
"<https://weblate.mcvsoftware.com/projects/twblue/twblue/pl/>\n"
"Plural-Forms: nplurals=3; plural=n==1 ? 0 : n%10>=2 && n%10<=4 && "
"(n%100<10 || n%100>=20) ? 1 : 2;\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
"|| n%100>=20) ? 1 : 2;\n"
"X-Generator: Weblate 5.10.4\n"
"Generated-By: Babel 2.17.0\n"
#: languageHandler.py:61
@@ -101,7 +102,7 @@ msgstr "Domyślne dla użytkownika"
#: main.py:105
#, fuzzy
msgid "https://twblue.mcvsoftware.com/donate"
msgstr "https://twblue.es/donate"
msgstr "https://twblue.mcvsoftware.com/donate"
#: main.py:118
#, python-brace-format
@@ -246,9 +247,8 @@ msgid "Following for {}"
msgstr "Śledzący użytkownika {}"
#: controller/messages.py:18
#, fuzzy
msgid "Translated"
msgstr "&Przetłumacz"
msgstr "Przetłumaczono"
#: controller/settings.py:60
msgid "System default"
@@ -540,9 +540,8 @@ msgid "There are no more items in this buffer."
msgstr "W tym buforze nie ma więcej elementów."
#: controller/mastodon/handler.py:30 wxUI/dialogs/mastodon/updateProfile.py:35
#, fuzzy
msgid "Update Profile"
msgstr "&Edytuj profil"
msgstr "Zaktualizuj profil"
#: controller/mastodon/handler.py:31 wxUI/dialogs/mastodon/search.py:10
#: wxUI/view.py:19
@@ -615,13 +614,12 @@ msgid "Add a&lias"
msgstr "Dodaj a&lias"
#: controller/mastodon/handler.py:51
#, fuzzy
msgid "Show user profile"
msgstr "&Pokaż profil użytkownika"
msgstr "Pokaż profil użytkownika"
#: controller/mastodon/handler.py:54
msgid "Create c&ommunity timeline"
msgstr ""
msgstr "Stwórz &oś czasu społeczności"
#: controller/mastodon/handler.py:55 wxUI/view.py:57
msgid "Create a &filter"
@@ -647,10 +645,9 @@ msgstr "Wyszukiwanie {}"
#: controller/mastodon/handler.py:111
msgid "Communities"
msgstr ""
msgstr "Społeczności"
#: controller/mastodon/handler.py:114
#, fuzzy
msgid "federated"
msgstr "federowana"
@@ -4864,4 +4861,3 @@ msgstr "Dodatki"
#~ msgid "DeepL API Key: "
#~ msgstr ""

View File

@@ -14,7 +14,7 @@ persist_size = integer(default=0)
load_cache_in_memory=boolean(default=True)
show_screen_names = boolean(default=False)
hide_emojis = boolean(default=False)
buffer_order = list(default=list('home', 'local', 'mentions', 'direct_messages', 'sent', 'favorites', 'bookmarks', 'followers', 'following', 'blocked', 'muted', 'notifications'))
buffer_order = list(default=list('home', 'local', 'mentions', 'direct_messages', 'sent', 'favorites', 'bookmarks', 'followers', 'following', 'blocked', 'muted', 'notifications', 'announcements'))
boost_mode = string(default="ask")
disable_streaming = boolean(default=False)
@@ -54,6 +54,7 @@ post = string(default="$display_name, $safe_text $image_descriptions $date. $vis
person = string(default="$display_name (@$screen_name). $followers followers, $following following, $posts posts. Joined $created_at.")
conversation = string(default="Conversation with $users. Last message: $last_post")
notification = string(default="$display_name $text, $date")
announcement = string(default="$text. Published $published_at. $read")
[filters]

View File

@@ -17,6 +17,11 @@ def compose_post(post, db, settings, relative_times, show_screen_names, safe=Tru
text = _("Boosted from @{}: {}").format(post.reblog.account.acct, templates.process_text(post.reblog, safe=safe))
else:
text = templates.process_text(post, safe=safe)
# Handle quoted posts
if hasattr(post, 'quote') and post.quote != None and hasattr(post.quote, 'quoted_status') and post.quote.quoted_status != None:
quoted_user = post.quote.quoted_status.account.acct
quoted_text = templates.process_text(post.quote.quoted_status, safe=safe)
text = text + " " + _("Quoting @{}: {}").format(quoted_user, quoted_text)
filtered = utils.evaluate_filters(post=post, current_context="home")
if filtered != None:
text = _("hidden by filter {}").format(filtered)
@@ -79,4 +84,10 @@ def compose_notification(notification, db, settings, relative_times, show_screen
filtered = utils.evaluate_filters(post=notification, current_context="notifications")
if filtered != None:
text = _("hidden by filter {}").format(filtered)
return [user, text, ts]
return [user, text, ts]
def compose_announcement(announcement, db, settings, relative_times, show_screen_names, safe=False):
# Use the default template or a configured one if available
template = settings.get("templates", {}).get("announcement", templates.announcement_default_template)
text = templates.render_announcement(announcement, template, settings, relative_times, db["utc_offset"])
return [text]

View File

@@ -222,9 +222,10 @@ class Session(base.baseSession):
in_reply_to_id = reply_to
for obj in posts:
text = obj.get("text")
scheduled_at = obj.get("scheduled_at")
if len(obj["attachments"]) == 0:
try:
item = self.api_call(call_name="status_post", status=text, _sound="tweet_send.ogg", in_reply_to_id=in_reply_to_id, visibility=visibility, sensitive=obj["sensitive"], spoiler_text=obj["spoiler_text"], language=language)
item = self.api_call(call_name="status_post", status=text, _sound="tweet_send.ogg", in_reply_to_id=in_reply_to_id, visibility=visibility, sensitive=obj["sensitive"], spoiler_text=obj["spoiler_text"], language=language, scheduled_at=scheduled_at)
# If it fails, let's basically send an event with all passed info so we will catch it later.
except Exception as e:
pub.sendMessage("mastodon.error_post", name=self.get_name(), reply_to=reply_to, visibility=visibility, posts=posts, lang=language)
@@ -241,13 +242,113 @@ class Session(base.baseSession):
for i in obj["attachments"]:
media = self.api_call("media_post", media_file=i["file"], description=i["description"], synchronous=True)
media_ids.append(media.id)
item = self.api_call(call_name="status_post", status=text, _sound="tweet_send.ogg", in_reply_to_id=in_reply_to_id, media_ids=media_ids, visibility=visibility, poll=poll, sensitive=obj["sensitive"], spoiler_text=obj["spoiler_text"], language=language)
item = self.api_call(call_name="status_post", status=text, _sound="tweet_send.ogg", in_reply_to_id=in_reply_to_id, media_ids=media_ids, visibility=visibility, poll=poll, sensitive=obj["sensitive"], spoiler_text=obj["spoiler_text"], language=language, scheduled_at=scheduled_at)
if item != None:
in_reply_to_id = item["id"]
except Exception as e:
pub.sendMessage("mastodon.error_post", name=self.get_name(), reply_to=reply_to, visibility=visibility, posts=posts, lang=language)
return
def edit_post(self, post_id, posts=[]):
""" Convenience function to edit a post. Only the first item in posts list is used as threads cannot be edited.
Note: According to Mastodon API, not all fields can be edited. Visibility, language, and reply context cannot be changed.
Args:
post_id: ID of the status to edit
posts: List with post data. Only first item is used.
Returns:
Updated status object or None on failure
"""
if len(posts) == 0:
log.warning("edit_post called with empty posts list")
return None
obj = posts[0]
text = obj.get("text")
if not text:
log.warning("edit_post called without text content")
return None
media_ids = []
media_attributes = []
try:
poll = None
# Handle poll attachments
if len(obj["attachments"]) == 1 and obj["attachments"][0]["type"] == "poll":
poll = self.api.make_poll(
options=obj["attachments"][0]["options"],
expires_in=obj["attachments"][0]["expires_in"],
multiple=obj["attachments"][0]["multiple"],
hide_totals=obj["attachments"][0]["hide_totals"]
)
log.debug("Editing post with poll (this will reset votes)")
# Handle media attachments
elif len(obj["attachments"]) > 0:
for i in obj["attachments"]:
# If attachment has an 'id', it's an existing media that we keep
if "id" in i:
media_ids.append(i["id"])
# If existing media has metadata to update, use generate_media_edit_attributes
if "description" in i or "focus" in i:
media_attr = self.api.generate_media_edit_attributes(
id=i["id"],
description=i.get("description"),
focus=i.get("focus")
)
media_attributes.append(media_attr)
# Otherwise it's a new file to upload
elif "file" in i:
description = i.get("description", "")
focus = i.get("focus", None)
media = self.api_call(
"media_post",
media_file=i["file"],
description=description,
focus=focus,
synchronous=True
)
media_ids.append(media.id)
log.debug("Uploaded new media with id: {}".format(media.id))
# Prepare parameters for status_update
update_params = {
"id": post_id,
"status": text,
"_sound": "tweet_send.ogg",
"sensitive": obj.get("sensitive", False),
"spoiler_text": obj.get("spoiler_text", None),
}
# Add optional parameters only if provided
if media_ids:
update_params["media_ids"] = media_ids
if media_attributes:
update_params["media_attributes"] = media_attributes
if poll:
update_params["poll"] = poll
# Call status_update API
log.debug("Editing post {} with params: {}".format(post_id, {k: v for k, v in update_params.items() if k not in ["_sound"]}))
item = self.api_call(call_name="status_update", **update_params)
if item:
log.info("Successfully edited post {}".format(post_id))
return item
except MastodonAPIError as e:
log.exception("Mastodon API error updating post {}: {}".format(post_id, str(e)))
output.speak(_("Error editing post: {}").format(str(e)))
pub.sendMessage("mastodon.error_edit", name=self.get_name(), post_id=post_id, error=str(e))
return None
except Exception as e:
log.exception("Unexpected error updating post {}: {}".format(post_id, str(e)))
output.speak(_("Error editing post: {}").format(str(e)))
return None
def get_name(self):
instance = self.settings["mastodon"]["instance"]
instance = instance.replace("https://", "")

View File

@@ -13,12 +13,14 @@ post_variables = ["date", "display_name", "screen_name", "source", "lang", "safe
person_variables = ["display_name", "screen_name", "description", "followers", "following", "favorites", "posts", "created_at"]
conversation_variables = ["users", "last_post"]
notification_variables = ["display_name", "screen_name", "text", "date"]
announcement_variables = ["text", "published_at", "updated_at", "starts_at", "ends_at", "read"]
# Default, translatable templates.
post_default_template = _("$display_name, $text $image_descriptions $date. $source")
dm_sent_default_template = _("Dm to $recipient_display_name, $text $date")
person_default_template = _("$display_name (@$screen_name). $followers followers, $following following, $posts posts. Joined $created_at.")
notification_default_template = _("$display_name $text, $date")
announcement_default_template = _("$text. Published $published_at. $read")
def process_date(field, relative_times=True, offset_hours=0):
original_date = arrow.get(field)
@@ -76,6 +78,13 @@ def render_post(post, template, settings, relative_times=False, offset_hours=0):
else:
text = process_text(post, safe=False)
safe_text = process_text(post)
# Handle quoted posts
if hasattr(post, 'quote') and post.quote != None and hasattr(post.quote, 'quoted_status') and post.quote.quoted_status != None:
quoted_user = post.quote.quoted_status.account.acct
quoted_text = process_text(post.quote.quoted_status, safe=False)
quoted_safe_text = process_text(post.quote.quoted_status, safe=True)
text = text + " " + _("Quoting @{}: {}").format(quoted_user, quoted_text)
safe_text = safe_text + " " + _("Quoting @{}: {}").format(quoted_user, quoted_safe_text)
filtered = utils.evaluate_filters(post=post, current_context="home")
if filtered != None:
text = _("hidden by filter {}").format(filtered)
@@ -178,3 +187,23 @@ def render_notification(notification, template, post_template, settings, relativ
result = Template(_(template)).safe_substitute(**available_data)
result = result.replace(" . ", "")
return result
def render_announcement(announcement, template, settings, relative_times=False, offset_hours=0):
""" Renders any given announcement according to the passed template. """
global announcement_variables
available_data = dict()
# Process dates
for date_field in ["published_at", "updated_at", "starts_at", "ends_at"]:
if hasattr(announcement, date_field) and getattr(announcement, date_field) is not None:
available_data[date_field] = process_date(getattr(announcement, date_field), relative_times, offset_hours)
else:
available_data[date_field] = ""
available_data["text"] = utils.html_filter(announcement.content)
if announcement.read:
available_data["read"] = _("Read")
else:
available_data["read"] = _("Unread")
result = Template(_(template)).safe_substitute(**available_data)
return result

View File

@@ -3,23 +3,47 @@ import demoji
from html.parser import HTMLParser
from datetime import datetime, timezone
url_re = re.compile('<a\s*href=[\'|"](.*?)[\'"].*?>')
url_re = re.compile(r'<a\s*href=[\'|"](.*?)[\'"].*?>')
class HTMLFilter(HTMLParser):
# Classes to ignore when parsing HTML
IGNORED_CLASSES = ["quote-inline"]
text = ""
first_paragraph = True
skip_depth = 0 # Track nesting depth of ignored elements
def handle_data(self, data):
self.text += data
# Only add data if we're not inside an ignored element
if self.skip_depth == 0:
self.text += data
def handle_starttag(self, tag, attrs):
if tag == "br":
self.text = self.text+"\n"
elif tag == "p":
if self.first_paragraph:
self.first_paragraph = False
else:
self.text = self.text+"\n\n"
# Check if this tag has a class that should be ignored
attrs_dict = dict(attrs)
tag_class = attrs_dict.get("class", "")
# Check if any ignored class is present in this tag
should_skip = any(ignored_class in tag_class for ignored_class in self.IGNORED_CLASSES)
if should_skip:
self.skip_depth += 1
elif self.skip_depth == 0: # Only process tags if we're not skipping
if tag == "br":
self.text = self.text+"\n"
elif tag == "p":
if self.first_paragraph:
self.first_paragraph = False
else:
self.text = self.text+"\n\n"
else:
# We're inside a skipped element, increment depth for nested tags
self.skip_depth += 1
def handle_endtag(self, tag):
# Decrement skip depth when closing any tag while skipping
if self.skip_depth > 0:
self.skip_depth -= 1
def html_filter(data):
f = HTMLFilter()
@@ -116,6 +140,11 @@ def evaluate_filters(post: dict, current_context: str) -> str | None:
- None if no applicable filters are found, meaning the post should be shown normally.
"""
filters = post.get("filtered", None)
# Automatically hide muted conversations from home timeline.
if current_context == "home" and post.get("muted") == True:
return "hide"
if filters == None:
return
warn_filter_title = None

View File

@@ -3,7 +3,7 @@ import sys
import application
import platform
import os
from cx_Freeze import setup, Executable, winmsvcr
from cx_Freeze import setup, Executable
from requests import certs
def get_architecture_files():
@@ -34,7 +34,7 @@ def find_accessible_output2_datafiles():
base = None
if sys.platform == 'win32':
base = 'Win32GUI'
base = 'GUI'
build_exe_options = dict(
build_exe="dist",
@@ -51,8 +51,6 @@ executables = [
Executable('main.py', base=base, target_name="twblue")
]
winmsvcr.FILES = ()
winmsvcr.FILES_TO_DUPLICATE = ()
setup(name=application.name,
version=application.version,
description=application.description,

View File

@@ -2,4 +2,5 @@
from .base import basePanel
from .conversationList import conversationListPanel
from .notifications import notificationsPanel
from .user import userPanel
from .user import userPanel
from .announcements import announcementsPanel

View File

@@ -0,0 +1,36 @@
# -*- coding: utf-8 -*-
import wx
from multiplatform_widgets import widgets
class announcementsPanel(wx.Panel):
def set_focus_function(self, f):
self.list.list.Bind(wx.EVT_LIST_ITEM_FOCUSED, f)
def create_list(self):
self.list = widgets.list(self, _("Announcement"), style=wx.LC_REPORT|wx.LC_SINGLE_SEL|wx.LC_VRULES)
self.list.set_windows_size(0, 800)
self.list.set_size()
def __init__(self, parent, name):
super(announcementsPanel, self).__init__(parent)
self.name = name
self.type = "baseBuffer"
self.sizer = wx.BoxSizer(wx.VERTICAL)
self.create_list()
self.dismiss = wx.Button(self, -1, _("Dismiss"))
btnSizer = wx.BoxSizer(wx.HORIZONTAL)
btnSizer.Add(self.dismiss, 0, wx.ALL, 5)
self.sizer.Add(btnSizer, 0, wx.ALL, 5)
self.sizer.Add(self.list.list, 1, wx.ALL|wx.EXPAND, 5)
self.SetSizer(self.sizer)
self.SetClientSize(self.sizer.CalcMin())
def set_position(self, reversed=False):
if reversed == False:
self.list.select_item(self.list.get_count()-1)
else:
self.list.select_item(0)
def set_focus_in_list(self):
self.list.list.SetFocus()

View File

@@ -9,10 +9,10 @@ class basePanel(wx.Panel):
def create_list(self):
self.list = widgets.list(self, _(u"User"), _(u"Text"), _(u"Date"), _(u"Client"), style=wx.LC_REPORT|wx.LC_SINGLE_SEL|wx.LC_VRULES)
self.list.set_windows_size(0, 60)
self.list.set_windows_size(1, 320)
self.list.set_windows_size(2, 110)
self.list.set_windows_size(3, 84)
self.list.set_windows_size(0, 200)
self.list.set_windows_size(1, 600)
self.list.set_windows_size(2, 200)
self.list.set_windows_size(3, 200)
self.list.set_size()
def __init__(self, parent, name):
@@ -35,7 +35,7 @@ class basePanel(wx.Panel):
btnSizer.Add(self.bookmark, 0, wx.ALL, 5)
btnSizer.Add(self.dm, 0, wx.ALL, 5)
self.sizer.Add(btnSizer, 0, wx.ALL, 5)
self.sizer.Add(self.list.list, 0, wx.ALL|wx.EXPAND, 5)
self.sizer.Add(self.list.list, 1, wx.ALL|wx.EXPAND, 5)
self.SetSizer(self.sizer)
self.SetClientSize(self.sizer.CalcMin())

View File

@@ -9,10 +9,10 @@ class conversationListPanel(wx.Panel):
def create_list(self):
self.list = widgets.list(self, _(u"User"), _(u"Text"), _(u"Date"), _(u"Client"), style=wx.LC_REPORT|wx.LC_SINGLE_SEL|wx.LC_VRULES)
self.list.set_windows_size(0, 60)
self.list.set_windows_size(1, 320)
self.list.set_windows_size(2, 110)
self.list.set_windows_size(3, 84)
self.list.set_windows_size(0, 200)
self.list.set_windows_size(1, 600)
self.list.set_windows_size(2, 200)
self.list.set_windows_size(3, 200)
self.list.set_size()
def __init__(self, parent, name):
@@ -27,7 +27,7 @@ class conversationListPanel(wx.Panel):
btnSizer.Add(self.post, 0, wx.ALL, 5)
btnSizer.Add(self.reply, 0, wx.ALL, 5)
self.sizer.Add(btnSizer, 0, wx.ALL, 5)
self.sizer.Add(self.list.list, 0, wx.ALL|wx.EXPAND, 5)
self.sizer.Add(self.list.list, 1, wx.ALL|wx.EXPAND, 5)
self.SetSizer(self.sizer)
self.SetClientSize(self.sizer.CalcMin())

View File

@@ -9,8 +9,8 @@ class notificationsPanel(wx.Panel):
def create_list(self):
self.list = widgets.list(self, _("Text"), _("Date"), style=wx.LC_REPORT|wx.LC_SINGLE_SEL|wx.LC_VRULES)
self.list.set_windows_size(0, 320)
self.list.set_windows_size(2, 110)
self.list.set_windows_size(0, 600)
self.list.set_windows_size(1, 200)
self.list.set_size()
def __init__(self, parent, name):
@@ -25,7 +25,7 @@ class notificationsPanel(wx.Panel):
btnSizer.Add(self.post, 0, wx.ALL, 5)
btnSizer.Add(self.dismiss, 0, wx.ALL, 5)
self.sizer.Add(btnSizer, 0, wx.ALL, 5)
self.sizer.Add(self.list.list, 0, wx.ALL|wx.EXPAND, 5)
self.sizer.Add(self.list.list, 1, wx.ALL|wx.EXPAND, 5)
self.SetSizer(self.sizer)
self.SetClientSize(self.sizer.CalcMin())

View File

@@ -6,7 +6,7 @@ class userPanel(wx.Panel):
def create_list(self):
self.list = widgets.list(self, _("User"), style=wx.LC_REPORT|wx.LC_SINGLE_SEL|wx.LC_VRULES)
self.list.set_windows_size(0, 320)
self.list.set_windows_size(0, 600)
self.list.set_size()
def __init__(self, parent, name):
@@ -23,7 +23,7 @@ class userPanel(wx.Panel):
btnSizer.Add(self.actions, 0, wx.ALL, 5)
btnSizer.Add(self.message, 0, wx.ALL, 5)
self.sizer.Add(btnSizer, 0, wx.ALL, 5)
self.sizer.Add(self.list.list, 0, wx.ALL|wx.EXPAND, 5)
self.sizer.Add(self.list.list, 1, wx.ALL|wx.EXPAND, 5)
self.SetSizer(self.sizer)
self.SetClientSize(self.sizer.CalcMin())

View File

@@ -47,7 +47,7 @@ class generalAccount(wx.Panel, baseDialog.BaseWXDialog):
self.SetSizer(sizer)
class templates(wx.Panel, baseDialog.BaseWXDialog):
def __init__(self, parent, post_template, conversation_template, person_template):
def __init__(self, parent, post_template, conversation_template, person_template, announcement_template):
super(templates, self).__init__(parent)
sizer = wx.BoxSizer(wx.VERTICAL)
self.post = wx.Button(self, wx.ID_ANY, _("Edit template for &posts. Current template: {}").format(post_template))
@@ -56,6 +56,8 @@ class templates(wx.Panel, baseDialog.BaseWXDialog):
sizer.Add(self.conversation, 0, wx.ALL, 5)
self.person = wx.Button(self, wx.ID_ANY, _("Edit template for p&ersons. Current template: {}").format(person_template))
sizer.Add(self.person, 0, wx.ALL, 5)
self.announcement = wx.Button(self, wx.ID_ANY, _("Edit template for &announcements. Current template: {}").format(announcement_template))
sizer.Add(self.announcement, 0, wx.ALL, 5)
self.SetSizer(sizer)
class sound(wx.Panel):
@@ -152,8 +154,8 @@ class configurationDialog(baseDialog.BaseWXDialog):
self.buffers = other_buffers(self.notebook)
self.notebook.AddPage(self.buffers, _(u"Buffers"))
def create_templates(self, post_template, conversation_template, person_template):
self.templates = templates(self.notebook, post_template=post_template, conversation_template=conversation_template, person_template=person_template)
def create_templates(self, post_template, conversation_template, person_template, announcement_template):
self.templates = templates(self.notebook, post_template=post_template, conversation_template=conversation_template, person_template=person_template, announcement_template=announcement_template)
self.notebook.AddPage(self.templates, _("Templates"))
def create_sound(self, output_devices, input_devices, soundpacks):

View File

@@ -31,7 +31,7 @@ class FilterKeywordPanel(wx.Panel):
button_sizer.Add(self.add_button, 0, wx.RIGHT, 5)
button_sizer.Add(self.remove_button, 0)
main_sizer = wx.BoxSizer(wx.VERTICAL)
main_sizer.Add(wx.StaticText(self, label=_("Palabras clave a filtrar:")), 0, wx.BOTTOM, 5)
main_sizer.Add(wx.StaticText(self, label=_("Keywords to filter:")), 0, wx.BOTTOM, 5)
main_sizer.Add(list_panel, 1, wx.EXPAND | wx.BOTTOM, 5)
main_sizer.Add(input_sizer, 0, wx.EXPAND | wx.BOTTOM, 5)
main_sizer.Add(button_sizer, 0, wx.ALIGN_RIGHT)

View File

@@ -8,10 +8,14 @@ class base(wx.Menu):
self.Append(self.boost)
self.reply = wx.MenuItem(self, wx.ID_ANY, _(u"Re&ply"))
self.Append(self.reply)
self.edit = wx.MenuItem(self, wx.ID_ANY, _(u"&Edit"))
self.Append(self.edit)
self.fav = wx.MenuItem(self, wx.ID_ANY, _(u"&Add to favorites"))
self.Append(self.fav)
self.unfav = wx.MenuItem(self, wx.ID_ANY, _(u"R&emove from favorites"))
self.Append(self.unfav)
self.mute = wx.MenuItem(self, wx.ID_ANY, _(u"Mute/Unmute conversation"))
self.Append(self.mute)
self.openUrl = wx.MenuItem(self, wx.ID_ANY, _("&Open URL"))
self.Append(self.openUrl)
self.openInBrowser = wx.MenuItem(self, wx.ID_ANY, _(u"&Open in instance"))
@@ -36,6 +40,8 @@ class notification(wx.Menu):
self.Append(self.boost)
self.reply = wx.MenuItem(self, wx.ID_ANY, _(u"Re&ply"))
self.Append(self.reply)
self.edit = wx.MenuItem(self, wx.ID_ANY, _(u"&Edit"))
self.Append(self.edit)
self.fav = wx.MenuItem(self, wx.ID_ANY, _(u"&Add to favorites"))
self.Append(self.fav)
self.unfav = wx.MenuItem(self, wx.ID_ANY, _(u"R&emove from favorites"))

View File

@@ -1,5 +1,7 @@
# -*- coding: utf-8 -*-
import wx
import wx.adv
import datetime
class Post(wx.Dialog):
def __init__(self, caption=_("Post"), text="", languages=[], *args, **kwds):
@@ -51,7 +53,7 @@ class Post(wx.Dialog):
visibility_sizer.Add(self.visibility, 0, 0, 0)
language_sizer = wx.BoxSizer(wx.HORIZONTAL)
post_actions_sizer.Add(language_sizer, 0, wx.RIGHT, 20)
lang_label = wx.StaticText(self, wx.ID_ANY, _("Language"))
lang_label = wx.StaticText(self, wx.ID_ANY, _("&Language"))
language_sizer.Add(lang_label, 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, 5)
self.language = wx.ComboBox(self, wx.ID_ANY, choices=languages, style=wx.CB_DROPDOWN | wx.CB_READONLY)
language_sizer.Add(self.language, 0, wx.ALIGN_CENTER_VERTICAL, 0)
@@ -60,6 +62,28 @@ class Post(wx.Dialog):
self.sensitive.SetValue(False)
self.sensitive.Bind(wx.EVT_CHECKBOX, self.on_sensitivity_changed)
main_sizer.Add(self.sensitive, 0, wx.ALL, 5)
# Scheduled post section
scheduled_box = wx.BoxSizer(wx.HORIZONTAL)
self.scheduled = wx.CheckBox(self, wx.ID_ANY, _("Schedule &post"))
self.scheduled.SetValue(False)
self.scheduled.Bind(wx.EVT_CHECKBOX, self.on_schedule_changed)
scheduled_box.Add(self.scheduled, 0, wx.ALL, 5)
# Default to now + 6 minutes to be safe for the 5 minute minimum
future_dt = wx.DateTime.Now()
future_dt.Add(wx.TimeSpan(0, 6, 0, 0))
self.date_picker = wx.adv.DatePickerCtrl(self, wx.ID_ANY, dt=future_dt, style=wx.adv.DP_DROPDOWN | wx.adv.DP_SHOWCENTURY)
self.date_picker.Enable(False)
scheduled_box.Add(self.date_picker, 0, wx.ALL, 5)
self.time_picker = wx.adv.TimePickerCtrl(self, wx.ID_ANY, dt=future_dt)
self.time_picker.Enable(False)
scheduled_box.Add(self.time_picker, 0, wx.ALL, 5)
main_sizer.Add(scheduled_box, 0, wx.ALL, 5)
spoiler_box = wx.BoxSizer(wx.HORIZONTAL)
spoiler_label = wx.StaticText(self, wx.ID_ANY, _("Content warning"))
self.spoiler = wx.TextCtrl(self, wx.ID_ANY)
@@ -80,8 +104,9 @@ class Post(wx.Dialog):
text_actions_sizer.Add(self.translate, 0, 0, 0)
btn_sizer = wx.StdDialogButtonSizer()
main_sizer.Add(btn_sizer, 0, wx.ALIGN_RIGHT | wx.ALL, 4)
self.send = wx.Button(self, wx.ID_OK, "")
self.send = wx.Button(self, wx.ID_ANY, _("&Send"))
self.send.SetDefault()
self.send.Bind(wx.EVT_BUTTON, self.validate_and_send)
btn_sizer.AddButton(self.send)
self.close = wx.Button(self, wx.ID_CLOSE, "")
btn_sizer.AddButton(self.close)
@@ -95,13 +120,50 @@ class Post(wx.Dialog):
""" Allows to react to certain keyboard events from the text control. """
shift=event.ShiftDown()
if event.GetKeyCode() == wx.WXK_RETURN and shift==False and hasattr(self,'send'):
self.EndModal(wx.ID_OK)
self.validate_and_send()
else:
event.Skip()
def validate_and_send(self, event=None):
scheduled_at = self.get_scheduled_at()
if scheduled_at:
min_time = datetime.datetime.now() + datetime.timedelta(minutes=5)
if scheduled_at < min_time:
wx.MessageDialog(self,
_("Scheduled posts must be set at least 5 minutes in the future. Please adjust the time."),
_("Invalid scheduled time"),
wx.ICON_ERROR | wx.OK).ShowModal()
return
self.EndModal(wx.ID_OK)
def on_sensitivity_changed(self, *args, **kwargs):
self.spoiler.Enable(self.sensitive.GetValue())
def on_schedule_changed(self, *args, **kwargs):
enabled = self.scheduled.GetValue()
self.date_picker.Enable(enabled)
self.time_picker.Enable(enabled)
def get_scheduled_at(self):
if not self.scheduled.GetValue():
return None
# Get date from date picker
wx_date = self.date_picker.GetValue()
# Get time from time picker
wx_time = self.time_picker.GetValue()
# Combine into a python datetime object
dt = datetime.datetime(
wx_date.GetYear(),
wx_date.GetMonth() + 1, # wx.DateTime months are 0-11
wx_date.GetDay(),
wx_time.GetHour(),
wx_time.GetMinute(),
wx_time.GetSecond()
)
return dt
def set_title(self, chars):
self.SetTitle(_("Post - {} characters").format(chars))
@@ -234,9 +296,9 @@ class viewPost(wx.Dialog):
def create_buttons_section(self, panel):
sizer = wx.BoxSizer(wx.HORIZONTAL)
self.mute = wx.Button(panel, wx.ID_ANY, _("Mute conversation"))
self.mute = wx.Button(panel, wx.ID_ANY, _("&Mute conversation"))
self.mute.Enable(False)
self.share = wx.Button(panel, wx.ID_ANY, _("Copy link to clipboard"))
self.share = wx.Button(panel, wx.ID_ANY, _("&Copy link to clipboard"))
self.share.Enable(False)
self.spellcheck = wx.Button(panel, wx.ID_ANY, _("Check &spelling..."))
self.translateButton = wx.Button(panel, wx.ID_ANY, _("&Translate..."))
@@ -295,7 +357,7 @@ class poll(wx.Dialog):
sizer_1 = wx.BoxSizer(wx.VERTICAL)
period_sizer = wx.BoxSizer(wx.HORIZONTAL)
sizer_1.Add(period_sizer, 1, wx.EXPAND, 0)
label_period = wx.StaticText(self, wx.ID_ANY, _("Participation time"))
label_period = wx.StaticText(self, wx.ID_ANY, _("&Participation time"))
period_sizer.Add(label_period, 0, 0, 0)
self.period = wx.ComboBox(self, wx.ID_ANY, choices=[_("5 minutes"), _("30 minutes"), _("1 hour"), _("6 hours"), _("1 day"), _("2 days"), _("3 days"), _("4 days"), _("5 days"), _("6 days"), _("7 days")], style=wx.CB_DROPDOWN | wx.CB_READONLY | wx.CB_SIMPLE)
self.period.SetFocus()
@@ -305,36 +367,36 @@ class poll(wx.Dialog):
sizer_1.Add(sizer_2, 1, wx.EXPAND, 0)
option1_sizer = wx.BoxSizer(wx.HORIZONTAL)
sizer_2.Add(option1_sizer, 1, wx.EXPAND, 0)
label_2 = wx.StaticText(self, wx.ID_ANY, _("Option 1"))
label_2 = wx.StaticText(self, wx.ID_ANY, _("Option &1"))
option1_sizer.Add(label_2, 0, 0, 0)
self.option1 = wx.TextCtrl(self, wx.ID_ANY, "")
self.option1.SetMaxLength(25)
option1_sizer.Add(self.option1, 0, 0, 0)
option2_sizer = wx.BoxSizer(wx.HORIZONTAL)
sizer_2.Add(option2_sizer, 1, wx.EXPAND, 0)
label_3 = wx.StaticText(self, wx.ID_ANY, _("Option 2"))
label_3 = wx.StaticText(self, wx.ID_ANY, _("Option &2"))
option2_sizer.Add(label_3, 0, 0, 0)
self.option2 = wx.TextCtrl(self, wx.ID_ANY, "")
self.option2.SetMaxLength(25)
option2_sizer.Add(self.option2, 0, 0, 0)
option3_sizer = wx.BoxSizer(wx.HORIZONTAL)
sizer_2.Add(option3_sizer, 1, wx.EXPAND, 0)
label_4 = wx.StaticText(self, wx.ID_ANY, _("Option 3"))
label_4 = wx.StaticText(self, wx.ID_ANY, _("Option &3"))
option3_sizer.Add(label_4, 0, 0, 0)
self.option3 = wx.TextCtrl(self, wx.ID_ANY, "")
self.option3.SetMaxLength(25)
option3_sizer.Add(self.option3, 0, 0, 0)
option4_sizer = wx.BoxSizer(wx.HORIZONTAL)
sizer_2.Add(option4_sizer, 1, wx.EXPAND, 0)
label_5 = wx.StaticText(self, wx.ID_ANY, _("Option 4"))
label_5 = wx.StaticText(self, wx.ID_ANY, _("Option &4"))
option4_sizer.Add(label_5, 0, 0, 0)
self.option4 = wx.TextCtrl(self, wx.ID_ANY, "")
self.option4.SetMaxLength(25)
option4_sizer.Add(self.option4, 0, 0, 0)
self.multiple = wx.CheckBox(self, wx.ID_ANY, _("Allow multiple choices per user"))
self.multiple = wx.CheckBox(self, wx.ID_ANY, _("&Allow multiple choices per user"))
self.multiple.SetValue(False)
sizer_1.Add(self.multiple, 0, wx.ALL, 5)
self.hide_votes = wx.CheckBox(self, wx.ID_ANY, _("Hide votes count until the poll expires"))
self.hide_votes = wx.CheckBox(self, wx.ID_ANY, _("&Hide votes count until the poll expires"))
self.hide_votes.SetValue(False)
sizer_1.Add(self.hide_votes, 0, wx.ALL, 5)
btn_sizer = wx.StdDialogButtonSizer()

View File

@@ -141,7 +141,7 @@ class ShowUserProfile(wx.Dialog):
mainSizer.Add(privateSizer, 0, wx.ALL | wx.CENTER)
botSizer = wx.BoxSizer(wx.HORIZONTAL)
botLabel = wx.StaticText(self.panel, label=_("&Bot account: "))
botLabel = wx.StaticText(self.panel, label=_("B&ot account: "))
botText = self.createTextCtrl(bullSwitch[user.bot], (30, 30))
botSizer.Add(botLabel, wx.SizerFlags().Center())
botSizer.Add(botText, wx.SizerFlags().Center())
@@ -154,7 +154,7 @@ class ShowUserProfile(wx.Dialog):
discoverSizer.Add(discoverText, wx.SizerFlags().Center())
mainSizer.Add(discoverSizer, 0, wx.ALL | wx.CENTER)
posts = wx.Button(self.panel, label=_("{} p&osts. Click to open posts timeline").format(user.statuses_count))
posts = wx.Button(self.panel, label=_("{} pos&ts. Click to open posts timeline").format(user.statuses_count))
# posts.SetToolTip(_("Click to open {}'s posts").format(user.display_name))
posts.Bind(wx.EVT_BUTTON, self.onPost)
mainSizer.Add(posts, wx.SizerFlags().Center())

View File

@@ -119,7 +119,7 @@ class UpdateProfileDialog(wx.Dialog):
self.locked = wx.CheckBox(panel, label=_("&Private account"))
self.locked.SetValue(locked)
self.bot = wx.CheckBox(panel, label=_("&Bot account"))
self.bot = wx.CheckBox(panel, label=_("B&ot account"))
self.bot.SetValue(bot)
self.discoverable = wx.CheckBox(panel, label=_("&Discoverable account"))
self.discoverable.SetValue(discoverable)

View File

@@ -26,7 +26,7 @@ class EditTemplateDialog(wx.Dialog):
sizer_3.AddButton(self.button_SAVE)
self.button_CANCEL = wx.Button(self, wx.ID_CANCEL)
sizer_3.AddButton(self.button_CANCEL)
self.button_RESTORE = wx.Button(self, wx.ID_ANY, _("Restore template"))
self.button_RESTORE = wx.Button(self, wx.ID_ANY, _("&Restore template"))
self.button_RESTORE.Bind(wx.EVT_BUTTON, self.on_restore)
sizer_3.AddButton(self.button_CANCEL)
sizer_3.Realize()

View File

@@ -22,11 +22,11 @@ class UserListDialog(wx.Dialog):
user_list_sizer.Add(self.user_list, 1, wx.EXPAND | wx.ALL, 10)
main_sizer.Add(user_list_sizer, 1, wx.EXPAND | wx.ALL, 15)
buttons_sizer = wx.BoxSizer(wx.HORIZONTAL)
self.actions_button = wx.Button(panel, wx.ID_ANY, "Actions")
self.actions_button = wx.Button(panel, wx.ID_ANY, "&Actions")
buttons_sizer.Add(self.actions_button, 0, wx.RIGHT, 10)
self.details_button = wx.Button(panel, wx.ID_ANY, _("View profile"))
self.details_button = wx.Button(panel, wx.ID_ANY, _("&View profile"))
buttons_sizer.Add(self.details_button, 0, wx.RIGHT, 10)
close_button = wx.Button(panel, wx.ID_CANCEL, "Close")
close_button = wx.Button(panel, wx.ID_CANCEL, "&Close")
buttons_sizer.Add(close_button, 0)
main_sizer.Add(buttons_sizer, 0, wx.ALIGN_CENTER | wx.BOTTOM, 15)
panel.SetSizer(main_sizer)

View File

@@ -19,7 +19,7 @@ class mainFrame(wx.Frame):
self.menuitem_search = self.menubar_application.Append(wx.ID_ANY, _(u"&Search"))
self.lists = self.menubar_application.Append(wx.ID_ANY, _(u"&Lists manager"))
self.lists.Enable(False)
self.manageAliases = self.menubar_application.Append(wx.ID_ANY, _("Manage user aliases"))
self.manageAliases = self.menubar_application.Append(wx.ID_ANY, _("M&anage user aliases"))
self.keystroke_editor = self.menubar_application.Append(wx.ID_ANY, _(u"&Edit keystrokes"))
self.account_settings = self.menubar_application.Append(wx.ID_ANY, _(u"Account se&ttings"))
self.prefs = self.menubar_application.Append(wx.ID_PREFERENCES, _(u"&Global settings"))
@@ -56,7 +56,7 @@ class mainFrame(wx.Frame):
self.trends = self.menubar_buffer.Append(wx.ID_ANY, _(u"New &trending topics buffer..."))
self.filter = self.menubar_buffer.Append(wx.ID_ANY, _(u"Create a &filter"))
self.manage_filters = self.menubar_buffer.Append(wx.ID_ANY, _(u"&Manage filters"))
self.find = self.menubar_buffer.Append(wx.ID_ANY, _(u"Find a string in the currently focused buffer..."))
self.find = self.menubar_buffer.Append(wx.ID_ANY, _(u"F&ind a string in the currently focused buffer..."))
self.load_previous_items = self.menubar_buffer.Append(wx.ID_ANY, _(u"&Load previous items"))
self.menubar_buffer.AppendSeparator()
self.mute_buffer = self.menubar_buffer.AppendCheckItem(wx.ID_ANY, _(u"&Mute"))
@@ -66,8 +66,8 @@ class mainFrame(wx.Frame):
# audio menu
self.menubar_audio = wx.Menu()
self.seekLeft = self.menubar_audio.Append(wx.ID_ANY, _(u"&Seek back 5 seconds"))
self.seekRight = self.menubar_audio.Append(wx.ID_ANY, _(u"&Seek forward 5 seconds"))
self.seekLeft = self.menubar_audio.Append(wx.ID_ANY, _(u"Seek &back 5 seconds"))
self.seekRight = self.menubar_audio.Append(wx.ID_ANY, _(u"Seek &forward 5 seconds"))
# Help Menu
self.menubar_help = wx.Menu()
@@ -134,9 +134,9 @@ class mainFrame(wx.Frame):
self.buffers[name] = buffer.GetId()
def prepare(self):
self.sizer.Add(self.nb, 0, wx.ALL, 5)
self.sizer.Add(self.nb, 1, wx.ALL | wx.EXPAND, 5)
self.panel.SetSizer(self.sizer)
# self.Maximize()
self.Maximize()
self.sizer.Layout()
self.SetClientSize(self.sizer.CalcMin())
# print self.GetSize()

View File

@@ -1,7 +1,9 @@
{"current_version": "2025.03.08",
"description": "Initial filter support, added pinned posts. Fixed some minor issues.",
"date": "unknown",
{
"current_version": "2025.03.08",
"description": "Added support for editing and scheduling Mastodon posts, improved quoted posts reading, and added server announcements buffer. Includes visual stability fixes and keyboard shortcut improvements.",
"date": "2026-01-12",
"downloads":
{"Windows32": "https://github.com/MCV-Software/TWBlue/releases/download/v2025.03.08/TWBlue_portable_v2024.05.23.zip",
"Windows64": "https://github.com/MCV-Software/TWBlue/releases/download/v2025.03.08/TWBlue_portable_v2025.03.08.zip"}
{
"Windows64": "https://github.com/MCV-Software/TWBlue/releases/download/v2026.01.12/TWBlue_portable_v2026.01.12.zip"
}
}