Everything is in scrollback.db and there is no way to get it out: no export of a
conversation, no plain-text or JSON dump. HexChat and WeeChat write logs by default;
Element and Discord both export.
Agreed design (2026-09-11)
Scoped in discussion. This is larger than the original filing, and supersedes it.
Where it is reached from
The conversation's avatar and nameplate at the top of the screen
(main-header in src/renderer/src/App.tsx). Clicking it opens a menu carrying the
same options as the buffer's right-click menu, plus Export.
Deliberately not in the BufferList right-click menu. Export is a rare action and
that menu is already long; putting it there costs every user something to serve a few.
Making the nameplate clickable also gives the header a menu it does not have today,
which is worth having on its own.
This needs the entry builder currently inline in BufferList.tsx (~line 812) hoisted
into something both callers share, and BufferFace extended past kind === 'dm' so
channels have a face to click.
The range dialog
Clicking Export opens a dialog asking for the date/time range. Defaults to
everything; the range is for somebody who knows they want one month.
What is produced
An HTML representation of exactly what is in the chat buffer - the conversation
moved 1:1 to local disk, not a reformatting of it.
Written into a folder, not a single file.
Embedded media is downloaded into that folder and referenced locally, so the
export still reads with no network and after the original expires.
Lands in the default or user-defined Downloads location.
Fetching
If the requested range needs more history than is held locally, it is paginated from
the service, and that pagination respects the service's rate limits. A long export
is expected to take a while; it is not expected to get the account limited.
Progress, and stopping it
An indicator on the settings cog while an export is running, with a way to cancel.
A row in the Downloads pane (the one XDCC transfers use), showing progress and
speed, with pause and cancel where they can be supported.
Modelled as a transfer rather than as a parallel mechanism: the transfers table, the
panel, the progress and speed rendering and the cancel path all exist already and are
the right shape. Needs a kind on the row so the panel can describe an export
differently from a DCC file.
Not doing
Live per-buffer log files, which the original filing offered as an option. Everything is
already durably in scrollback.db with search and backfill; a live plain-text log is a
second copy that can drift, and it exists in HexChat mainly because HexChat has no
database. Export covers the need that matters.
Everything is in `scrollback.db` and there is no way to get it out: no export of a
conversation, no plain-text or JSON dump. HexChat and WeeChat write logs by default;
Element and Discord both export.
## Agreed design (2026-09-11)
Scoped in discussion. This is larger than the original filing, and supersedes it.
### Where it is reached from
The conversation's **avatar and nameplate at the top of the screen**
(`main-header` in `src/renderer/src/App.tsx`). Clicking it opens a menu carrying the
same options as the buffer's right-click menu, **plus Export**.
Deliberately **not** in the BufferList right-click menu. Export is a rare action and
that menu is already long; putting it there costs every user something to serve a few.
Making the nameplate clickable also gives the header a menu it does not have today,
which is worth having on its own.
This needs the entry builder currently inline in `BufferList.tsx` (~line 812) hoisted
into something both callers share, and `BufferFace` extended past `kind === 'dm'` so
channels have a face to click.
### The range dialog
Clicking Export opens a dialog asking for the **date/time range**. Defaults to
everything; the range is for somebody who knows they want one month.
### What is produced
An **HTML representation of exactly what is in the chat buffer** - the conversation
moved 1:1 to local disk, not a reformatting of it.
- Written into a **folder**, not a single file.
- **Embedded media is downloaded** into that folder and referenced locally, so the
export still reads with no network and after the original expires.
- Lands in the **default or user-defined Downloads location**.
### Fetching
If the requested range needs more history than is held locally, it is **paginated from
the service**, and that pagination **respects the service's rate limits**. A long export
is expected to take a while; it is not expected to get the account limited.
### Progress, and stopping it
- An **indicator on the settings cog** while an export is running, with a way to cancel.
- A row in the **Downloads pane** (the one XDCC transfers use), showing progress and
speed, with **pause and cancel** where they can be supported.
Modelled as a transfer rather than as a parallel mechanism: the `transfers` table, the
panel, the progress and speed rendering and the cancel path all exist already and are
the right shape. Needs a `kind` on the row so the panel can describe an export
differently from a DCC file.
### Not doing
Live per-buffer log files, which the original filing offered as an option. Everything is
already durably in `scrollback.db` with search and backfill; a live plain-text log is a
second copy that can drift, and it exists in HexChat mainly because HexChat has no
database. Export covers the need that matters.
History pagination needed no new code, which was the good outcome: getBacklog
with a before already pages older history per service - Discord snowflakes,
Kick cursors, IRC CHATHISTORY, Matrix /messages tokens - each with its own
pacing. The export loop drives that rather than growing a second implementation
that would have to learn the same four things again. Sneedchat is the one that
cannot reach back, and the dialog says so before starting rather than quietly
returning short.
Verified end to end against a daemon on its own socket: a folder with
index.html and media, the body part cleaned up on sealing, the transfer row
reading kind=export, pause and resume, cancel refusing further writes, a second
export of the same conversation on the same day landing beside the first, and a
title of "../../escape" staying inside the downloads folder.
527 daemon tests and typecheck green. Deployed.
Finished.
Daemon — https://git.salastil.com/Salastil/nobilis/commit/2e9bd0d (export engine, transfer kind, pause), https://git.salastil.com/Salastil/nobilis/commit/f3a92e6 (time-paged range reads)
Window — https://git.salastil.com/Salastil/moho/commit/a68752e (the loop, dialog, downloads row, cog), https://git.salastil.com/Salastil/moho/commit/ad06b4e (header menu, the way in)
History pagination needed no new code, which was the good outcome: `getBacklog`
with a `before` already pages older history per service - Discord snowflakes,
Kick cursors, IRC CHATHISTORY, Matrix `/messages` tokens - each with its own
pacing. The export loop drives that rather than growing a second implementation
that would have to learn the same four things again. Sneedchat is the one that
cannot reach back, and the dialog says so before starting rather than quietly
returning short.
Verified end to end against a daemon on its own socket: a folder with
index.html and media, the body part cleaned up on sealing, the transfer row
reading kind=export, pause and resume, cancel refusing further writes, a second
export of the same conversation on the same day landing beside the first, and a
title of "../../escape" staying inside the downloads folder.
527 daemon tests and typecheck green. Deployed.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Everything is in
scrollback.dband there is no way to get it out: no export of aconversation, no plain-text or JSON dump. HexChat and WeeChat write logs by default;
Element and Discord both export.
Agreed design (2026-09-11)
Scoped in discussion. This is larger than the original filing, and supersedes it.
Where it is reached from
The conversation's avatar and nameplate at the top of the screen
(
main-headerinsrc/renderer/src/App.tsx). Clicking it opens a menu carrying thesame options as the buffer's right-click menu, plus Export.
Deliberately not in the BufferList right-click menu. Export is a rare action and
that menu is already long; putting it there costs every user something to serve a few.
Making the nameplate clickable also gives the header a menu it does not have today,
which is worth having on its own.
This needs the entry builder currently inline in
BufferList.tsx(~line 812) hoistedinto something both callers share, and
BufferFaceextended pastkind === 'dm'sochannels have a face to click.
The range dialog
Clicking Export opens a dialog asking for the date/time range. Defaults to
everything; the range is for somebody who knows they want one month.
What is produced
An HTML representation of exactly what is in the chat buffer - the conversation
moved 1:1 to local disk, not a reformatting of it.
export still reads with no network and after the original expires.
Fetching
If the requested range needs more history than is held locally, it is paginated from
the service, and that pagination respects the service's rate limits. A long export
is expected to take a while; it is not expected to get the account limited.
Progress, and stopping it
speed, with pause and cancel where they can be supported.
Modelled as a transfer rather than as a parallel mechanism: the
transferstable, thepanel, the progress and speed rendering and the cancel path all exist already and are
the right shape. Needs a
kindon the row so the panel can describe an exportdifferently from a DCC file.
Not doing
Live per-buffer log files, which the original filing offered as an option. Everything is
already durably in
scrollback.dbwith search and backfill; a live plain-text log is asecond copy that can drift, and it exists in HexChat mainly because HexChat has no
database. Export covers the need that matters.
Finished.
Daemon — https://git.salastil.com/Salastil/nobilis/commit/2e9bd0d (export engine, transfer kind, pause), https://git.salastil.com/Salastil/nobilis/commit/f3a92e6 (time-paged range reads)
Window — https://git.salastil.com/Salastil/moho/commit/a68752e (the loop, dialog, downloads row, cog), https://git.salastil.com/Salastil/moho/commit/ad06b4e (header menu, the way in)
History pagination needed no new code, which was the good outcome:
getBacklogwith a
beforealready pages older history per service - Discord snowflakes,Kick cursors, IRC CHATHISTORY, Matrix
/messagestokens - each with its ownpacing. The export loop drives that rather than growing a second implementation
that would have to learn the same four things again. Sneedchat is the one that
cannot reach back, and the dialog says so before starting rather than quietly
returning short.
Verified end to end against a daemon on its own socket: a folder with
index.html and media, the body part cleaned up on sealing, the transfer row
reading kind=export, pause and resume, cancel refusing further writes, a second
export of the same conversation on the same day landing beside the first, and a
title of "../../escape" staying inside the downloads folder.
527 daemon tests and typecheck green. Deployed.