terminal icon indicating copy to clipboard operation
terminal copied to clipboard

Markdown Notebook Panes

Open heartacker opened this issue 1 year ago • 12 comments
trafficstars

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)

image

heartacker avatar Dec 23 '23 03:12 heartacker

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)

zadjii avatar Dec 23 '23 12:12 zadjii

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

zadjii-msft avatar Jan 04 '24 12:01 zadjii-msft

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

heartacker avatar Jan 04 '24 13:01 heartacker

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?

zadjii-msft avatar Jan 04 '24 15:01 zadjii-msft

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

heartacker avatar Jan 04 '24 15:01 heartacker

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 avatar Jan 04 '24 15:01 zadjii-msft

@zadjii-msft here is some demo using vscode.(yes, we can do this with vscode)

  1. text base panel is need indeed (good for teamwork and easy to use)
  2. notebook panel is welcome also (good for guiding)

txt notebook_panel

heartacker avatar Jan 05 '24 03:01 heartacker

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
    • 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 TermControl to stop rendering there.~~
    • ~~TermControl doesn't actually own the renderer, the ControlCore does. 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
    • Pass that to a new ControlInteractivity/ControlCore
      • Who grabs the RenderData out of that existing control data and uses it to create a new Renderer
    • Add that content (ControlInteractivity) to a new TermControl
    • attach the connection to it
    • mark the new control as the active one.

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& in TextBuffer and AdaptDispatch (et. al) to be a Renderer*, since references can't be reassigned.
  • [x] Terminal needs a ChangeRenderer method that takes a Renderer* and updates the buffer et al.
  • [x] Create a BlockRenderData class, (In TerminalCore project, 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] Change ControlCore to have a public non-projected ctor which takes a IControlData and settings, and initialize the _terminal from that if given.
  • [x] Create a Notebook class in Terminal.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: image

  • 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: fhl-notebooks-with-progress

2024-march-12 EoD screenshot: image

2024-march-11 EoD screenshot: image

2024-march-11 lunch screenshot: image 2024-march-08 screenshot: image

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:

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.

zadjii-msft avatar Mar 07 '24 23:03 zadjii-msft

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
    • 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: fhl-markdown-open-demo

Updates archive

2024-march-14 EoD screenshot: image

zadjii-msft avatar Mar 14 '24 22:03 zadjii-msft

Could you please provide a another text base panel, not only markdown panel?

heartacker avatar Mar 15 '24 00:03 heartacker

Sure, why not. Rendering plaintext is easy comparatively to rendering markdown in WinUI 😜

fhl-markdown-open-text-file

zadjii-msft avatar Mar 15 '24 15:03 zadjii-msft

Sure, why not. Rendering plaintext is easy comparatively to rendering markdown in WinUI 😜

fhl-markdown-open-text-file

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

heartacker avatar Mar 15 '24 23:03 heartacker