Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Markdown Notebook Panes #16495

Open
heartacker opened this issue Dec 23, 2023 · 12 comments
Open

Markdown Notebook Panes #16495

heartacker opened this issue Dec 23, 2023 · 12 comments
Labels
Issue-Scenario Needs-Tag-Fix Doesn't match tag requirements Product-Terminal The new Windows Terminal.

Comments

@heartacker
Copy link

heartacker commented Dec 23, 2023

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 heartacker added the Issue-Feature Complex enough to require an in depth planning process and actual budgeted, scheduled work. label Dec 23, 2023
@microsoft-github-policy-service microsoft-github-policy-service bot added Needs-Tag-Fix Doesn't match tag requirements Needs-Triage It's a new issue that the core contributor team needs to triage at the next triage meeting labels Dec 23, 2023
@zadjii
Copy link

zadjii commented Dec 23, 2023

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-msft
Copy link
Member

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:

@heartacker
Copy link
Author

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

@zadjii-msft
Copy link
Member

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?

@heartacker
Copy link
Author

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

@zadjii-msft
Copy link
Member

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 zadjii-msft changed the title support opening text file for precommand Markdown Notebook Panes Jan 4, 2024
@zadjii-msft zadjii-msft added Issue-Scenario Product-Terminal The new Windows Terminal. and removed Issue-Feature Complex enough to require an in depth planning process and actual budgeted, scheduled work. Needs-Triage It's a new issue that the core contributor team needs to triage at the next triage meeting labels Jan 4, 2024
@heartacker
Copy link
Author

@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

@zadjii-msft
Copy link
Member

zadjii-msft commented Mar 7, 2024

reserving for notes for hackathon

9da196d: dev/migrie/fhl/notebook-proto-000 was the eldest attempt here, circa March 2023. Notably predates tab tear out.
Actual relevant diff was: a04a51b...dev/migrie/fhl/notebook-proto-000

2024 spring hack is branched off dev/migrie/fhl/2024-spring-merge-base, dev/migrie/fhl/2024-spring-merge-base...dev/migrie/fhl/2024-inline-notebook

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

  • Convert the Renderer& in TextBuffer and AdaptDispatch (et. al) to be a Renderer*, since references can't be reassigned.
  • Terminal needs a ChangeRenderer method that takes a Renderer* and updates the buffer et al.
  • Create a BlockRenderData class, (In TerminalCore project, why not)
    • implements IRenderData,
    • Takes a Terminal* as a param
    • Originally, just implement as a passthrough on all the TerminalRenderData methods
  • Change ControlCore to have a public non-projected ctor which takes a IControlData and settings, and initialize the _terminal from that if given.
  • Create a Notebook class in Terminal.Control.
    • 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.

  • 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
Copy link
Member

zadjii-msft commented Mar 14, 2024

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:

2024-march-15 end of hackathon:
fhl-markdown-open-demo

Updates archive

2024-march-14 EoD screenshot:
image

@heartacker
Copy link
Author

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

@zadjii-msft
Copy link
Member

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

fhl-markdown-open-text-file

@heartacker
Copy link
Author

heartacker commented Mar 15, 2024

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

@zadjii-msft zadjii-msft mentioned this issue May 14, 2024
4 tasks
zadjii-msft added a commit that referenced this issue Nov 12, 2024
This adds support to the Terminal for parsing Markdown to XAML. We're
using https://github.com/github/cmark-gfm as our parser, so that we can
support the fullness of github-flavored markdown.

The parser parses the markdown to produce a `RichTextBlock`, which
covers just about all the scenarios we need. Since we're initially just
targeting using this for "Release notes", I didn't implement
_everything_ in markdown[^1]. But headers, bold & italic, unordered
lists, images, links, code spans & blocks - all that works. We can work
on additional elements as we need them. The parser is encapsulated into
`Microsoft.Terminal.UI.Markdown.dll`, so that we won't load it on
startup, only when the pane is actually made the first time.

To test this out, I've added a `MarkdownPaneContent` pane type on
`x-markdown` (the `x-` is "experimental"). Go ahead and add that with:

```json
{ "command": { "action": "splitPane", "type": "x-markdown" } }
```

That's got the ability to load arbitrary MD files and render them. I
wouldn't call that experience finished though[^2][^3](and it probably
won't be in 1.22 timeframe). However, it is an excellent testbed for
validating what we do and do not support.

We'll use the markdown parser Soon<sup>TM</sup> for the What's New
panes.

* Done in pursuit of displaying release notes in the Terminal.
* Doesn't quite close out #16495 
* Should make #8647 possible
* may help with #16484

[^1]: the most notable gap being "block quotes" with `>`. I don't think
I can draw a vertical line in a rich text block easily. Footnotes are
also missing, as well as tables.
[^2]: I say it's not finished because the aforementioned MD gaps. Also
the UX there is not polished at all.
[^3]: I don't believe we'll have time to polish out the pure markdown
pane for 1.22, but what the parser covers now is more than enough for
the release notes pane in time for 1.22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Issue-Scenario Needs-Tag-Fix Doesn't match tag requirements Product-Terminal The new Windows Terminal.
Projects
None yet
Development

No branches or pull requests

3 participants