feat(query): markdown import, button persistence, OR filters#8
Conversation
Parse fenced query/button blocks during page markdown import so export round-trips restore databaseId, DSL, label, confirm, and script attrs. Co-authored-by: Cursor <cursoragent@cursor.com>
Store lastRun on button block attrs so query tables survive reload. Cap persisted query rows at 50 and clear cached results when the database or script changes. Co-authored-by: Cursor <cursoragent@cursor.com>
Introduce DatabaseFieldColumnsPicker for Query/Button blocks (up to 5 fields) and reuse ScriptResultView in Script Explorer for query output. Co-authored-by: Cursor <cursoragent@cursor.com>
Parse WHERE clauses with AND/OR precedence into nested group filters and execute OR/AND groups in buildFiltersQuery for database.query. Co-authored-by: Cursor <cursoragent@cursor.com>
Nested OR/AND groups must call matchFilter, not matchFieldFilter, so TypeScript and in-memory filtering stay aligned with SQL group filters. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
Code Review
This pull request introduces support for logical AND/OR operators with standard precedence in record queries, recursive group filters, configurable result columns for query and button blocks, and persistence of the last execution result (lastRun) for button blocks. It also adds markdown import/export support for these blocks. The review feedback highlights a critical issue in ButtonBlockPanel where deriving the last run state via useMemo prevents the UI from updating upon execution in read-only mode (when onChange is not provided). The reviewer suggests refactoring this to use a local state synchronized with the prop to ensure the execution results are immediately visible.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| const persistedLastRun = useMemo( | ||
| () => parseButtonBlockLastRun(lastRun), | ||
| [lastRun] | ||
| ); |
There was a problem hiding this comment.
Using useMemo to derive persistedLastRun directly from the lastRun prop means that when the button is run in read-only mode (where onChange is not provided), the UI will not update to show the run results. Replacing this with a local state that is synchronized with the prop and updated on successful runs ensures the UI always displays the latest execution feedback.
| const persistedLastRun = useMemo( | |
| () => parseButtonBlockLastRun(lastRun), | |
| [lastRun] | |
| ); | |
| const [localLastRun, setLocalLastRun] = useState<ButtonBlockLastRun | null>(null); | |
| useEffect(() => { | |
| setLocalLastRun(parseButtonBlockLastRun(lastRun)); | |
| }, [lastRun]); |
| onSuccess: (output) => { | ||
| setLastRun(output); | ||
| onChange?.({ lastRun: serializeButtonBlockLastRun(output) }); |
There was a problem hiding this comment.
Update the onSuccess handler to update the local localLastRun state so that the run results are immediately visible even if onChange is not provided (e.g., in read-only views).
onSuccess: (output) => {
const serialized = serializeButtonBlockLastRun(output);
setLocalLastRun(serialized);
onChange?.({ lastRun: serialized });
| {persistedLastRun ? ( | ||
| <div className="w-full space-y-2"> | ||
| <ScriptResultView run={lastRun} fields={fields} /> | ||
| <ScriptResultView | ||
| run={persistedLastRun} | ||
| fields={fields} | ||
| ranAt={persistedLastRun.ranAt} | ||
| selectedFieldIds={columns ?? undefined} | ||
| /> | ||
| </div> | ||
| ) : null} |
There was a problem hiding this comment.
Use the new localLastRun state instead of persistedLastRun to render the script result view.
| {persistedLastRun ? ( | |
| <div className="w-full space-y-2"> | |
| <ScriptResultView run={lastRun} fields={fields} /> | |
| <ScriptResultView | |
| run={persistedLastRun} | |
| fields={fields} | |
| ranAt={persistedLastRun.ranAt} | |
| selectedFieldIds={columns ?? undefined} | |
| /> | |
| </div> | |
| ) : null} | |
| {localLastRun ? ( | |
| <div className="w-full space-y-2"> | |
| <ScriptResultView | |
| run={localLastRun} | |
| fields={fields} | |
| ranAt={localLastRun.ranAt} | |
| selectedFieldIds={columns ?? undefined} | |
| /> | |
| </div> | |
| ) : null} |
Summary
colanode-queryandcolanode-buttonfenced blocks from page markdownlastRun) across reloads; cap stored query rowsTest plan
packages/clientandpackages/uitests pass@colanode/uibuild passes (group filter recursion fix)ORmatches database view filtersMade with Cursor