Inject the summary card only once the answer starts streaming
Publish plugin / publish (push) Successful in 3s

Pressing Summarize used to inject a status card immediately, at a guessed
anchor, and move it once the fetch returned. Any failure before the model
answered (fetch error, empty history, unreachable host, bad key, HTTP error)
then left an error bubble in the chat and had already scrolled the user away
from where they were reading.

The pipeline is now fetch -> stream -> card. A bulletin acknowledges the
press; every failure before the first chunk is a bulletin only and leaves the
chat untouched. On the first chunk the card is created with its final anchor
and virtual id and placed strictly after the newest analyzed message. If that
message is not loaded, the chat is asked to load it and placement retries; the
card never falls back to the chat bottom. Placement then hard-jumps the layout
manager to the card's row: scrollToMessageId only smooth-scrolls from cell
positions that are stale right after an insert. The provisional anchor and
reanchor() machinery is gone.

The row is reserved with INSERT .. DO NOTHING, so re-summarizing a saved range
no longer blanks its text before the new answer arrives; a fresh row whose
stream breaks is deleted instead of left empty.

Also:
- Finished summaries end with #aisummaries as a native TL_iv.textHashtag
  (rendered by RichMessageLayout like a hashtag entity) and carry a plain-text
  body with a TL_messageEntityHashtag.
- Deleting a card (MessagesController.deleteMessages, all overloads) removes
  its saved row and the card, with a "Summary deleted" bulletin.
- Settings show the saved summary count and approximate size, and a confirmed
  "Clear all summaries" action.
- Version 1.1.0.

Verified on device: no card before streaming, correct placement and jump,
restore on reopen, deletion, and the hashtag rendering. Telegram's search is
server-side, so local cards do not appear in its results.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
lyuksovannyyandClaude Opus 5.5 committed 2026-10-06 23:19:06 +02:00
1 parent b2105ca753
commit 16bf8de84c
10 files changed
+1123 -382

No files matched your search

+27 -1
View File
@@ -28,6 +28,31 @@ redirects `sys.stdout`/`sys.stderr` to the logcat tags `python.stdout`/`python.s
This repo does not use the official Elyx/ElyxBuilder archive format — see
`docs/sdk-notes.md` for why.
## What the plugin does
**Summarize** (⋮ → Plugins → Summarize, or hold the unread button) runs in this order:
1. A bulletin confirms the press, then the history is fetched.
2. The transcript streams to the configured OpenAI-compatible endpoint.
3. Only when the first chunk arrives is a summary card injected, directly after the newest
analyzed message. If that message is not loaded (an offset, or history scrolled away),
the chat is asked to load it first. The chat then jumps to the card once.
4. Reasoning and answer stream into the card. The finished summary ends with a native
`#aisummaries` hashtag and is saved to a local SQLite store, from which it is restored
whenever the chat is reopened.
Any failure before step 3 (fetch error, empty history, unreachable host, bad key, HTTP
error) is reported as a bulletin only: the chat is not modified and the view does not move.
A failure mid-stream is written onto the card, and a summary saved earlier for the same
range is kept.
Deleting a card like any other message removes its saved row. The settings page shows how
many summaries are saved and roughly how much space they take, and offers **Clear all
summaries** (with a confirmation step).
Summaries exist only on this device, so Telegram's own search, which runs on the server,
does not find them, including a search for `#aisummaries`.
## Repo layout
```
@@ -38,7 +63,8 @@ src/ the plugin, written as an ordinary Python package
diagnostics.py bounded in-memory log, mirrored to a file (logcat is restricted here)
providers/ endpoint settings, prompt assembly and SSE streaming
services/ history fetch, anonymized transcript, markdown→RichMessage, injection, SQLite store
hooks/ Xposed hooks (unread-button entry points, summary restore on chat open)
hooks/ Xposed hooks (unread-button entry points, summary restore on chat open,
forgetting a summary when its card is deleted)
ui/ summarize dialog, bulletin and theme helpers
tools/
bundle.py compiles plugin.json + src/ into dist/<id>.plugin