Skip to content

Test: Follow Cell Execution from Chat #250343

@Yoyokrazy

Description

@Yoyokrazy

Refs: #248546

Complexity: 4

author: @Yoyokrazy

Create Issue


Summary

This iteration we introduced a feature that would allow the user to "follow" currently executing cells as the agent does so, and moves them around the notebook as they are run. The setting that controls this is: github.copilot.chat.notebook.followCellExecution.enabled.

To support this, actions were contributed from the copilot repo to the notebook toolbar that give the user a quick way to be able to toggle on/off the follow behavior without having to change the setting itself. Finally, the "autoReveal" behavior has been tweaked to reduce any unnecessary scrolling, and provide the user with the best blend of cell content and potential outputs to ensure that they have proper context for what is being executed.  

To avoid showing new toolbar actions for those who never want them, we only expose the action when both:

  • A: the setting is enabled
  • B: after the run cell tool as either hit the prepare invocation (if user needs to confirm) or invocation (already allowed) stage.

At that point, the pin button will appear in your toolbar, allowing you to toggle on/off easily

Steps to Test

Run 1:

  • make sure you have Jupyter extension + python ( + optionally python envs, it's advised) installed
  • grab the GPX file from here: https://github.com/rebornix/cycling_agent
  • Attach that to chat and ask something resembling Extract the attached GPX data and plot my heart rate over time. Run cells as you create them
    • important to say run cells as you create, as that is what will trigger cell executions
  • While the notebook generates and cells are run, the notebook cells should either be completely or partially revealed into the viewport. An easy way to test this 1 by 1 (less natural, but good to get a feel for how things work) is to just use the command Reset Tool Confirmations and then manually click approve for each call

Run 2:

  • Ask it to make an additional edit to the notebook, and run cells as they are changed or again from the beginning.
    • When outputs are already present in the cell, our heuristics should be smart enough to scroll to a point where you get a split between cell output and cell editor.
  • Do this and set an always in workspace confirmation to get a feel for how this works in a more continuous state.

For those that are curious, autoreveal code is here:

const SMART_VIEWPORT_TOP_REVEAL_PADDING = 20; // enough to not cut off top of cell toolbar
const SMART_VIEWPORT_BOTTOM_REVEAL_PADDING = 60; // enough to show full bottom of output element + tiny buffer below that vertical bar
function handleAutoReveal(cell: ICellViewModel, notebookEditor: IActiveNotebookEditor): void {
// always focus the container, blue bar is a good visual aid in tracking what's happening
notebookEditor.focusNotebookCell(cell, 'container', { skipReveal: true });
// Handle markup cells with simple reveal
if (cell.cellKind === CellKind.Markup) {
const cellIndex = notebookEditor.getCellIndex(cell);
notebookEditor.revealCellRangeInView({ start: cellIndex, end: cellIndex + 1 });
return;
}
// Ensure we're working with a code cell - we need the CodeCellViewModel type for accessing layout properties like outputTotalHeight
if (!(cell instanceof CodeCellViewModel)) {
return;
}
// Get all dimensions
const cellEditorScrollTop = notebookEditor.getAbsoluteTopOfElement(cell);
const cellEditorScrollBottom = cellEditorScrollTop + cell.layoutInfo.outputContainerOffset;
const cellOutputHeight = cell.layoutInfo.outputTotalHeight;
const cellOutputScrollBottom = notebookEditor.getAbsoluteBottomOfElement(cell);
const viewportHeight = notebookEditor.getLayoutInfo().height;
const viewportHeight34 = viewportHeight * 0.34;
const viewportHeight66 = viewportHeight * 0.66;
const totalHeight = cell.layoutInfo.totalHeight;
const isFullyVisible = cellEditorScrollTop >= notebookEditor.scrollTop && cellOutputScrollBottom <= notebookEditor.scrollBottom;
const isEditorBottomVisible = ((cellEditorScrollBottom - 25 /* padding for the cell status bar */) >= notebookEditor.scrollTop) &&
((cellEditorScrollBottom + 25 /* padding to see a sliver of the beginning of outputs */) <= notebookEditor.scrollBottom);
// Common scrolling functions
const revealWithTopPadding = (position: number) => { notebookEditor.setScrollTop(position - SMART_VIEWPORT_TOP_REVEAL_PADDING); };
const revealWithNoPadding = (position: number) => { notebookEditor.setScrollTop(position); };
const revealWithBottomPadding = (position: number) => { notebookEditor.setScrollTop(position + SMART_VIEWPORT_BOTTOM_REVEAL_PADDING); };
// CASE 0: Total is already visible
if (isFullyVisible) {
return;
}
// CASE 1: Total fits within viewport
if (totalHeight <= viewportHeight && !isEditorBottomVisible) {
revealWithTopPadding(cellEditorScrollTop);
return;
}
// CASE 2: Total doesn't fit in the viewport
if (totalHeight > viewportHeight && !isEditorBottomVisible) {
if (cellOutputHeight > 0 && cellOutputHeight >= viewportHeight66) {
// has large outputs -- Show 34% editor, 66% output
revealWithNoPadding(cellEditorScrollBottom - viewportHeight34);
} else if (cellOutputHeight > 0) {
// has small outputs -- Show output at viewport bottom
revealWithBottomPadding(cellOutputScrollBottom - viewportHeight);
} else {
// No outputs, just big cell -- put editor bottom @ 2/3 of viewport height
revealWithNoPadding(cellEditorScrollBottom - viewportHeight66);
}
}
}


Thanks for testing!

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions