terminal
terminal copied to clipboard
Markdown Notebook Panes
Pre-requisite: #997
Spec in progress in https://github.com/microsoft/terminal/blob/dev/migrie/s/north-star/doc/specs/NorthStar/Markdown%20Notebooks.md
User Stories
- A: The user can perform some commandline action (like
wt open README.md), which opens a new pane in the Terminal, with the markdown file rendered into that pane.- B: Markdown panes have buttons next to code blocks that allow the text of that block to be sent directly to the adjacent terminal as input.
- C: The user can press different buttons to run some subset of all the commands in the file
- C.1: Run all the commands in this file
- C.2: Run all the commands from (the currently selected block) to the end of the file
- C.1: Run all the commands from (the currently selected block) to the next header. (e.g., run all the commands in this section of the doc.)
- D: The user can edit the contents of the markdown file directly in the Terminal.
- E: The Terminal could be configured to automatically open a markdown file when
cding to a directory- F: The command for opening a markdown file also supports opening files from the web (e.g. directly from GitHub)
- G: Code blocks in the markdown file are automatically assigned autoincrementing IDs. The user can perform an action (via keybinding, command palette, whatever) to execute a command block from the file, based on it's assigned ID.
- H: ...
maintainer notes: original OP below the break
Description of the new feature/enhancement
For many developers, especially embedded developers, they would pre-define many text commands and save them in a text file. If WT can split views and open and display text in one view, we can quickly send the text commands to the terminal. I can send lines, send selected blocks, or send the entire content of the file, and preset some shortcuts.
Proposed technical implementation details (optional)
Guys this is totally the notebooks thing I was talking about 😉
But also, there's probably some other useful things we're working on here for "Tasks" - basically, having a big long list of sendInput actions that you can quickly activate - either through the suggestions UI, or maybe even adding a "Tasks" pane.
(we'll probably have more to say after the holidays)
Okay so, you may be interested in the Suggestions UI and Tasks, which are new in 1.19: Using SendInput Actions in the Suggestions UI

I have Big Plans for these in upcoming releases - some of which is tracked in #13445 (though I think a better thread is floating around here somewhere)
see also:
- #1595
- #15664
no, I know this, and we DO NOT want to config the setting.json, we just want to code in some text file for TeamWorks
so, we want to open the text and the send those command easily, through terminal
No worries! I've got a lot of related ideas in this space, and untangling them all is sometimes a bit tricky.
You're looking for something more like this?

Yea? A file, with blocks of code that lets you run individual commands, or the whole block?
This is so innovative.
and a little simple txt file for me
We can run the line to terminal where curser focused (with shortcut )
or select some block to run
Congrats, this gets to be the thread then ☺️
I've been ideating in this space for a long time, just never actually filed a thread for it. I'm gonna co-opt the OP here to add some other notes. Thanks!
@zadjii-msft here is some demo using vscode.(yes, we can do this with vscode)
- text base panel is need indeed (good for teamwork and easy to use)
- notebook panel is welcome also (good for guiding)
reserving for notes for hackathon
9da196da7da7e342698a354a322cfc0265cc9d04: dev/migrie/fhl/notebook-proto-000 was the eldest attempt here, circa March 2023. Notably predates tab tear out.
Actual relevant diff was: https://github.com/microsoft/terminal/compare/a04a51bbe4270d65a5a58652772b00fb719261ac...dev/migrie/fhl/notebook-proto-000
2024 spring hack is branched off dev/migrie/fhl/2024-spring-merge-base, https://github.com/microsoft/terminal/compare/dev/migrie/fhl/2024-spring-merge-base...dev/migrie/fhl/2024-inline-notebook
dev/migrie/f/sui-panes-
- til events ==>
dev/migrie/fhl/2024-spring-merge-base
- Inline notebooks: https://github.com/microsoft/terminal/compare/dev/migrie/fhl/2024-spring-merge-base...dev/migrie/fhl/2024-inline-notebook
- til events ==>
- Tasks pane: https://github.com/microsoft/terminal/compare/dev/migrie/f/sui-panes...dev/migrie/fhl/tasks-pane
- Markdown pane: https://github.com/microsoft/terminal/compare/dev/migrie/f/tasks-pane...dev/migrie/fhl/md-pane
-
Notes that I have saved locally, but should be persisted elsewhere just in case
Notes as I try to make this work
There's a lot of in-flight changes to the Terminal, so I'm gonna start off of sui-panes + dev/migrie/til-events-for-all
Main workflow: User clicks "Run" on a textblock with a command.
We want to send that to the connection.
buttonPressedHandler(auto&&,auto&&)
{
Notebook.SendInput(block.Text());
}
notebook sends that to the ControlCore? Active TermControl? Let's start there, by sending it to the active TermControl
Control does it's thing. Eventually the command ends, and the control raises a NewPrompt
On the NewPrompt:
- Notebook needs to cork the connection
- Remove the connection from the
TermControl.- Including removing old event handlers
- ~~Tell the
TermControlto stop rendering there.~~- ~~
TermControldoesn't actually own the renderer, theControlCoredoes. So plumb it.~~ - ~~Tell the
Terminal~~ - The TermControl can totally just keep rendering. That's fine.
- ~~
- Create a new
TermControl,- Start by instantiating a new
ExistingControlData- Create a new
BlockRenderData. This renderdata starts at the current,new mark, and doesn't have a bottom set - the existing root
Terminal - the existing connection
- Create a new
- Pass that to a new ControlInteractivity/ControlCore
- Who grabs the
RenderDataout of that existing control data and uses it to create a newRenderer
- Who grabs the
- Add that content (ControlInteractivity) to a new TermControl
- attach the connection to it
- mark the new control as the active one.
- Start by instantiating a new
On a CommandStarted:
This is when the control actually needs to be rendered. Raise some event that notebook can bubble to mark it as Visible
Some interfaces
class Notebook {
Notebook(ITerminalConnection connection, Settings...);
}
class Notebook
{
TermControl[] controls;
TermControl active;
ITerminalConnection connection;
shared_ptr<Terminal> terminal;
Notebook(ITerminalConnection connection, Settings...); // Initialize the terminal, the NotebookRenderData, and the first TermControl
void NewPromptHandler()
}
class NotebookRenderData : IRenderData
{
}
interface IControlData
{
IRenderData* renderData;
Terminal* terminal;
ITerminalConnection connection;
}
struct Control.ExistingControlData : IControlData
{
IRenderData* renderData;
Terminal* terminal;
ITerminalConnection connection;
}
The IRenderData for a control is just the Terminal itself.
TextBuffer is created with a reference to a Renderer. Yike.
In the ControlCore ctor is where we construct the Renderer and pass in the Terminal as the IRenderData
~~Terminal already knows how to be a IRenderData so we can just modify it's implementation to also work in block mode. NO NO NO there's only one Terminal. It needs to implement a second IBlockRenderData interface, and have the control nah fuck~~
ControlCore::ControlCore(ExistingControlData dataFromNotebook, Settings...)
{
_terminal = dataFromNotebook.terminal;
_renderer = make_unique<Renderer>(renderSettings,
dataFromNotebook.renderData, // IRenderData
nullptr, // pEngine
0, // cEngines
std::move(renderThread));
}
The notebook holds the Terminal. It holds a bunch of controls&cores. So there's one Renderer for each control. Each renderer is given a NotebookRenderData that the Notebook owns and instantiates for each control.
At creation
We instantiate a new Notebook, with the given connection and settings.
- It's going to create a
Terminal,- Passing in to that Terminal, it'll pass it's own renderdata?
Terminal already implements IRenderData. And on Terminal::Create(), Terminal recieves a Renderer&. That's annoying. We
- And eventually, a first
TermControl.
Todo list
- [x] Convert the
Renderer&inTextBufferandAdaptDispatch(et. al) to be aRenderer*, since references can't be reassigned. - [x]
Terminalneeds a ChangeRenderer method that takes aRenderer*and updates the buffer et al. - [x] Create a
BlockRenderDataclass, (InTerminalCoreproject, why not)- [x] implements
IRenderData, - [x] Takes a
Terminal*as a param - [x] Originally, just implement as a passthrough on all the TerminalRenderData methods
- [x] implements
- [x] Change ControlCore to have a public non-projected ctor which takes a
IControlDataand settings, and initialize the_terminalfrom that if given. - [x] Create a
Notebookclass inTerminal.Control.- [x] Just stick a pair of TermControls into the notebook.
At this point, I was able to stick the pair of TermControls from the notebook into the MyPage of the scratch project.
- [x] De-dupe the input.
- I think there's two OUTPUT handlers registered somewhere.
- [ ] Update all bocks when relevant
- [ ] Start making more blocks for each command
2024-march-14 lunch screenshot:
- All blocks share one Terminal, but have different RenderData.
- Every time we get a
FTCS A, we make a new block.- We do this behind the scenes
- Each block's renderdata only renders that specific mark.
- Each block's renderdata independently tracks the viewport, so that they can be scrolled independently
- Each block has a play button
- Blocks are generated from markdown.
- Markdown files can be loaded from a given file path.
Updates archive
2024-march-13 lunch screenshot:
2024-march-12 EoD screenshot:
2024-march-11 EoD screenshot:
2024-march-11 lunch screenshot:
2024-march-08 screenshot:
These two blocks both share a single Connection and Terminal. They have separate ControlCore & ControlInteractivity & TermControl's. Only the last one actually gets updates to its viewport, we should fix that.
Conclusions
Overall, project was a huge success. Clearly, it's possible to have Terminal notebooks that have a single hidden connection & terminal core, and then have a markdown frontend for that notebook, with each block of output rendered separately.
However, at this point, one should probably ask: why the heck isn't this just .ipynb's? Especially with the existence of:
- https://github.com/dotnet/interactive and
- Polyglot Notebooks
Couldn't the Terminal just be a kernel for that? There's not really a reason why not, right? And if we want to do that, then we really don't need... any... of what was written here. I should have probably done more research first.
There's probably also still value in the side-by-side UX. Probably.
I'm gonna leave the above comment for the "inline notebooks" investigation that eventually petered out, and use this one for side-by-side markdown in the Terminal
Branch map:
dev/migrie/f/sui-panes-
- til events ==>
dev/migrie/fhl/2024-spring-merge-base
- Inline notebooks: https://github.com/microsoft/terminal/compare/dev/migrie/fhl/2024-spring-merge-base...dev/migrie/fhl/2024-inline-notebook
- til events ==>
- Tasks pane: https://github.com/microsoft/terminal/compare/dev/migrie/f/sui-panes...dev/migrie/fhl/tasks-pane
- Markdown pane: https://github.com/microsoft/terminal/compare/dev/migrie/f/tasks-pane...dev/migrie/fhl/md-pane
-
2024-march-15 end of hackathon:
Updates archive
2024-march-14 EoD screenshot:
Could you please provide a another text base panel, not only markdown panel?
Sure, why not. Rendering plaintext is easy comparatively to rendering markdown in WinUI 😜
Sure, why not. Rendering plaintext is easy comparatively to rendering markdown in WinUI 😜
thank ,now i just need a short cut to send the line command without text_selected ,or selected_text command ,
after that ,we don't need copy->paste->commit line