Skip to content
Merged

Vcvk #305

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
0fb72f7
Format the translated message through telegram entities (0.1.1-dev.19)
claude Aug 6, 2026
5a07031
Unstick the install button after cancelling deps; send files into the…
claude Aug 6, 2026
39f8b97
Merge branch 'main' into vcvk
shareui Aug 6, 2026
da893ec
Bind sticker icons at runtime and stand in while they load (0.1.1-dev…
claude Aug 6, 2026
8f38cb3
Resolve sticker sets the way the host does (0.1.1-dev.22)
claude Aug 6, 2026
bbbc31b
Stop the icon catalog ticker from stranding previews (0.1.1-dev.23)
claude Aug 6, 2026
6e8feda
Stop cropping the round catalog buttons' icons (0.1.1-dev.24)
claude Aug 6, 2026
368dcdd
Stage suggestion uploads with a write the volume accepts (0.1.1-dev.25)
claude Aug 7, 2026
ddc53a9
Fold the overflowing tag into "+N" instead of squeezing it (0.1.1-dev…
claude Aug 7, 2026
d20bdbd
Count what the list holds and keep an emptied filter empty (0.1.1-dev…
claude Aug 7, 2026
e62bf08
Version 0.1.1
claude Aug 8, 2026
c325f22
Merge branch 'main' into vcvk
shareui Aug 8, 2026
bb713ea
Rewrite the sources screen as a fragment with repository cards (0.1.2…
claude Aug 12, 2026
7f0bbde
Keep the share link resolvable and the url inside its field (0.1.2-de…
claude Aug 12, 2026
4f3fe8a
Let the host's switch keep its own colours (0.1.2-dev.3)
claude Aug 12, 2026
7a84034
Accept a repomap with a trailing comma (0.1.2-dev.4)
claude Aug 12, 2026
b9cc116
Stop the layout helper from weighting fixed-size views (0.1.2-dev.5)
claude Aug 12, 2026
ad381b2
Set the switch up the way the client's own plugin card does (0.1.2-de…
claude Aug 12, 2026
3aafcbb
Harmonize the repository palette with the system one (0.1.2-dev.7)
claude Aug 12, 2026
45e8b2f
Use the client's loading button in the add dialog (0.1.2-dev.8)
claude Aug 12, 2026
723ddc3
Restore the default repository instead of adding another (0.1.2-dev.9)
claude Aug 12, 2026
825a3a8
Say the link was sent, not copied (0.1.2-dev.10)
claude Aug 12, 2026
8c8e3dc
Report whether the source is on, not how old its cache is
claude Aug 12, 2026
78945aa
Repaint repository cards instead of rebuilding them
claude Aug 12, 2026
46a27a6
Cap repository names at 32 characters
claude Aug 12, 2026
979513b
Stop clipping the switch on repository cards
claude Aug 12, 2026
748632e
Do not let a repository's name decide whether it works
claude Aug 12, 2026
8d63265
Split the card's tap from its switch, and fix back
claude Aug 12, 2026
fa3e4cc
Make the sources toolbar the same component as the catalogue's
claude Aug 12, 2026
2ae762d
Give the sources screen something to say
claude Aug 12, 2026
d4df568
One accent, no translucent fills, and the client's switch box back
claude Aug 12, 2026
7fe75b1
Give the card two rows that start and end in the same places
claude Aug 13, 2026
6aa8197
Put the card's bottom half on one line
claude Aug 13, 2026
784b827
Label the manage button, move the count to the left edge
claude Aug 13, 2026
75d31ed
Move the fetch time off the card, pin the installed pill left
claude Aug 13, 2026
b55390f
Show the repository in the add sheet, not a folder glyph
claude Aug 13, 2026
ef04e64
Reword the add-sheet disclaimer
claude Aug 13, 2026
3fa74b2
Cut the add-sheet disclaimer to one line
claude Aug 13, 2026
858b1f8
Use the wording the author asked for on the add sheet
claude Aug 13, 2026
1d67307
Separate the subtitle from the fine print on the add sheet
claude Aug 13, 2026
69b1c19
Scale the add sheet's prose and balance its line breaks
claude Aug 13, 2026
18c2719
Ask a repository through one module
claude Aug 14, 2026
e183caa
Split the repomap cache out into utils/cachedRepos
claude Aug 14, 2026
c03e8eb
Lowercase every package, PascalCase every module
claude Aug 14, 2026
7e2e592
Sort the modules into folders that say what they are
claude Aug 14, 2026
f1544b5
Write down where things go
claude Aug 14, 2026
b958d6a
Move the python tree under src/python
claude Aug 14, 2026
7768b2a
Move kotlin under src, and ship one dex instead of four
claude Aug 14, 2026
6f3a2fa
снес все нахуй
shareui Aug 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 109 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,114 @@
# Contributing

## Where things live

```
packit/
meta.yml plugin name, id, version, minimum client and SDK
locales/ strings_{en,ru,de,be}.json — the only place UI text belongs
res/ fonts, sounds, drawables shipped with the plugin
dex/ packit.dex — all of kawaii.packetik, built from src/kotlin
native/ .so files per ABI
src/
python/ the plugin itself, everything below
kotlin/ src/ and the compile-only Xposed stubs

scripts/ one-off tooling; yours goes in scripts/{username}/
linux/kotlin-build.sh Kotlin -> packit/dex/packit.dex
linux/build-native.sh C -> packit/native/<abi>/
```

`src/` holds one folder per language the plugin is written in, and `src/python`
is the package root — the path `refmap.yml` and the builder's `source:` both
point at. Move it and those two have to move with it.

There is one dex, not one per Kotlin package. R8 emits a single `classes.dex`
from all of the sources, so splitting it by name only ever shipped the same
bytes several times over. Add a class under `kawaii.packetik.*`, rerun
`kotlin-build.sh`, and reach it from `core/DexLoader.py` by its fully qualified
name — nothing else needs to change.

It is laid out by what a module *is*, not by which client screen it happens to
touch:

```
src/python/
BasePlugin.py the entry point — the class the loader looks for
Main.py startup, hooks, lifecycle

core/ installing and removing plugins, loading dexes and native
libraries, the repository list itself
network/ everything that goes over the wire to a repository
utils/ helpers with no UI of their own, including where files
live on disk
scl/ the TOML parser (native-backed) used for .afp files and
plugin export

ui/ the plugin's own screens
MainActivity.py builds the plugin's settings root
components/ pieces screens are assembled from — never a screen
dialogs/ sheets and dialogs that belong to no single screen
settings/ the plugin's settings pages
plugins/ plugin/ icons/ repos/ updates/ files/
achievements/ contributors/ suggest/

integrations/ code that reaches into a screen the *client* owns
chat/ the chat screen: import sheets, inline mode, security
chatlist/ the dialogs list: buttons, widgets, update sheet
hooks/ hooks into the client's own settings and fragments
decorations/ badges and title icons drawn into client UI

deeplinks/ one module per tg://packit?… route, dispatched by
DeepHandler
```

### Where do I put a new file?

| What you are adding | Where it goes |
|---|---|
| A screen of the plugin's own | `ui/<screen>/Fragment.py`, with its sheets and helpers beside it |
| A dialog or sheet used by one screen | that screen's package |
| A dialog or sheet used by several | `ui/dialogs/` |
| A reusable widget, or a view helper | `ui/components/` |
| A page in the plugin's settings | `ui/settings/subsettings/` and register it in `ui/settings/Settings.py` |
| Something drawn into the client's chat, chat list or profile | `integrations/<area>/` |
| A hook into a client class | `integrations/hooks/` |
| A new `tg://packit?…` route | `deeplinks/<Route>.py`, registered in `deeplinks/DeepHandler.py` |
| A pure helper — parsing, hashing, paths, formatting | `utils/` |
| Anything that downloads from a repository | `network/Storage.py` — do not add a second one |
| Anything that reads `reposCache/{rm_rid}.json` | `utils/CachedRepos.py` — same rule |
| User-visible text | `packit/locales/strings_*.json`, all four in lockstep |

If a file does not obviously belong anywhere, that is usually a sign it does
two things. Split it before inventing a folder for it.

### Naming

- **Folders are lowercase**, no separators: `ui/plugins`, `integrations/chatlist`.
- **Modules are PascalCase**: `CachedRepos.py`, `AddSheet.py`, `EnterView.py`.
- Two names are fixed and must not be renamed: `BasePlugin.py`, which
`refmap.yml` and the builder's `compilationIgnore` both point at by path, and
`__init__.py`, which is Python's.

### Imports

All imports inside `src/python` are relative — `from ..utils import Paths`,
never an absolute path from the package root. When you move a file, remember that
the number of dots changes with its depth.

Two modules are the only way to reach a repository, and which one you want is
readable from the call:

```python
from ..utils import CachedRepos # off disk, no network, safe anywhere
from ..network import Storage # over the network, never on the UI thread

url = CachedRepos.plugins_url(repo)
plugins, error = Storage.fetch_plugins(url)
```

---

## Logging

**Always use `logx` from `packutil` for all logs. Never use `log` from `android_utils` directly.**
Expand Down
10 changes: 7 additions & 3 deletions packit/.elyxbuilder/config.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
zipFormat: eaf
source: packit/src
source: packit/src/python
buildNameUncompiled: '{name}-{version}'
buildNameCompiled: '{name}-{version}-3.11'
# fnmatch against each file's full path in the archive, so a bare directory
# never matches anything — the trailing /* is what does the work
ignoreAll:
- packit/.elyxbuilder/cache/*
- packit/docs/
- packit/docs/*
# the kotlin sources build into packit/dex/packit.dex; only the dex ships
- packit/src/kotlin/*
compilationIgnore:
- packit/src/BasePlugin.py
- packit/src/python/BasePlugin.py
obfuscationConfig:
stripDocstrings: true
removeLogs: false
Expand Down
4 changes: 2 additions & 2 deletions packit/.elyxbuilder/stats.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
builds: 553
compiledBuilds: 17
builds: 557
compiledBuilds: 21
failedBuilds: 9
uncompiledBuilds: 527
Binary file removed packit/dex/catalog.dex
Binary file not shown.
Binary file removed packit/dex/openfile.dex
Binary file not shown.
File renamed without changes.
Binary file removed packit/dex/sfx.dex
Binary file not shown.
55 changes: 40 additions & 15 deletions packit/locales/strings_be.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,7 @@
"contributors": "Удзельнікі",
"max_repositories_allowed": "Максімум 10 рэпазіторыяў. Расслабься.",
"max_buttons_allowed": "Максімум 4 кнопкі. Расслабься.",
"fill_previous_repository": "Спачатку запоўні папярэдні рэпазіторый. Сур'ёзна.",
"add_repository": "Дадаць рэпазіторый",
"repository_form": "Рэпазіторый #{0}",
"delete_repository_title": "Выдаліць",
"delete_repository_message": "Ты ўпэўнены, што хочаш выдаліць гэты рэпазіторый? Назад шляху няма.",
"delete_button": "Выдаліць",
Expand All @@ -33,9 +31,6 @@
"achiev_unlocked": "Дасягненне разблакавана!",
"repo_link_copied": "Скапіявана ў буфер! Магія.",
"failed_to_copy": "Не атрымалася скапіяваць :(",
"repo_icon_text": "Іконка: {0}",
"repo_icon_not_selected": "Іконка: не выбрана",
"repo_enabled": "Уключана",
"repo_name": "Назва",
"repo_url": "URL канфігурацыі",
"invalid_repository": "Няправільны рэпазіторый",
Expand Down Expand Up @@ -99,7 +94,6 @@
"file_not_found": "Файл не знойдзены :(",
"error_opening_settings": "Памылка адкрыцця налад: {0}",
"error_generic": "Памылка: {0}",
"icon_selected": "Іконка \"{0}\" выбрана! Крута.",
"arrows_navigation": "Стрэлкі і навігацыя",
"attachments_media": "Укладанні і медыя",
"avatar_profile": "Аватар і профіль",
Expand Down Expand Up @@ -175,6 +169,7 @@
"repo_add_fetching": "Загрузка дадзеных рэпазіторыя...",
"repo_add_fetch_failed": "Не атрымалася загрузіць дадзеныя рэпазіторыя.",
"repo_add_disclaimer": "PackIt не нясе адказнасці за змест {0}. Усе правы і адказнасць належаць уладальніку, {1}.\\n\\nКолькасць плагінаў: {2}",
"repo_add_disclaimer_short": "PackIt не адказвае за змест старонніх крыніц.",
"repo_add_no_repometa": "Рэпазіторый не мае метададзеных. Дадаць усё роўна?\\n\\nКолькасць плагінаў: {0}",
"sort_title": "Сартаваць плагіны",
"sort_title_icons": "Сартаванне набораў іконак",
Expand Down Expand Up @@ -827,8 +822,8 @@
"achiev_hint_secret_utils_rule": "кстати цябе врадли выложат у utilits. Ты па факту паўтарыў kpm. А як бы ў utils правіла другі варыянт нельга выкладваць",
"achiev_title_secret_aytist": "Ты знайшоў аметыст!",
"achiev_hint_secret_aytist": "Ты сапраўды знайшоў аметыст, цяпер табе не трэба працаваць да канца жыцця...",
"achiev_title_secret_opsec": "opsec усталяваны",
"achiev_hint_secret_opsec": "sudo packit install opsec — гатова. Інкогніта актывавана, акуляры надзеты, ніхто нічога не бачыў.",
"achiev_title_secret_opsec": "Аперацыйная бяспека",
"achiev_hint_secret_opsec": "Ты ўсталяваў opsec прывілеяваным спосабам: sudo packit install opsec.",
"tags_section_title": "Тэгі",
"apply_button": "Ужыць",
"authors_section_title": "Аўтары",
Expand Down Expand Up @@ -1136,8 +1131,6 @@
"role_founder": "Заснавальнік",
"achiev_title_secret_connect_is_bullshit": "Усе мы ненавідзім Connect",
"achiev_hint_secret_connect_is_bullshit": "Малююць багіню Умі Асанагі, не ведаючы анатомію чалавека... Гарэць ім у пекле.",
"achiev_title_secret_opsec": "Аперацыйная бяспека",
"achiev_hint_secret_opsec": "Ты ўсталяваў opsec прывілеяваным спосабам: sudo packit install opsec.",
"pp_closed_source_moderated": "Закрыты зыходны код, але з мадэрацыяй, таму вы ў бяспецы.",
"beta_build_title": "Бэта-зборка",
"beta_build_msg": "Вы не тэсціроўшчык, вам не варта было ўсталёўваць PackIt.",
Expand All @@ -1149,10 +1142,6 @@
"pp_no_license": "Няма ліцэнзіі",
"pp_languages": "Мовы",
"pp_clients": "Кліенты",
"pp_closed_source_moderated": "Закрыты зыходны код, але з мадэрацыяй, таму вы ў бяспецы.",
"beta_build_title": "Бэта-зборка",
"beta_build_msg": "Вы не тэсціроўшчык, вам не варта было ўсталёўваць PackIt.",
"delete_packit": "Выдаліць PackIt",
"debug_menu": "Меню адладкі",
"debug_menu_desc": "Меню для адладкі",
"debug_logs": "Логі адладкі",
Expand Down Expand Up @@ -1183,5 +1172,41 @@
"bi_root_no": "Не",
"bi_app_version": "Версія праграмы",
"bi_app_package": "Пакет праграмы",
"plus_sponsor": "+ Спонсар"
"plus_sponsor": "+ Спонсар",
"repo_card_status_missing": "Не загружаны",
"repo_card_plugins": "{0} плагінаў",
"repo_card_icons": "{0} набораў",
"repo_card_installed": "Усталявана: {0}",
"repo_edit": "Змяніць",
"repo_copy_link": "Скапіяваць спасылку",
"repos_updating": "Абнаўленне крыніц…",
"repo_add_sheet_title": "Новая крыніца",
"repo_add_sheet_subtitle": "Устаўце спасылку на repomap.json",
"repo_add_field_url": "Спасылка",
"repo_sheet_edit_title": "Змяніць крыніцу",
"repo_sheet_edit_sub": "Назва і спасылка",
"repo_err_empty": "Увядзіце спасылку",
"repo_err_name_empty": "Увядзіце назву",
"repo_err_scheme": "Спасылка мусіць пачынацца з https://",
"repo_err_duplicate": "Гэтая крыніца ўжо дададзена",
"repo_err_not_found": "Файл не знойдзены",
"repo_err_forbidden": "Доступ забаронены",
"repo_err_rate_limited": "Занадта шмат запытаў, паспрабуйце пазней",
"repo_err_redirect": "Спасылка перанакіроўвае — патрэбна простая спасылка на файл",
"repo_err_timeout": "Час чакання скончыўся",
"repo_err_server": "Памылка на баку сервера",
"repo_err_http": "Сервер адказаў: {0}",
"repo_err_json": "Файл не з'яўляецца карэктным JSON",
"repo_err_meta": "У файле няма блока repometa",
"repo_err_rid": "У repometa няма rm_rid",
"repo_err_name": "У repometa няма rm_name",
"repo_err_cache": "Не атрымалася захаваць кэш",
"repo_err_network": "Няма злучэння",
"repo_err_unknown": "Невядомая памылка: {0}",
"repos_empty_title": "Пакуль пуста",
"repos_empty_text": "Дадайце крыніцу, каб ставіць плагіны",
"repos_manage": "Кіраванне",
"retry": "Паўтарыць",
"repo_default_already": "Стандартная крыніца ўжо на месцы",
"repo_link_shared": "Спасылка на крыніцу адпраўлена"
}
45 changes: 38 additions & 7 deletions packit/locales/strings_de.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,7 @@
"contributors": "Mitwirkende",
"max_repositories_allowed": "Maximal 10 Repositorys zulässig. Kühlen.",
"max_buttons_allowed": "Maximal 4 Tasten zulässig. Kühlen.",
"fill_previous_repository": "Füllen Sie zuerst das vorherige Repository aus. Ja, wirklich.",
"add_repository": "Repository hinzufügen",
"repository_form": "Repository Nr.{0}",
"delete_repository_title": "Löschen",
"delete_repository_message": "Sind Sie sicher, dass Sie dieses Repository löschen möchten? Keine Rücknahme.",
"delete_button": "Löschen",
Expand All @@ -33,9 +31,6 @@
"achiev_unlocked": "Erfolg freigeschaltet!",
"repo_link_copied": "In die Zwischenablage kopiert! Magie.",
"failed_to_copy": "Kopieren fehlgeschlagen :(",
"repo_icon_text": "Symbol: {0}",
"repo_icon_not_selected": "Symbol: nicht ausgewählt",
"repo_enabled": "Ermöglicht",
"repo_name": "Name",
"repo_url": "Konfigurations-URL",
"invalid_repository": "Ungültiges Repository",
Expand Down Expand Up @@ -99,7 +94,6 @@
"file_not_found": "Datei nicht gefunden :(",
"error_opening_settings": "Fehler beim Öffnen der Einstellungen: {0}",
"error_generic": "Fehler: {0}",
"icon_selected": "Symbol „{0}“ ausgewählt! Schick.",
"arrows_navigation": "Pfeile und Navigation",
"attachments_media": "Anhänge und Medien",
"avatar_profile": "Avatar & Profil",
Expand Down Expand Up @@ -175,6 +169,7 @@
"repo_add_fetching": "Repository-Daten werden abgerufen...",
"repo_add_fetch_failed": "Das Abrufen der Repository-Daten ist fehlgeschlagen.",
"repo_add_disclaimer": "PackIt ist nicht für den Inhalt von {0} verantwortlich. Alle Rechte und Pflichten liegen beim Eigentümer {1}.\n\nPlugin-Anzahl: {2}",
"repo_add_disclaimer_short": "PackIt ist nicht für den Inhalt fremder Quellen verantwortlich.",
"repo_add_no_repometa": "Das Repository verfügt über keine Metadaten. Trotzdem hinzufügen?\n\nPlugin-Anzahl: {0}",
"sort_title": "Plugins sortieren",
"sort_title_icons": "Symbolpakete sortieren",
Expand Down Expand Up @@ -1177,5 +1172,41 @@
"bi_root_no": "Nein",
"bi_app_version": "App-Version",
"bi_app_package": "App-Paket",
"plus_sponsor": "+ Sponsor"
"plus_sponsor": "+ Sponsor",
"repo_card_status_missing": "Nicht geladen",
"repo_card_plugins": "{0} Plugins",
"repo_card_icons": "{0} Icon-Sets",
"repo_card_installed": "{0} installiert",
"repo_edit": "Bearbeiten",
"repo_copy_link": "Link kopieren",
"repos_updating": "Quellen werden aktualisiert…",
"repo_add_sheet_title": "Neue Quelle",
"repo_add_sheet_subtitle": "Link zur repomap.json einfügen",
"repo_add_field_url": "Link",
"repo_sheet_edit_title": "Quelle bearbeiten",
"repo_sheet_edit_sub": "Name und Link",
"repo_err_empty": "Link eingeben",
"repo_err_name_empty": "Namen eingeben",
"repo_err_scheme": "Der Link muss mit https:// beginnen",
"repo_err_duplicate": "Diese Quelle ist bereits hinzugefügt",
"repo_err_not_found": "Datei nicht gefunden",
"repo_err_forbidden": "Zugriff verweigert",
"repo_err_rate_limited": "Zu viele Anfragen, später erneut versuchen",
"repo_err_redirect": "Der Link leitet weiter — direkter Dateilink nötig",
"repo_err_timeout": "Zeitüberschreitung",
"repo_err_server": "Serverfehler",
"repo_err_http": "Server antwortete: {0}",
"repo_err_json": "Die Datei ist kein gültiges JSON",
"repo_err_meta": "Die Datei hat keinen repometa-Block",
"repo_err_rid": "repometa hat kein rm_rid",
"repo_err_name": "repometa hat kein rm_name",
"repo_err_cache": "Cache konnte nicht gespeichert werden",
"repo_err_network": "Keine Verbindung",
"repo_err_unknown": "Unbekannter Fehler: {0}",
"repos_empty_title": "Noch nichts da",
"repos_empty_text": "Füge eine Quelle hinzu, um Plugins zu installieren",
"repos_manage": "Verwalten",
"retry": "Erneut versuchen",
"repo_default_already": "Die Standardquelle ist bereits vorhanden",
"repo_link_shared": "Link zur Quelle gesendet"
}
Loading
Loading