Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@
All notable changes to Third Brain. Versions follow [semantic versioning](https://semver.org/). The app version is
`version` in `app/package.json`; the desktop app (`desktop/package.json`) keeps the same number.

## 0.1.8 - 2026-11-03

- **EzyChat:** the **Add EzyChat** dialog no longer shows "Counting chats..." for ever before EzyChat is connected.
Until the sign-in, each history choice shows the cost per chat, as in the Gmail dialog. When EzyChat is already
connected, the dialog is called **Reconnect EzyChat**.

## 0.1.7 - 2026-11-03

- **EzyChat connector:** the customer chats of an EzyChat team inbox (Lite or Enterprise, any number of inboxes)
Expand Down
2 changes: 1 addition & 1 deletion app/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "mailsync",
"version": "0.1.7",
"version": "0.1.8",
"private": true,
"type": "module",
"scripts": {
Expand Down
4 changes: 3 additions & 1 deletion app/portal/portal.js
Original file line number Diff line number Diff line change
Expand Up @@ -787,11 +787,13 @@ function openEzyChatDialog() {
else for (const c of m.choices) box.querySelector(`[data-line="${c.days}"]`).textContent = c.line;
};
form.reset(); $('#ezychat-error').hidden = true;
$('#ezychat-title').textContent = status.ezychat?.connected ? 'Reconnect EzyChat' : 'Add EzyChat';
if (status.ezychat?.address) form.elements.address.value = status.ezychat.address;
draw(true);
dialog.showModal();
form.elements.address.focus();
if (status.ezychat?.connected) void fetch('/api/backfill/estimate?feed=ezychat', { cache: 'no-store' })
// Asked also before the sign-in (like Gmail): the server then answers without counts, so the choices show per-chat figures.
void fetch('/api/backfill/estimate?feed=ezychat', { cache: 'no-store' })
.then(async r => { const d = await r.json().catch(() => null); return r.ok && d?.choices ? d : 'unavailable'; }, () => 'unavailable')
.then(est => { if (seq === ezySeq && dialog.open) { ctx.est = est; draw(false); } });
}
Expand Down
2 changes: 1 addition & 1 deletion app/portal/view.js
Original file line number Diff line number Diff line change
Expand Up @@ -762,7 +762,7 @@ export function windowDialogModel({ est, status, feed, mode, current, claude })
else if (claude && (mode === 'add' || (loaded && !est.counted))) notes.push('Chat counts appear once this account is signed in; until then each choice shows the cost per chat.');
else if (est === 'unavailable') notes.push(`${gmail ? 'Thread' : 'Chat'} counts appear after the portal restarts; until then each choice shows the cost per ${one}.`);
if (wa) notes.push('Counts cover the chats that become notes now, and only the history the phone sent at linking.');
if (ezy) notes.push(mode === 'add' ? 'Chat counts appear once EzyChat is connected. Each chat makes one note per month with messages.' : 'Each chat makes one note per month with messages.');
if (ezy) notes.push(status?.ezychat?.connected ? 'Each chat makes one note per month with messages.' : 'Chat counts appear once EzyChat is connected. Each chat makes one note per month with messages.');
if (loaded && est.perChat.source === 'default') notes.push(`Per-${one} figures are a typical measurement; they become your own after a few runs.`);
if (loaded && est.counted && est.choices.some(c => c.approximate)) notes.push(`Counts marked ≈ are estimated from your most recent ${many}.`);
notes.push(`Time assumes ${number(threadsPerRun)} conversations per run, one run every 15 minutes, shared with your other connectors.`);
Expand Down
8 changes: 8 additions & 0 deletions app/test/ezychat.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -404,5 +404,13 @@ describe("view", () => {
expect(m.title).toBe("How far back should EzyChat chats go?");
expect(m.selected).toBe(30);
expect(m.choices[0].line).toBe("Counting chats…");
// Before the sign-in the server answers without counts: per-chat figures, not "Counting chats…" for ever.
const uncounted = { counted: false, perChat: { in: 8000, out: 400, source: "default" }, threadsPerRun: 25, writer: { provider: "codex" },
choices: [30, 182, 365, 730].map(days => ({ days, chats: null, approximate: false, inputTokens: null, hours: null })) };
const before = windowDialogModel({ est: uncounted, status: { ...base, ezychat: { connected: false } }, feed: "ezychat", mode: "add", claude: false });
expect(before.choices[0].line).toBe("≈ 8.0 k tokens in and 400 out per chat");
expect(before.notes.join(" ")).toContain("Chat counts appear once EzyChat is connected");
const reconnect = windowDialogModel({ est: null, status: { ...base, ezychat: { connected: true } }, feed: "ezychat", mode: "add", claude: false });
expect(reconnect.notes.join(" ")).not.toContain("once EzyChat is connected");
});
});
2 changes: 1 addition & 1 deletion desktop/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "thirdbrain-desktop",
"productName": "Third Brain",
"version": "0.1.7",
"version": "0.1.8",
"private": true,
"description": "Desktop window and tray icon for the Third Brain portal. The background service does the work.",
"license": "Apache-2.0",
Expand Down
Loading