@@ -288,26 +288,14 @@ struct LogTableView: View {
290 .onChange(of: records) { _, newRecords in
290 .onChange(of: records) { _, newRecords in
293− widthTask = Task.detached(priority: .userInitiated) {
294− let widths = computeColumnWidths(columns: cols, records: newRecords)
291+ recomputeColumnWidths(columns: columns, records: newRecords)
295− guard !Task.isCancelled else { return }
296− await MainActor.run {
297− // Preserve any width the user dragged or auto-fit; only fill
298− // in columns that don't have a width yet (e.g. newly appearing
299− // columns). A wholesale overwrite would discard manual resizes
300− // on every re-query/filter/refresh.
301− var merged = columnWidths
302− for (col, width) in widths where merged[col] == nil {
305− columnWidths = merged
308 debouncedRebuildSort()
292 debouncedRebuildSort()
310 .onChange(of: columns) { _, newColumns in
294 .onChange(of: columns) { _, newColumns in
295+ // New columns (e.g. shown via the Column Manager) need widths
296+ // computed; the width task otherwise only runs on a records
297+ // change, leaving them at the default width until the next query.
298+ recomputeColumnWidths(columns: newColumns, records: records)
311 // After a stream switch the previous sort column may not exist
299 // After a stream switch the previous sort column may not exist
312 // in the new schema, which would silently produce a no-op sort
300 // in the new schema, which would silently produce a no-op sort
313 // (every row compares as .null). Drop it so rows fall back to
301 // (every row compares as .null). Drop it so rows fall back to
@@ -326,17 +314,33 @@ struct LogTableView: View {
326 .onChange(of: sortColumn) { _, _ in debouncedRebuildSort() }
314 .onChange(of: sortColumn) { _, _ in debouncedRebuildSort() }
327 .onChange(of: sortAscending) { _, _ in debouncedRebuildSort() }
315 .onChange(of: sortAscending) { _, _ in debouncedRebuildSort() }
328 .onAppear {
316 .onAppear {
329− let cols = columns
317+ recomputeColumnWidths(columns: columns, records: records)
331− widthTask = Task.detached(priority: .userInitiated) {
332− let widths = computeColumnWidths(columns: cols, records: recs)
333− await MainActor.run { columnWidths = widths }
335 rebuildSort()
318 rebuildSort()
323+ /// Computes column widths off the main actor and merges them into
324+ /// `columnWidths`, filling only columns that don't yet have a width.
325+ /// Preserves any width the user dragged or auto-fit; a wholesale overwrite
326+ /// would discard manual resizes on every re-query/filter/refresh. Runs on
327+ /// both records changes and columns-only changes so newly shown columns get
328+ /// a fitted width rather than the default.
329+ private func recomputeColumnWidths(columns cols: [String], records recs: [LogRecord]) {
331+ widthTask = Task.detached(priority: .userInitiated) {
332+ let widths = computeColumnWidths(columns: cols, records: recs)
333+ guard !Task.isCancelled else { return }
334+ await MainActor.run {
335+ var merged = columnWidths
336+ for (col, width) in widths where merged[col] == nil {
339+ columnWidths = merged
340 private func debouncedRebuildSort() {
344 private func debouncedRebuildSort() {
341 sortTask?.cancel()
345 sortTask?.cancel()
342 sortTask = Task {
346 sortTask = Task {