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
21 changes: 21 additions & 0 deletions src/tui/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2042,6 +2042,7 @@ export class TuiController {
const REFRESH_DEBOUNCE_MS = 300;
let refreshTimer: ReturnType<typeof setTimeout> | null = null;
let refreshFallbackIndex: number | null = null;
// Watcher for database directory changes.
let dataWatcher: fs.FSWatcher | null = null;
let isShuttingDown = false;

Expand All @@ -2067,6 +2068,12 @@ export class TuiController {
const dataDir = pathImpl.dirname(dataPath);
const dataFile = pathImpl.basename(dataPath);
try {
// Initialize lastJsonlMtime from the current JSONL export if present so
// subsequent watch events can compare against a known baseline. If the
// stat fails, leave lastJsonlMtime as null which will cause watch events
// to be ignored until a successful stat occurs.
// No longer consult a JSONL mtime for watch decisions; always refresh
// on observed database directory events (debounced).
// Watch for changes to either the main DB file or the WAL file.
// In SQLite WAL mode, changes are written to the -wal file first,
// so we need to watch both files to detect all database changes.
Expand All @@ -2080,6 +2087,20 @@ export class TuiController {
watchDebounce = setTimeout(() => {
watchDebounce = null;
const selectedIndex = typeof list.selected === 'number' ? (list.selected as number) : 0;
// If the watcher provided a specific filename (eg. 'worklog.db' or
// 'worklog.db-wal') then refresh unconditionally — the event
// directly refers to the database file. When filename is undefined
// we only refresh when the transient JSONL export mtime changed to
// avoid unnecessary reloads caused by unrelated directory changes.
if (filename) {
scheduleRefreshFromDatabase(selectedIndex);
return;
}
// Always refresh on debounced directory events. The watcher may
// provide a filename for the DB or WAL; when it does we refresh
// immediately. When filename is undefined (some platforms) we also
// refresh — we no longer rely on a JSONL export mtime to gate
// updates.
scheduleRefreshFromDatabase(selectedIndex);
}, 75);
});
Expand Down
12 changes: 8 additions & 4 deletions test/tui-integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -585,20 +585,24 @@ describe('TUI integration: style preservation', () => {
const onWatch = watchCallbacks[0];
const baselineCalls = listMock.mock.calls.length;

// We no longer consult a JSONL mtime for watch decisions; every
// debounced directory event should schedule a refresh. Verify that
// each debounced callback results in an additional db.list() call.
onWatch('change', undefined);
await vi.advanceTimersByTimeAsync(400);
expect(listMock.mock.calls.length).toBe(baselineCalls);
expect(listMock.mock.calls.length).toBeGreaterThan(baselineCalls);

const afterFirstCalls = listMock.mock.calls.length;
dataMtimeMs = 2000;
onWatch('change', undefined);
await vi.advanceTimersByTimeAsync(400);
expect(listMock.mock.calls.length).toBeGreaterThan(baselineCalls);
expect(listMock.mock.calls.length).toBeGreaterThan(afterFirstCalls);

const afterChangedMtimeCalls = listMock.mock.calls.length;
const afterSecondCalls = listMock.mock.calls.length;
mtimeReadError = true;
onWatch('change', undefined);
await vi.advanceTimersByTimeAsync(400);
expect(listMock.mock.calls.length).toBe(afterChangedMtimeCalls);
expect(listMock.mock.calls.length).toBeGreaterThan(afterSecondCalls);

vi.useRealTimers();
});
Expand Down
14 changes: 14 additions & 0 deletions tmux.windows.conf.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
session_name: Dev
windows:
- name: "ContextHub"
cwd: "~/projects/ContextHub"
panes:
- cmd: "opencode -c"
- cmd: "wl tui"
split:
dir: vertical
size_pct: 50
- cmd: "clear"
split:
dir: horizontal
size_pct: 30
Loading