improvise-y5eb: read the declined cell through the frame's own origin

declined_at indexed the cache with the cursor's offsets, which the
client's own outgoing frame moves before the host's projection arrives.
A wrapping Down read the old window's top row and closed an editor on
Revenue/Feb with Profit's refusal. projected_at reads against
AxisFrame::first, so a stale cache answers None instead.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
This commit is contained in:
Edward Langley
2026-09-23 18:36:34 -07:00
parent 347b2154a5
commit 100627fd57
+9 -1
View File
@@ -1040,13 +1040,21 @@ fn reopened_at(state: &ClientState, row: usize, col: usize, mode: AppMode) -> Ve
/// Why `i` declines the cell the host projected at `(row, col)`. `None` for an
/// editable cell, and for one not projected yet, which the fold settles later.
pub(crate) fn declined_at(state: &ClientState, row: usize, col: usize) -> Option<String> {
match cell_at(state, row, col)?.id.as_ref()? {
match projected_at(state, row, col)?.id.as_ref()? {
CellId::Aggregate(_) => Some(AGGREGATE_DECLINE.to_string()),
CellId::Derived { target } => Some(derived_decline(target)),
CellId::Record(_) | CellId::Create(_) | CellId::Coordinate => None,
}
}
/// The cell the host projected at an absolute position, read against the frame's
/// own origin: offsets the client has scrolled ahead of the cache cannot alias it.
fn projected_at(state: &ClientState, row: usize, col: usize) -> Option<&crate::CellDisplay> {
let r = row.checked_sub(state.grid.row_frame.first)?;
let c = col.checked_sub(state.grid.col_frame.first)?;
state.grid.cell(r, c)
}
/// What the host says of an aggregate, the fold of a collapsed group included.
const AGGREGATE_DECLINE: &str =
"Cannot write to an aggregate — it combines several records, so there is no one cell to write";