@@ -266,8 +266,8 @@ export function Table({
266266 const [ { sort : sortColumn , dir : sortDirection , view : activeViewId } , setTableParams ] =
267267 useQueryStates ( tableDetailParsers , tableDetailUrlKeys )
268268
269- // Read-only mirrors for the resolve effect: it must know whether the user has
270- // already applied a filter / hidden columns without re-running when they change.
269+ // Read-only mirrors for the resolve effect and replaceFilter's echo check:
270+ // both must read the current values without re-running when they change.
271271 const filterRef = useRef ( filter )
272272 filterRef . current = filter
273273 const hiddenColumnsRef = useRef ( hiddenColumns )
@@ -428,9 +428,13 @@ export function Table({
428428 * this an open panel keeps showing the rules of the filter it replaced.
429429 *
430430 * The remount discards an unapplied draft, which is the point — the rules on
431- * screen must be the rules in effect.
431+ * screen must be the rules in effect. An incoming filter identical to the
432+ * current one is skipped entirely: the resolve effect re-applies the config
433+ * after this client's own autosave settles, and letting that echo remount an
434+ * open panel would wipe keystrokes typed since the flush and steal focus.
432435 */
433436 const replaceFilter = useCallback ( ( next : TablePredicate | null ) => {
437+ if ( JSON . stringify ( next ) === JSON . stringify ( filterRef . current ) ) return
434438 setFilter ( next )
435439 setFilterSeed ( ( seed ) => seed + 1 )
436440 } , [ ] )
@@ -1208,7 +1212,9 @@ export function Table({
12081212 * "Filter by cell value" from the grid's cell context menu. Narrows the
12091213 * PRUNED filter, so a condition the current schema already invalidated is not
12101214 * resurrected, and opens the panel — a silently narrowed table would leave the
1211- * user no way to see what was applied.
1215+ * user no way to see what was applied. Persists explicitly: the reseeded
1216+ * panel starts signature-matched to this filter, so its debounce alone would
1217+ * never save it.
12121218 */
12131219 const handleFilterByCellValue = ( conditions : readonly Predicate [ ] ) => {
12141220 const next = withCellValueFilter ( effectiveFilter , conditions )
@@ -1434,8 +1440,9 @@ export function Table({
14341440 // a one-line query forward.
14351441 const { data : executionLog } = useLogByExecutionId ( workspaceId , executionId )
14361442
1437- // Stable identity so the memoized Resource.Options can bail — an inline
1438- // object literal (with an inline arrow) would defeat its memo every render.
1443+ // Identity only changes with filterOpen (the flush targets the open panel),
1444+ // so unrelated parent re-renders still let the memoized Resource.Options
1445+ // bail; filterConfig below re-memoizes on filterOpen anyway.
14391446 const handleToggleFilter = useCallback ( ( ) => {
14401447 if ( filterOpen ) tableFilterRef . current ?. flush ( )
14411448 setFilterOpen ( ! filterOpen )
0 commit comments