cat / 012
clone

a spreadsheet for the terminal: lotus 1-2-3 looks, google sheets keys and formulas, one go binary

Merge branch 'main': notebook tabs replace command regions; random edits send outputs' rows as the UI does

Dami committed 7f2c1d2
commit
7f2c1d208a5509af0f69029cd6a40e5c98854f08
parent
0bba14514141b8eb84af22023a045deb47ffa21d
parent
89ca25dd4e0ee089e709d2b9a2d7e31f9a87bd80
author
Dami <dami.etoile@gmail.com>
220 files changed, +15907 -3613
MREADME.md11+-
MROADMAP.md22+-
Acmd/012/args.go94++++
Acmd/012/diffcmd.go209++++++++++
Acmd/012/evaluate.go102+++++
Acmd/012/export.go117+++++
Acmd/012/git_test.go204+++++++++
Mcmd/012/main.go38+-
Mcmd/012/nu_test.go10+-
Acmd/012/scripts.go181++++++++
Acmd/012/scripts_test.go212++++++++++
Mdemos/follow-log.tape15+-
Mdemos/notebook.tape62++-
Mdemos/nu-recipe.tape29+-
Mdocs/README.md4+-
Mdocs/contributing/architecture.md167+++++---
Mdocs/contributing/testing.md39++
Mdocs/contributing/ux.md4+-
Mdocs/files/README.md2+
Mdocs/files/following.md11+-
Mdocs/files/format.md80++-
Adocs/files/git.md152+++++++
Adocs/files/scripts.md153+++++++
Mdocs/getting-started/install.md9+-
Mdocs/media/follow-log.gifbin
Mdocs/media/notebook.gifbin
Mdocs/media/nu-recipe.gifbin
Mdocs/nushell/README.md32+-
Mdocs/nushell/cookbook.md79++-
Mdocs/nushell/notebooks.md430++++++++++++++------
Mdocs/nushell/pipelines.md17+
Mdocs/nushell/types.md2+-
Mdocs/reference/README.md1+
Adocs/reference/command-line.md63+++
Mdocs/reference/config.md32+-
Mdocs/reference/keys.md41++-
Mdocs/terminal/ssh.md18+-
Me2e/notebook_test.go109++++-
Me2e/screennotebook_test.go122++++-
Ae2e/testdata/nu/ide/error.ast.json1+
Ae2e/testdata/nu/ide/error.check.jsonl4+
Ae2e/testdata/nu/ide/error.nu4+
Ae2e/testdata/nu/ide/highlight.ast.json1+
Ae2e/testdata/nu/ide/highlight.nu4+
Me2e/testdata/nu/nu51++-
Me2e/testdata/screens/help-light.html2+-
Me2e/testdata/screens/help-narrow.html2+-
Me2e/testdata/screens/help-wide.html80++--
Me2e/testdata/screens/help.html2+-
Me2e/testdata/screens/macro-menu-light.html10+-
Me2e/testdata/screens/macro-menu.html10+-
Ae2e/testdata/screens/notebook-cells-high-contrast.html30+
Ae2e/testdata/screens/notebook-cells-light.html30+
Ae2e/testdata/screens/notebook-cells.html30+
De2e/testdata/screens/notebook-completion-light.html30-
De2e/testdata/screens/notebook-completion.html30-
Ae2e/testdata/screens/notebook-error-high-contrast.html30+
Ae2e/testdata/screens/notebook-error-light.html30+
Ae2e/testdata/screens/notebook-error.html30+
De2e/testdata/screens/notebook-failed-high-contrast.html30-
De2e/testdata/screens/notebook-failed-light.html30-
De2e/testdata/screens/notebook-failed.html30-
Ae2e/testdata/screens/notebook-nu-error-high-contrast.html30+
Ae2e/testdata/screens/notebook-nu-error-light.html30+
Ae2e/testdata/screens/notebook-nu-error.html30+
Ae2e/testdata/screens/notebook-nu-highlight-high-contrast.html30+
Ae2e/testdata/screens/notebook-nu-highlight-light.html30+
Ae2e/testdata/screens/notebook-nu-highlight.html30+
Ae2e/testdata/screens/notebook-output-high-contrast.html30+
Ae2e/testdata/screens/notebook-output-light.html30+
Ae2e/testdata/screens/notebook-output.html30+
De2e/testdata/screens/notebook-regions-high-contrast.html30-
De2e/testdata/screens/notebook-regions-light.html30-
De2e/testdata/screens/notebook-regions.html30-
Ae2e/testdata/screens/notebook-running-high-contrast.html30+
Ae2e/testdata/screens/notebook-running-light.html30+
Ae2e/testdata/screens/notebook-running.html30+
Re2e/testdata/screens/notebook-completion-high-contrast.html → e2e/testdata/screens/notebook-sent-high-contrast.html24+-
Ae2e/testdata/screens/notebook-sent-light.html30+
Ae2e/testdata/screens/notebook-sent.html30+
Ae2e/testdata/screens/notebook-stale-high-contrast.html30+
Ae2e/testdata/screens/notebook-stale-light.html30+
Ae2e/testdata/screens/notebook-stale.html30+
Ae2e/testdata/screens/notebook-wrap-high-contrast.html24+
Ae2e/testdata/screens/notebook-wrap-light.html24+
Ae2e/testdata/screens/notebook-wrap-narrow-high-contrast.html24+
Ae2e/testdata/screens/notebook-wrap-narrow-light.html24+
Ae2e/testdata/screens/notebook-wrap-narrow.html24+
Ae2e/testdata/screens/notebook-wrap.html24+
Me2e/testdata/screens/palette-narrow.html4+-
Me2e/testdata/screens/palette-search-light.html2+-
Me2e/testdata/screens/palette-search.html2+-
Me2e/testdata/screens/palette-wide.html12+-
Me2e/testdata/screens/palette.html2+-
Minternal/config/registry.go20+-
Ainternal/diff/cells.go285+++++++++++++
Ainternal/diff/diff.go203+++++++++
Ainternal/diff/diff_test.go216++++++++++
Ainternal/diff/fields.go187+++++++++
Ainternal/diff/match.go103+++++
Ainternal/diff/merge.go287+++++++++++++
Ainternal/diff/merge_test.go180++++++++
Ainternal/diff/mergesheets.go165++++++++
Ainternal/diff/notebook.go160+++++++
Ainternal/diff/raw.go211++++++++++
Ainternal/diff/text.go199+++++++++
Minternal/fileio/nuon_test.go58++-
Ainternal/fileio/nuonrows.go35++
Minternal/fileio/nuonwrite.go4+
Ainternal/fileio/sheetscorpus_test.go241+++++++++++
Ainternal/fileio/sheetscorpus_xlsx_test.go96++++
Ainternal/fileio/testdata/sheets_corpus.tsv320+++++++++++++++
Ainternal/headless/get.go179++++++++
Ainternal/headless/headless.go125++++++
Ainternal/headless/headless_test.go316+++++++++++++++
Ainternal/headless/jev.go54++
Ainternal/headless/notebook.go237+++++++++++
Ainternal/headless/recalc.go51++
Ainternal/headless/ref.go95++++
Ainternal/headless/set.go71+++
Ainternal/notebook/graph.go224++++++++++
Ainternal/notebook/notebook.go158+++++++
Ainternal/notebook/notebook_test.go135++++++
Ainternal/notebook/refs.go148+++++++
Ainternal/nushell/ide.go219++++++++++
Ainternal/nushell/ide_test.go127++++++
Minternal/nushell/nushell.go167++++----
Minternal/nushell/nushell_test.go85+---
Ainternal/nushell/testdata/ide/ast.json1+
Ainternal/nushell/testdata/ide/ast.nu1+
Ainternal/nushell/testdata/ide/check.jsonl6+
Ainternal/nushell/testdata/ide/check.nu1+
Ainternal/nushell/testdata/ide/complete.json1+
Ainternal/nushell/testdata/ide/complete.nu1+
Ainternal/nushell/testdata/ide/version.txt1+
Minternal/sheet/file.go44+-
Minternal/sheet/fileread.go6+-
Minternal/sheet/filestream_test.go14+-
Minternal/sheet/linked.go38+-
Minternal/sheet/linkedregions_test.go55+--
Minternal/sheet/live.go10+-
Ainternal/sheet/notebook.go229+++++++++++
Ainternal/sheet/notebookfile.go201+++++++++
Minternal/sheet/randedit_ops_test.go48++-
Minternal/sheet/randedit_test.go130+++++-
Minternal/sheet/region.go343++++------------
Minternal/sheet/region_test.go370+++++++++--------
Dinternal/sheet/regiondata.go66---
Minternal/sheet/regionfile.go125++---
Dinternal/sheet/regiongraph.go161-------
Minternal/sheet/regionshape.go155++------
Dinternal/sheet/regionsort.go68---
Minternal/sheet/regionwrite.go179+-------
Minternal/sheet/sheet.go11+-
Dinternal/sheet/shellhistory.go35--
Minternal/sheet/workbook.go4+-
Minternal/ui/commands.go11+-
Minternal/ui/docs_test.go19+
Minternal/ui/file.go14+-
Minternal/ui/filter.go9+-
Minternal/ui/goto_test.go37+-
Minternal/ui/gridlines.go19-
Minternal/ui/help.go1+
Minternal/ui/hosts.go16-
Ainternal/ui/lineedit/area.go246+++++++++++
Ainternal/ui/lineedit/area_test.go93++++
Minternal/ui/lineedit/lineedit.go3+
Ainternal/ui/lineedit/word.go98+++++
Ainternal/ui/lineedit/word_test.go75+++
Minternal/ui/linked.go2+-
Minternal/ui/menubar.go4+-
Minternal/ui/model.go42+-
Minternal/ui/monochrome_test.go83+++-
Ainternal/ui/nbjob.go166++++++++
Ainternal/ui/nblang.go83++++
Ainternal/ui/nblang_test.go94++++
Ainternal/ui/nbrun.go417+++++++++++++++++++
Ainternal/ui/nbscreen.go292+++++++++++++
Ainternal/ui/nbsend.go201+++++++++
Ainternal/ui/nbview/bench_test.go68+++
Ainternal/ui/nbview/edit.go320+++++++++++++++
Ainternal/ui/nbview/full.go298++++++++++++++
Ainternal/ui/nbview/highlight.go173++++++++
Ainternal/ui/nbview/keys.go375+++++++++++++++++
Ainternal/ui/nbview/markdown.go167++++++++
Ainternal/ui/nbview/nu.go219++++++++++
Ainternal/ui/nbview/nu_test.go367+++++++++++++++++
Ainternal/ui/nbview/nuprelude.go146+++++++
Ainternal/ui/nbview/output.go409+++++++++++++++++++
Ainternal/ui/nbview/panel.go113+++++
Ainternal/ui/nbview/syntax.go241+++++++++++
Ainternal/ui/nbview/testdata/nu/check.jsonl5+
Ainternal/ui/nbview/testdata/nu/check.nu4+
Ainternal/ui/nbview/testdata/nu/complete.json1+
Ainternal/ui/nbview/testdata/nu/complete.nu4+
Ainternal/ui/nbview/testdata/nu/highlight.json1+
Ainternal/ui/nbview/testdata/nu/highlight.nu4+
Ainternal/ui/nbview/testdata/nu/sheet.json1+
Ainternal/ui/nbview/testdata/nu/sheet.nu5+
Ainternal/ui/nbview/view.go405+++++++++++++++++++
Ainternal/ui/nbview/view_test.go238+++++++++++
Minternal/ui/notebook.go651+++++++++++++++---------------
Minternal/ui/notebook_test.go479++++++++++++++--------
Dinternal/ui/nukey_test.go26-
Dinternal/ui/nuprompt/nuprompt.go331---------------
Dinternal/ui/nuprompt/nuprompt_test.go100-----
Dinternal/ui/nurun.go325---------------
Minternal/ui/overlay.go5+-
Minternal/ui/panel.go67++-
Minternal/ui/pivot.go2+-
Minternal/ui/selection.go2+-
Minternal/ui/sort.go6+-
Minternal/ui/tabs.go4+
Minternal/ui/tabstrip/tabstrip.go11+-
Ainternal/ui/theme/code.go42++
Minternal/ui/theme/scheme.go2+-
Minternal/ui/theme/theme.go22+-
Minternal/ui/view.go5+-
Ainternal/ui/wordkeys_test.go20+
Mwebsite/src/pages/index.tsx2+-
M README.md +6 -5
@@ -48,10 +48,11 @@ rest: building from a clone, importing files, `012 serve` and settings.
4848 nushell module `012 nu --install-module` installs, or as4949 `ls | to nuon | ^012 --pipe | from nuon` without it5050 ([pipelines](docs/nushell/pipelines.md)).51-- **Nushell notebooks**: `012 nu` (or Data > Shell in any workbook) runs nushell52- pipelines whose tables become live, named regions of the sheet;53- `$r1` reads one in the next command, and refreshing it runs what reads54- it ([notebooks](docs/nushell/notebooks.md)).51+- **Nushell notebooks**: `012 nu` (or Data > Shell in any workbook)52+ opens a notebook tab of code and note cells, run with Jupyter's keys,53+ each output under its cell; later cells read it as `$name`, and sent to54+ a sheet it's a live table formulas read as `nu.name`55+ ([notebooks](docs/nushell/notebooks.md)).5556 - **Macros**, recorded or written as Starlark scripts saved with the sheet5657 ([macros](docs/sheets/macros.md)).5758 - **Made for terminals**: the mouse, hyperlinks, the system clipboard over
@@ -67,7 +68,7 @@ rest: building from a clone, importing files, `012 serve` and settings.
6768 | Menus and the command palette (Ctrl+K) | Charts that float over the grid and follow their data |6869 | ![A task list with a color scale, a dropdown and checkboxes](docs/media/rules.gif) | ![A pivot table of sales by region and quarter](docs/media/pivot.gif) |6970 | Conditional formatting and data validation | Pivot tables, live |70-| ![UNIQUE, SORT and FILTER spilling their results](docs/media/arrays.gif) | ![Two nushell pipelines as live regions, the second reading the first](docs/media/notebook.gif) |71+| ![UNIQUE, SORT and FILTER spilling their results](docs/media/arrays.gif) | ![A notebook: a cell reads a CSV, a second reads its output, which is sent to a sheet and summed there](docs/media/notebook.gif) |7172 | Arrays that spill | Nushell notebooks |7273 | ![JEV functions classifying reviews](docs/media/jev.gif) | ![Recording a macro and replaying it](docs/media/macros.gif) |7374 | JEV functions in formulas | Macros |
M ROADMAP.md +10 -12
@@ -27,17 +27,14 @@ rises in measured steps; see [Bounds of support](docs/contributing/limits.md#wha
2727 2828 ### 2. A TUI Jupyter for nushell2929 30-A notebook is its own kind of tab in a workbook: cells, not a grid. Code31-cells hold a nushell pipeline (several lines if needed) with its output32-under it (a scrollable table view, text, a record or an error); note cells33-hold Markdown. You run cells in the order you choose, with the usual34-controls, and send any output to a sheet, where it is a live region that35-formulas, charts and pivots use. It replaces the grid-based notebook sheet.30+Notebook tabs are a TUI Jupyter for nushell31+([Notebooks](docs/nushell/notebooks.md)): code and note cells, outputs in32+place, sent to sheets as live regions, and cells highlighted, checked and33+completed by nu as they're written.3634 3735 | Item | Result | Size |3836 |---|---|---|39-| Notebook tabs: code and note cells, outputs rendered in place (tables that scroll and open full-screen as a grid, text, records, errors), Jupyter keys inside the notebook (Shift+Enter run and next, Ctrl+Enter run, a/b add above/below, dd delete, m/y note/code, Esc/Enter command/edit mode) plus menus and palette; run cell, run all, run above, run below, stop, clear outputs; run counts and timings; cells named for their output (`$files`), stale marks when a cell they read has changed, an opt-in reactive mode that re-runs dependents; send an output to a sheet as a live region (`nu.files`); outputs saved in the workbook up to a size cap; existing notebook sheets converted, one code cell per region; `012 nu` opens a notebook | A TUI Jupyter with nushell and 012 | L |40-| Code cells highlighted, completed and checked as you type by nushell itself: `nu --ide-ast` token shapes mapped to theme roles, `nu --ide-complete` plus 012's cell, region and sheet names on Tab, `nu --ide-check` errors underlined; debounced in the background, plain text when nu is missing or slow; hover docs through `nu --lsp` later | Writing pipelines in 012 feels like nushell's own prompt | S to M |37+| A command's signature and description for the word at the caret, on the context line, through `nu --ide-hover` or `nu --lsp` | Writing a cell without leaving for `help` | S |4138 4239 ### 3. Toward multiplayer4340
@@ -48,7 +45,7 @@ log macros record. Each step is useful on its own.
4845 4946 | Step | Result | Size |5047 |---|---|---|51-| A nushell region follows a streaming pipeline through the linked regions' live sources (`live.Source`), rows arriving as the pipeline writes them | Pipelines as live sheets | S |48+| A notebook cell's output follows a streaming pipeline through the linked regions' live sources (`live.Source`), rows arriving on its sheet as the pipeline writes them | Pipelines as live sheets | S |5249 | Shared viewing over SSH: several `012 serve` sessions open the same workbook; one edits, the others follow live with their own cursor, scroll and theme; presence shows who is where | Watch-along and review, one writer | M |5350 | Shared editing over SSH: every session edits, the server orders operations (no CRDT needed while one server holds the workbook), per-user undo, presence and edit ownership shown in the grid, saves by the server | Multiplayer 012 over SSH | L |5451
@@ -68,8 +65,6 @@ shared editing in section 3.
6865 6966 | Item | Result | Size |7067 |---|---|---|71-| Commands for scripts without the screen: `012 get`, `012 set`, `012 recalc`, `012 export` on a workbook file | 012 in scripts, cron and nushell pipelines | S to M |72-| `012 diff` cell by cell (values, formulas, formats, regions) and a git diff and merge driver for `.012` | Sheets kept in git review like code | M |7368 | Formula tracing: precedents and dependents shown in the grid, a formula evaluated step by step | Finding why a number is wrong | M |7469 | Named tables with structured references (`Sales[Amount]`); notebook regions are tables | Formulas that read by column name | M |7570 | A sheet or chart exported as a static HTML page in 012's look | Sharing a sheet with someone without 012 | S |
@@ -139,12 +134,15 @@ stream above rather than a design of their own.
139134 140135 - 012 as a stage in a pipeline: `012 -` reads a table from standard input, `012 --pipe` sends the sheet or selection on, with nushell's types kept through NUON: [Pipelines](docs/nushell/pipelines.md)141136 - The `sheet` command: a nushell module shipped in the binary (`012 nu --install-module`), so nu calls 012 without `^012` or NUON on either side: [Pipelines](docs/nushell/pipelines.md#the-sheet-command)142-- `012 nu` and notebook sheets: nushell pipelines at a prompt on the formula bar become live, named regions that read each other and refresh in dependency order, saved as commands and never run on open: [Notebooks](docs/nushell/notebooks.md)137+- Notebook tabs, a TUI Jupyter for nushell: code and note cells with Jupyter's keys, outputs drawn in place and opened full-screen, `$name` between cells, stale marks and a reactive mode, outputs sent to sheets as live regions (`nu.name`), saved up to a cap, and earlier notebook sheets converted on open: [Notebooks](docs/nushell/notebooks.md)138+- Code cells highlighted, checked and completed as they're written by nu itself (`--ide-ast`, `--ide-check`, `--ide-complete`), in the background, falling back to 012's own when nu is missing, old, slow or not trusted: [Notebooks](docs/nushell/notebooks.md#writing-a-cell)143139 144140 **Files**145141 146142 - Import CSV, TSV, JSON, NUON, XLSX, SQLite, Parquet and Lotus `.wk1`; export CSV, TSV, JSON, NUON, XLSX and SQLite; import locations; save-as and overwrite checks: [Files](docs/files/README.md)147143 - `.012` files read and written as a stream, cells straight into and out of the store: [The .012 format](docs/files/format.md#reading-and-writing)144+- Commands for scripts without the screen: `012 get` (text, CSV, TSV, JSON or NUON), `012 set`, `012 recalc` and `012 export`, running notebooks and JEV only behind flags: [Scripts](docs/files/scripts.md)145+- `012 diff` cell by cell, as git's diff command or textconv, and `012 merge-driver` merging cell by cell with conflicts noted on the cells: [Diff and merge in git](docs/files/git.md)148146 - Following files: a linked region follows a CSV, TSV, JSON lines or NUON file as it grows and any importable file as it's rewritten, keeping every row or the last ones, its rows arriving as the change stream: [Following files](docs/files/following.md)149147 - The `.012` format written down with a version, and each release's workbooks kept as fixtures that every later build opens and saves unchanged: [The .012 format](docs/files/format.md#versions), [Releasing](docs/contributing/releasing.md#file-fixtures)150148 - A crash keeps unsaved work for recovery, restores the terminal and writes a report, locally and in `012 serve`: [Saving](docs/files/saving.md#if-012-crashes)
A cmd/012/args.go +94 -0
@@ -0,0 +1,94 @@
1+package main2+3+import (4+ "errors"5+ "fmt"6+ "slices"7+ "strings"8+)9+10+// exitError is an error with the exit status it calls for. A nil err11+// exits with code saying nothing, as diff(1) does for "files differ".12+type exitError struct {13+ code int14+ err error15+}16+17+func (e *exitError) Error() string {18+ if e.err == nil {19+ return fmt.Sprintf("exit status %d", e.code)20+ }21+ return e.err.Error()22+}23+24+func (e *exitError) Unwrap() error { return e.err }25+26+// exitCode is the status err asks for, 1 unless it says otherwise, and27+// whether there is anything to print.28+func exitCode(err error) (code int, show bool) {29+ var ee *exitError30+ if errors.As(err, &ee) {31+ return ee.code, ee.err != nil32+ }33+ return 1, true34+}35+36+// usageError is a command used wrongly: status 2, as most tools use for37+// usage, with the command's usage line.38+func usageError(why, usage string) error {39+ switch {40+ case usage == "":41+ usage = why42+ case why != "":43+ usage = why + "\n" + usage44+ }45+ return &exitError{code: 2, err: errors.New(usage)}46+}47+48+// cliArgs are a subcommand's arguments: its flags, by name without the49+// dashes, and the rest in order.50+type cliArgs struct {51+ flags map[string]string52+ pos []string53+}54+55+func (a cliArgs) has(name string) bool { _, ok := a.flags[name]; return ok }56+57+// parseArgs splits args into flags and the rest. Flags start with --58+// and may come anywhere; valued ones take the next argument or =value.59+// Only flags the command knows are flags, so an argument such as -5 or60+// -A1 is a value; after --, everything is.61+func parseArgs(args []string, valued, bools []string) (cliArgs, error) {62+ out := cliArgs{flags: map[string]string{}}63+ for i := 0; i < len(args); i++ {64+ arg := args[i]65+ if arg == "--" {66+ out.pos = append(out.pos, args[i+1:]...)67+ break68+ }69+ if !strings.HasPrefix(arg, "--") || arg == "--" {70+ out.pos = append(out.pos, arg)71+ continue72+ }73+ name, value, hasValue := strings.Cut(arg[2:], "=")74+ switch {75+ case slices.Contains(bools, name):76+ if hasValue {77+ return out, fmt.Errorf("--%s takes no value", name)78+ }79+ out.flags[name] = ""80+ case slices.Contains(valued, name):81+ if !hasValue {82+ if i+1 == len(args) {83+ return out, fmt.Errorf("--%s needs a value", name)84+ }85+ i++86+ value = args[i]87+ }88+ out.flags[name] = value89+ default:90+ return out, fmt.Errorf("unknown flag --%s", name)91+ }92+ }93+ return out, nil94+}
A cmd/012/diffcmd.go +209 -0
@@ -0,0 +1,209 @@
1+package main2+3+import (4+ "bytes"5+ "fmt"6+ "io"7+ "io/fs"8+ "os"9+ "slices"10+ "strings"11+12+ "github.com/FelineStateMachine/012/internal/diff"13+ "github.com/FelineStateMachine/012/internal/headless"14+)15+16+// 012 diff and 012 merge-driver: workbooks compared and merged cell by17+// cell, alone or as git's diff and merge drivers (see docs/files/git.md).18+19+const (20+ diffUsage = "usage: 012 diff a.012 b.012 [--format text|json|nuon] [--color auto|always|never]\n" +21+ " 012 diff --textconv file.012"22+ mergeUsage = "usage: 012 merge-driver base.012 ours.012 theirs.012 [path]"23+)24+25+// runDiff is 012 diff: exit status 0 when the workbooks are the same, 126+// when they differ and 2 on trouble, as diff(1). Called by git as27+// diff.<driver>.command, with git's seven or nine arguments, it writes28+// the changes under a header naming the file and exits 0, as git29+// expects of an external diff; with --textconv it writes one workbook30+// as lines for git's own diff.31+func runDiff(args []string, e env) error {32+ a, err := parseArgs(args, []string{"format", "color"}, []string{"textconv", "help"})33+ switch {34+ case err != nil:35+ return usageError(err.Error(), diffUsage)36+ case a.has("help"):37+ fmt.Fprintln(e.stdout, diffUsage)38+ return nil39+ case a.has("textconv"):40+ if len(a.pos) != 1 {41+ return usageError("", diffUsage)42+ }43+ return textconv(a.pos[0], e.stdout)44+ }45+ format := strings.ToLower(a.flags["format"])46+ if format != "" && !slices.Contains(diff.Formats, format) {47+ return usageError("--format "+format+": 012 diff writes "+strings.Join(diff.Formats, ", "), diffUsage)48+ }49+ color, err := useColor(a.flags["color"], e)50+ if err != nil {51+ return usageError(err.Error(), diffUsage)52+ }53+ var oldPath, newPath, gitPath string54+ switch len(a.pos) {55+ case 2:56+ oldPath, newPath = a.pos[0], a.pos[1]57+ case 7, 9: // path old-file old-hex old-mode new-file new-hex new-mode [new-path xfrm]58+ gitPath, oldPath, newPath = a.pos[0], a.pos[1], a.pos[4]59+ default:60+ return usageError("", diffUsage)61+ }62+ changes, err := compare(oldPath, newPath)63+ if err != nil {64+ return &exitError{code: 2, err: err}65+ }66+ if gitPath != "" {67+ return gitDiff(e.stdout, gitPath, changes, format, color)68+ }69+ if err := diff.WriteChanges(e.stdout, changes, format, color && format != "json" && format != "nuon"); err != nil {70+ return &exitError{code: 2, err: err}71+ }72+ if len(changes) > 0 {73+ return &exitError{code: 1}74+ }75+ return nil76+}77+78+// gitDiff writes the changes to one file for git: a header naming it,79+// as git's own diff has, and exit status 0.80+func gitDiff(w io.Writer, path string, changes []diff.Change, format string, color bool) error {81+ head := "diff --012 a/" + path + " b/" + path82+ if color {83+ head = "\x1b[1m" + head + "\x1b[0m"84+ }85+ if format == "" || format == "text" {86+ fmt.Fprintln(w, head)87+ }88+ if err := diff.WriteChanges(w, changes, format, color); err != nil {89+ return &exitError{code: 2, err: err}90+ }91+ return nil92+}93+94+// useColor decides whether the text output is colored: --color always95+// or never, or by default when standard output is a terminal or git's96+// pager, unless NO_COLOR is set.97+func useColor(flag string, e env) (bool, error) {98+ switch flag {99+ case "always":100+ return true, nil101+ case "never":102+ return false, nil103+ case "", "auto":104+ return e.getenv("NO_COLOR") == "" && (e.stdoutTTY || e.getenv("GIT_PAGER_IN_USE") == "true"), nil105+ }106+ return false, fmt.Errorf("--color %s: auto, always or never", flag)107+}108+109+// compare reads two workbooks and lists what changed.110+func compare(oldPath, newPath string) ([]diff.Change, error) {111+ a, err := readBook(oldPath)112+ if err != nil {113+ return nil, err114+ }115+ b, err := readBook(newPath)116+ if err != nil {117+ return nil, err118+ }119+ return diff.Compare(a, b), nil120+}121+122+// readBook reads a workbook to compare; /dev/null, which git names for123+// a file added or deleted, is a workbook without sheets.124+func readBook(path string) (*diff.Book, error) {125+ data, err := readSide(path)126+ if err != nil {127+ return nil, err128+ }129+ b, err := diff.Read(data)130+ if err != nil {131+ return nil, fmt.Errorf("%s: %w", path, err)132+ }133+ return b, nil134+}135+136+func readSide(path string) ([]byte, error) {137+ if path == os.DevNull {138+ return nil, nil139+ }140+ return os.ReadFile(path)141+}142+143+// textconv is 012 diff --textconv: a workbook as lines of text.144+func textconv(path string, w io.Writer) error {145+ b, err := readBook(path)146+ if err != nil {147+ return &exitError{code: 2, err: err}148+ }149+ return diff.Dump(w, b)150+}151+152+// runMergeDriver is 012 merge-driver, git's merge driver for .012153+// files: it merges theirs into ours cell by cell, from their common154+// base, and writes the result over ours. With conflicts it keeps ours155+// for each, lists them on standard error and exits 1, which git takes156+// as a conflict to resolve; on trouble it leaves ours as it was and157+// exits 2.158+func runMergeDriver(args []string, e env) error {159+ a, err := parseArgs(args, nil, []string{"help"})160+ switch {161+ case err != nil:162+ return usageError(err.Error(), mergeUsage)163+ case a.has("help"):164+ fmt.Fprintln(e.stdout, mergeUsage)165+ return nil166+ case len(a.pos) != 3 && len(a.pos) != 4:167+ return usageError("", mergeUsage)168+ }169+ ours, label := a.pos[1], a.pos[1]170+ if len(a.pos) == 4 {171+ label = a.pos[3]172+ }173+ var sides [3][]byte174+ for i, path := range a.pos[:3] {175+ if sides[i], err = readSide(path); err != nil {176+ return &exitError{code: 2, err: err}177+ }178+ }179+ merged, conflicts, err := diff.Merge(sides[0], sides[1], sides[2])180+ if err != nil {181+ return &exitError{code: 2, err: fmt.Errorf("merging %s: %w; ours is left as it was", label, err)}182+ }183+ if merged != nil {184+ if err := headless.WriteAtomic(ours, fileMode(ours), func(w io.Writer) error {185+ _, err := io.Copy(w, bytes.NewReader(merged))186+ return err187+ }); err != nil {188+ return &exitError{code: 2, err: err}189+ }190+ }191+ if len(conflicts) == 0 {192+ return nil193+ }194+ fmt.Fprintf(e.stderr, "012 merge-driver: %d %s in %s, where ours is kept; a cell's note says what theirs had:\n",195+ len(conflicts), plural(len(conflicts), "conflict", "conflicts"), label)196+ for _, c := range conflicts {197+ fmt.Fprintln(e.stderr, " "+c.String())198+ }199+ return &exitError{code: 1}200+}201+202+// fileMode is path's permissions, or 0644 when it has none to keep.203+func fileMode(path string) fs.FileMode {204+ st, err := os.Stat(path)205+ if err != nil {206+ return 0o644207+ }208+ return st.Mode().Perm()209+}
A cmd/012/evaluate.go +102 -0
@@ -0,0 +1,102 @@
1+package main2+3+import (4+ "context"5+ "errors"6+ "fmt"7+ "os"8+ "os/signal"9+ "time"10+11+ "github.com/FelineStateMachine/012/internal/config"12+ "github.com/FelineStateMachine/012/internal/headless"13+ "github.com/FelineStateMachine/012/internal/jev"14+ "github.com/FelineStateMachine/012/internal/nushell"15+)16+17+// jevTimeout bounds each JEV question, as on the screen.18+const jevTimeout = 60 * time.Second19+20+// evaluate runs what the flags ask of a workbook before it's read:21+// --notebooks runs its notebook cells with nu, when the workbook is22+// trusted here or --trust says so, as the screen asks before running23+// them; --jev answers its JEV functions over the network. Cells that24+// fail are reported on standard error.25+func evaluate(f *headless.File, a cliArgs, e env) evalFailures {26+ if a.has("trust") && !a.has("notebooks") {27+ return evalFailures{stop: usageError("--trust goes with --notebooks", "")}28+ }29+ if !a.has("notebooks") && !a.has("jev") {30+ return evalFailures{}31+ }32+ cfg, err := loadConfig(e, nil)33+ if err != nil {34+ return evalFailures{stop: err}35+ }36+ ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)37+ defer stop()38+ var out evalFailures39+ if a.has("notebooks") {40+ if err := mayRun(f, cfg, a.has("trust")); err != nil {41+ return evalFailures{stop: err}42+ }43+ out.cells = headless.RunNotebooks(ctx, f.Book, headless.NotebookOptions{44+ Runner: e.nuRunner(), Timeout: cfg.Duration("nu-timeout"), NuConfig: cfg.Bool("nu-config")})45+ for _, c := range out.cells {46+ fmt.Fprintln(e.stderr, "012: "+c)47+ }48+ }49+ if a.has("jev") {50+ client, err := jevClient(ctx, cfg, e)51+ if err != nil {52+ return evalFailures{stop: err}53+ }54+ headless.AnswerJEV(ctx, f.Book, client, jevTimeout)55+ }56+ return out57+}58+59+// mayRun reports why a workbook's notebook cells may not run: shell =60+// off, or a workbook from another computer without --trust (or shell =61+// on). A trusted workbook is marked trusted here, which a command that62+// saves keeps, as answering Run on the screen does.63+func mayRun(f *headless.File, cfg *config.Config, trust bool) error {64+ switch cfg.String("shell") {65+ case "off":66+ return errors.New("notebook cells don't run (shell = off in your config)")67+ case "on":68+ return nil69+ }70+ machine := machineID()71+ if headless.Trusted(f.Book, machine) {72+ return nil73+ }74+ if !trust {75+ return fmt.Errorf("%s was saved on another computer: its notebook cells would run as you, with your files; --trust runs them", f.Path)76+ }77+ if machine != "" {78+ f.Book.SetMacroOrigin(machine)79+ }80+ return nil81+}82+83+// nuRunner runs notebook cells: nu, or the test's fake.84+func (e env) nuRunner() nushell.Runner {85+ if e.nu != nil {86+ return e.nu87+ }88+ return nushell.Nu{}89+}90+91+// jevClient connects to JEV with the key from the environment, the92+// credential store or jev-api-key-command, as the screen finds it.93+func jevClient(ctx context.Context, cfg *config.Config, e env) (jev.Client, error) {94+ key, _, err := jev.ResolveKey(ctx, jev.KeySources{Getenv: e.getenv, Store: keyStore(cfg, e), Command: cfg.String("jev-api-key-command")})95+ if err != nil {96+ return nil, fmt.Errorf("JEV API key: %w", err)97+ }98+ if key == "" {99+ return nil, errors.New("--jev needs an API key: run 012 config set-key, or set " + jev.KeyEnv)100+ }101+ return jev.NewClient(jevConfig(cfg, key))102+}
A cmd/012/export.go +117 -0
@@ -0,0 +1,117 @@
1+package main2+3+import (4+ "context"5+ "fmt"6+ "os"7+ "path/filepath"8+ "strings"9+10+ "github.com/FelineStateMachine/012/internal/fileio"11+ "github.com/FelineStateMachine/012/internal/headless"12+ "github.com/FelineStateMachine/012/internal/sheet"13+)14+15+const exportUsage = "usage: 012 export file.012 out.csv|tsv|xlsx|json|nuon|sqlite [ref] [--format kind] [--table name] [--notebooks] [--jev] [--trust]"16+17+// runExport is 012 export: a sheet, a range or (to formats holding18+// several sheets) the whole workbook written as File > Download writes19+// it.20+func runExport(args []string, e env) error {21+ a, err := parseArgs(args, []string{"format", "table"}, append([]string{"help"}, evalFlags...))22+ switch {23+ case err != nil:24+ return usageError(err.Error(), exportUsage)25+ case a.has("help"):26+ fmt.Fprintln(e.stdout, exportUsage)27+ return nil28+ case len(a.pos) < 2 || len(a.pos) > 3:29+ return usageError("", exportUsage)30+ }31+ src, out := a.pos[0], a.pos[1]32+ k, err := exportKind(out, a.flags["format"])33+ if err != nil {34+ return usageError(err.Error(), exportUsage)35+ }36+ if same(src, out) {37+ return fmt.Errorf("%s is the workbook being exported: name another file", out)38+ }39+ f, err := headless.Open(src, false)40+ if err != nil {41+ return err42+ }43+ ref := ""44+ if len(a.pos) == 3 {45+ ref = a.pos[2]46+ }47+ t, err := headless.Resolve(f.Book, ref)48+ if err != nil {49+ return err50+ }51+ failed := evaluate(f, a, e)52+ if err := failed.fatal(); err != nil {53+ return err54+ }55+ snap := exportSnap(t, k, ref == "")56+ res, err := fileio.Export(context.Background(), out, k, snap, fileio.ExportOptions{Table: a.flags["table"]})57+ if err != nil {58+ return fmt.Errorf("%s: %w", out, err)59+ }60+ for _, n := range res.Notes {61+ fmt.Fprintln(e.stderr, "012: note: "+n)62+ }63+ return failed.err()64+}65+66+// exportKind is the format to write: --format's, or out's extension's.67+func exportKind(out, named string) (fileio.Kind, error) {68+ k, ok := fileio.KindOf(out)69+ if named != "" {70+ k, ok = fileio.KindNamed(named)71+ if !ok {72+ return 0, fmt.Errorf("--format %s: %s", named, exportable())73+ }74+ }75+ switch {76+ case !ok && strings.EqualFold(filepath.Ext(out), sheet.FileExt):77+ return 0, fmt.Errorf("%s is a workbook: copy the file instead, or %s", out, exportable())78+ case !ok:79+ return 0, fmt.Errorf("%s: name the format with --format, or give the file one of %s's extensions", out, exportable())80+ case !k.CanExport():81+ return 0, fmt.Errorf("012 reads %s files but doesn't write them; %s", k, exportable())82+ }83+ return k, nil84+}85+86+// exportable lists the formats 012 export writes.87+func exportable() string {88+ var names []string89+ for _, k := range fileio.Kinds() {90+ if k.CanExport() {91+ names = append(names, strings.ToLower(k.String()))92+ }93+ }94+ return "012 export writes " + strings.Join(names, ", ")95+}96+97+// exportSnap is what's written, as File > Download writes it: every98+// sheet for formats that hold several when no reference narrows it,99+// else the target, with the rows a filter hides left out of formats100+// that can't hide them.101+func exportSnap(t headless.Target, k fileio.Kind, whole bool) *fileio.Snapshot {102+ if whole && k.HoldsSheets() {103+ return fileio.SnapBook(t.Sheet)104+ }105+ r := t.Range106+ if t.Whole {107+ r = sheet.Rect{}108+ }109+ return fileio.Snap(t.Sheet, r, t.Sheet.Name())110+}111+112+// same reports whether two paths name one file.113+func same(a, b string) bool {114+ sa, err1 := os.Stat(a)115+ sb, err2 := os.Stat(b)116+ return err1 == nil && err2 == nil && os.SameFile(sa, sb)117+}
A cmd/012/git_test.go +204 -0
@@ -0,0 +1,204 @@
1+package main2+3+import (4+ "bytes"5+ "os"6+ "os/exec"7+ "path/filepath"8+ "strings"9+ "testing"10+11+ "github.com/FelineStateMachine/012/internal/headless"12+ "github.com/FelineStateMachine/012/internal/sheet"13+)14+15+// With O12_AS_MAIN set, the test binary is 012 itself, so git can run16+// it as a diff and merge driver.17+func TestMain(m *testing.M) {18+ if os.Getenv("O12_AS_MAIN") == "1" {19+ main()20+ os.Exit(0)21+ }22+ os.Exit(m.Run())23+}24+25+// gitRepo is a repository in a temporary directory whose .012 files use26+// 012 (this test binary) as their diff and merge drivers, as27+// docs/files/git.md sets them up.28+type gitRepo struct {29+ t *testing.T30+ dir string31+ env []string32+}33+34+func newGitRepo(t *testing.T) *gitRepo {35+ t.Helper()36+ if _, err := exec.LookPath("git"); err != nil {37+ t.Skip("git isn't installed")38+ }39+ exe, err := os.Executable()40+ if err != nil {41+ t.Fatal(err)42+ }43+ home := t.TempDir()44+ r := &gitRepo{t: t, dir: t.TempDir(), env: append(os.Environ(),45+ "O12_AS_MAIN=1", "HOME="+home, "XDG_CONFIG_HOME="+home, "GIT_CONFIG_NOSYSTEM=1", "GIT_CONFIG_GLOBAL="+filepath.Join(home, "gitconfig"),46+ "GIT_AUTHOR_NAME=t", "GIT_AUTHOR_EMAIL=t@example.com", "GIT_COMMITTER_NAME=t", "GIT_COMMITTER_EMAIL=t@example.com", "NO_COLOR=1")}47+ r.git("init", "-q", "-b", "main")48+ r.git("config", "diff.012.command", "'"+exe+"' diff")49+ r.git("config", "diff.012.textconv", "'"+exe+"' diff --textconv")50+ r.git("config", "merge.012.name", "012 cell by cell")51+ r.git("config", "merge.012.driver", "'"+exe+"' merge-driver %O %A %B %P")52+ r.write(".gitattributes", "*.012 diff=012 merge=012\n")53+ return r54+}55+56+// git runs git in the repository and returns its output, failing the57+// test when it fails.58+func (r *gitRepo) git(args ...string) string {59+ r.t.Helper()60+ out, err := r.try(args...)61+ if err != nil {62+ r.t.Fatalf("git %v: %v\n%s", args, err, out)63+ }64+ return out65+}66+67+func (r *gitRepo) try(args ...string) (string, error) {68+ cmd := exec.Command("git", args...)69+ cmd.Dir, cmd.Env = r.dir, r.env70+ out, err := cmd.CombinedOutput()71+ return string(out), err72+}73+74+func (r *gitRepo) write(name, text string) {75+ os.WriteFile(filepath.Join(r.dir, name), []byte(text), 0o644)76+}77+78+// set sets cells of book.012 with 012 set and commits.79+func (r *gitRepo) set(msg string, pairs ...string) {80+ r.t.Helper()81+ e, _, _ := testEnv(r.t, nil)82+ if err := run(append([]string{"set", filepath.Join(r.dir, "book.012")}, pairs...), e); err != nil {83+ r.t.Fatal(err)84+ }85+ r.git("add", "-A")86+ r.git("commit", "-q", "-m", msg)87+}88+89+func (r *gitRepo) cell(ref string, input bool) string {90+ r.t.Helper()91+ f, err := headless.Open(filepath.Join(r.dir, "book.012"), false)92+ if err != nil {93+ r.t.Fatal(err)94+ }95+ t, err := headless.Resolve(f.Book, ref)96+ if err != nil {97+ r.t.Fatal(err)98+ }99+ var b bytes.Buffer100+ headless.Get(&b, t, headless.GetOptions{Input: input})101+ return strings.TrimSuffix(b.String(), "\n")102+}103+104+func TestGitMerge(t *testing.T) {105+ r := newGitRepo(t)106+ r.set("base", "A1", "Item", "B1", "Price", "B2", "1", "B3", "2", "B4", "=SUM(B2:B3)")107+ r.git("checkout", "-q", "-b", "theirs")108+ r.set("theirs", "B2", "10", "C1", "Note")109+ r.git("checkout", "-q", "main")110+ r.set("ours", "B3", "20")111+ out := r.git("merge", "-q", "--no-edit", "theirs")112+ if r.cell("B2", true) != "10" || r.cell("B3", true) != "20" || r.cell("C1", true) != "Note" || r.cell("B4", false) != "30" {113+ t.Errorf("merged: B2 %s, B3 %s, C1 %s, B4 %s\n%s", r.cell("B2", true), r.cell("B3", true), r.cell("C1", true), r.cell("B4", false), out)114+ }115+116+ // Both change B2: a conflict, ours kept, theirs in B2's note.117+ r.git("checkout", "-q", "-b", "theirs2")118+ r.set("theirs2", "B2", "11")119+ r.git("checkout", "-q", "main")120+ r.set("ours2", "B2", "12", "D1", "x")121+ out, err := r.try("merge", "--no-edit", "theirs2")122+ if err == nil || !strings.Contains(out, "012 merge-driver: 1 conflict in book.012") || !strings.Contains(out, "Sheet1!B2 input: ours 12, theirs 11, base 10") ||123+ !strings.Contains(out, "CONFLICT") {124+ t.Errorf("a conflict: %v\n%s", err, out)125+ }126+ f, _ := headless.Open(filepath.Join(r.dir, "book.012"), false)127+ b2, _ := sheet.ParseAddr("B2")128+ note := f.Book.Sheet(0).Note(b2)129+ if r.cell("B2", true) != "12" || r.cell("D1", true) != "x" || note != "Merge conflict, kept ours; theirs had input 11" {130+ t.Errorf("after the conflict: B2 %s, D1 %s, note %q", r.cell("B2", true), r.cell("D1", true), note)131+ }132+}133+134+func TestGitDiff(t *testing.T) {135+ r := newGitRepo(t)136+ r.set("base", "A1", "Item", "B1", "1", "B2", "=B1*2")137+ e, _, _ := testEnv(t, nil)138+ run([]string{"set", filepath.Join(r.dir, "book.012"), "B1", "5", "A2", "new"}, e)139+ out := r.git("diff")140+ want := "diff --012 a/book.012 b/book.012\nSheet1!B1 input 1 → 5\nSheet1!A2 input + new\nSheet1!B2 value 2 → 10\n"141+ if out != want {142+ t.Errorf("git diff:\n%s\nwant\n%s", out, want)143+ }144+ out = r.git("diff", "--no-ext-diff")145+ for _, line := range []string{"-Sheet1!B1 1\n", "+Sheet1!B1 5\n", "+Sheet1!A2 new\n", "-Sheet1!B2 =B1*2 = 2\n", "+Sheet1!B2 =B1*2 = 10\n"} {146+ if !strings.Contains(out, line) {147+ t.Errorf("git diff with textconv lacks %q:\n%s", line, out)148+ }149+ }150+ // A file added shows every cell added.151+ r.git("add", "-A")152+ r.git("commit", "-q", "-m", "more")153+ os.WriteFile(filepath.Join(r.dir, "other.012"), []byte(`{"version": 2, "cells": {"A1": "hi"}}`), 0o644)154+ r.git("add", "other.012")155+ out = r.git("diff", "--cached")156+ if !strings.Contains(out, "diff --012 a/other.012 b/other.012\nsheet Sheet1 added\nSheet1!A1 input + hi\n") {157+ t.Errorf("a file added:\n%s", out)158+ }159+}160+161+func TestDiffCommand(t *testing.T) {162+ e, out, _ := scriptEnv(t)163+ dir := t.TempDir()164+ a, b := filepath.Join(dir, "a.012"), filepath.Join(dir, "b.012")165+ status(e, "set", a, "A1", "1")166+ status(e, "set", b, "A1", "2")167+ out.Reset()168+ if code, _ := status(e, "diff", a, a); code != 0 || out.Len() > 0 {169+ t.Errorf("the same: %d %q", code, out)170+ }171+ if code, _ := status(e, "diff", a, b, "--format", "nuon"); code != 1 || out.String() != "[[kind, sheet, item, field, old, new]; [cell, Sheet1, A1, input, \"1\", \"2\"]]\n" {172+ t.Errorf("different: %d %q", code, out)173+ }174+ out.Reset()175+ e.stdoutTTY = true176+ status(e, "diff", a, b)177+ if !strings.Contains(out.String(), "\x1b[31m1\x1b[0m") {178+ t.Errorf("no color on a terminal: %q", out)179+ }180+ for _, c := range []struct {181+ args []string182+ code int183+ want string184+ }{185+ {[]string{"diff", a}, 2, "usage: 012 diff"},186+ {[]string{"diff", a, filepath.Join(dir, "none.012")}, 2, "no such file"},187+ {[]string{"diff", a, b, "--format", "xml"}, 2, "--format xml"},188+ {[]string{"diff", a, b, "--color", "sometimes"}, 2, "--color sometimes"},189+ {[]string{"merge-driver", a, b}, 2, "usage: 012 merge-driver"},190+ {[]string{"merge-driver", a, b, filepath.Join(dir, "none.012")}, 2, "no such file"},191+ } {192+ if code, err := status(e, c.args...); code != c.code || err == nil || !strings.Contains(err.Error(), c.want) {193+ t.Errorf("%v: %d %v, want %d and %q", c.args, code, err, c.code, c.want)194+ }195+ }196+ os.WriteFile(filepath.Join(dir, "broken.012"), []byte("{"), 0o644)197+ before, _ := os.ReadFile(b)198+ if code, err := status(e, "merge-driver", a, b, filepath.Join(dir, "broken.012")); code != 2 || !strings.Contains(err.Error(), "ours is left as it was") {199+ t.Errorf("a broken side: %d %v", code, err)200+ }201+ if after, _ := os.ReadFile(b); !bytes.Equal(before, after) {202+ t.Error("a failed merge changed ours")203+ }204+}
M cmd/012/main.go +27 -11
@@ -15,14 +15,18 @@ import (
1515 "github.com/FelineStateMachine/012/internal/fileio"1616 "github.com/FelineStateMachine/012/internal/jev"1717 "github.com/FelineStateMachine/012/internal/keyring"18+ "github.com/FelineStateMachine/012/internal/nushell"1819 "github.com/FelineStateMachine/012/internal/sheet"1920 "github.com/FelineStateMachine/012/internal/ui"2021 )2122 2223 func main() {2324 if err := run(os.Args[1:], system()); err != nil {24- fmt.Fprintln(os.Stderr, "012:", err)25- os.Exit(1)25+ code, show := exitCode(err)26+ if show {27+ fmt.Fprintln(os.Stderr, "012:", err)28+ }29+ os.Exit(code)2630 }2731 }2832
@@ -41,13 +45,19 @@ type env struct {
4145 // openTTY opens the terminal for the UI when standard input and4246 // output belong to a pipeline (012 -, --pipe).4347 openTTY func() (io.ReadCloser, io.WriteCloser, error)48+ // stdoutTTY is set when standard output is a terminal, where 01249+ // diff colors what it writes.50+ stdoutTTY bool51+ // nu runs notebook cells for 012 get, recalc and export52+ // --notebooks; nil is the real nu.53+ nu nushell.Runner4454 }4555 4656 func system() env {4757 return env{4858 getenv: os.Getenv, keys: keyring.System(),4959 stdin: os.Stdin, stdout: os.Stdout, stderr: os.Stderr,50- readKey: readPassword, isTTY: isTerminal(os.Stdin), editorIO: true,60+ readKey: readPassword, isTTY: isTerminal(os.Stdin), stdoutTTY: isTerminal(os.Stdout), editorIO: true,5161 runTUI: func(m tea.Model, opts ...tea.ProgramOption) error {5262 _, err := tea.NewProgram(m, append([]tea.ProgramOption{tea.WithFPS(ui.FrameRate)}, opts...)...).Run()5363 return err
@@ -67,24 +77,30 @@ func usage() error {
6777 " sheet, or a .csv, .tsv, .json, .nuon, .xlsx, .sqlite, .parquet or .wk1 file to import\n" +6878 " 012 [flags] -: a table from standard input (NUON, JSON, CSV or TSV)\n" +6979 " 012 [flags] --pipe [--to nuon|json|csv|tsv] [--send ask|selection|sheet] [file]: on quitting, send the table to standard output\n" +70- " 012 nu [flags] [file]: a nushell notebook, at its prompt (see docs/nushell/notebooks.md)\n" +80+ " 012 nu [flags] [file]: the workbook's nushell notebook (see docs/nushell/notebooks.md)\n" +7181 " 012 nu --module | --install-module [--force] [path]: the nushell module with sheet (see docs/nushell/README.md)\n" +7282 " 012 serve [flags] [dir]: serve sheets in dir over SSH (see docs/terminal/ssh.md)\n" +83+ " 012 get|set|recalc|export file.012 ...: read and change a workbook without the screen (see docs/files/scripts.md)\n" +84+ " 012 diff a.012 b.012, 012 merge-driver base ours theirs: compare and merge workbooks (see docs/files/git.md)\n" +7385 " 012 config [path|edit|default|themes|set-key|delete-key]\n" +7486 " 012 version")7587 }7688 89+// subcommands run without the screen. A file with one of these names90+// opens as ./name.91+var subcommands = map[string]func([]string, env) error{92+ "config": runConfig, "serve": runServe,93+ "get": runGet, "set": runSet, "recalc": runRecalc, "export": runExport,94+ "diff": runDiff, "merge-driver": runMergeDriver,95+}96+7797 func run(args []string, e env) error {78- if len(args) > 0 && args[0] == "config" {79- return runConfig(args[1:], e)98+ if len(args) > 0 && subcommands[args[0]] != nil {99+ return subcommands[args[0]](args[1:], e)80100 }81101 if len(args) > 0 && (args[0] == "version" || args[0] == "--version") {82102 return runVersion(e)83103 }84- if len(args) > 0 && args[0] == "serve" {85- // A file called serve opens as ./serve.86- return runServe(args[1:], e)87- }88104 if len(args) > 0 && args[0] == "nu" && isModuleCommand(args[1:]) {89105 return runNuModule(args[1:], e)90106 }
@@ -138,7 +154,7 @@ func run(args []string, e env) error {
138154 }139155 m.Configure(settings)140156 if notebook {141- m.OpenShell()157+ m.OpenNotebook()142158 }143159 if offersKept(args, pipe.stdin) {144160 m.OfferKept()
M cmd/012/nu_test.go +5 -5
A cmd/012/scripts.go +181 -0
@@ -0,0 +1,181 @@
1+package main2+3+import (4+ "errors"5+ "fmt"6+ "io"7+ "slices"8+ "strings"9+10+ "github.com/FelineStateMachine/012/internal/headless"11+ "github.com/FelineStateMachine/012/internal/notebook"12+)13+14+// 012 get, set and recalc: workbook files read and changed without the15+// screen, for scripts (see docs/files/scripts.md). 012 export is in16+// export.go, and what --notebooks, --jev and --trust run in evaluate.go.17+18+const (19+ getUsage = "usage: 012 get file.012 [ref] [--format text|csv|tsv|json|nuon] [--input] [--no-header] [--notebooks] [--jev] [--trust]"20+ setUsage = "usage: 012 set file.012 ref input [ref input ...] [--force]"21+ recalcUsage = "usage: 012 recalc file.012 [--notebooks] [--jev] [--trust]"22+)23+24+// evalFlags are the flags that let a command run a workbook's notebook25+// cells or ask JEV; nothing runs without them.26+var evalFlags = []string{"notebooks", "jev", "trust"}27+28+// runGet is 012 get: a cell's value or input, or a range's as a table.29+func runGet(args []string, e env) error {30+ a, err := parseArgs(args, []string{"format"}, append([]string{"input", "no-header", "help"}, evalFlags...))31+ switch {32+ case err != nil:33+ return usageError(err.Error(), getUsage)34+ case a.has("help"):35+ fmt.Fprintln(e.stdout, getUsage)36+ return nil37+ case len(a.pos) < 1 || len(a.pos) > 2:38+ return usageError("", getUsage)39+ }40+ o := headless.GetOptions{Format: strings.ToLower(a.flags["format"]), Input: a.has("input"), NoHeader: a.has("no-header")}41+ if o.Format != "" && !slices.Contains(headless.Formats, o.Format) {42+ return usageError("--format "+o.Format+": 012 get writes "+strings.Join(headless.Formats, ", "), getUsage)43+ }44+ f, err := headless.Open(a.pos[0], false)45+ if err != nil {46+ return err47+ }48+ t, err := headless.Resolve(f.Book, strings.Join(a.pos[1:], ""))49+ if err != nil {50+ return err51+ }52+ failed := evaluate(f, a, e)53+ if err := failed.fatal(); err != nil {54+ return err55+ }56+ if err := headless.Get(e.stdout, t, o); err != nil {57+ return err58+ }59+ if missing := headless.MissingOutputs(t.Sheet); !a.has("notebooks") && len(missing) > 0 {60+ fmt.Fprintf(e.stderr, "012: note: %s shows no rows for %s, whose output the file doesn't hold; --notebooks runs the notebook\n",61+ t.Sheet.Name(), strings.Join(missing, ", "))62+ }63+ return failed.err()64+}65+66+// runSet is 012 set: type entries into cells and save.67+func runSet(args []string, e env) error {68+ a, err := parseArgs(args, nil, []string{"force", "help"})69+ switch {70+ case err != nil:71+ return usageError(err.Error(), setUsage)72+ case a.has("help"):73+ fmt.Fprintln(e.stdout, setUsage)74+ return nil75+ case len(a.pos) < 3 || len(a.pos)%2 == 0:76+ return usageError("", setUsage)77+ }78+ f, err := headless.Open(a.pos[0], true)79+ if err != nil {80+ return err81+ }82+ var entries []headless.Entry83+ for i := 1; i < len(a.pos); i += 2 {84+ entries = append(entries, headless.Entry{Ref: a.pos[i], Input: a.pos[i+1]})85+ }86+ warnings, err := headless.Set(f.Book, entries, headless.SetOptions{Force: a.has("force")})87+ if err != nil {88+ return fmt.Errorf("%w; %s is unchanged", err, f.Path)89+ }90+ for _, w := range warnings {91+ fmt.Fprintln(e.stderr, "012: warning: "+w)92+ }93+ return save(f, e)94+}95+96+// runRecalc is 012 recalc: recalculate, save, and list the cells whose97+// formulas show errors, exiting 1 when there are any.98+func runRecalc(args []string, e env) error {99+ a, err := parseArgs(args, nil, append([]string{"help"}, evalFlags...))100+ switch {101+ case err != nil:102+ return usageError(err.Error(), recalcUsage)103+ case a.has("help"):104+ fmt.Fprintln(e.stdout, recalcUsage)105+ return nil106+ case len(a.pos) != 1:107+ return usageError("", recalcUsage)108+ }109+ f, err := headless.Open(a.pos[0], false)110+ if err != nil {111+ return err112+ }113+ failed := evaluate(f, a, e)114+ if err := failed.fatal(); err != nil {115+ return err116+ }117+ problems, circular := headless.Recalc(f.Book)118+ if err := save(f, e); err != nil {119+ return err120+ }121+ return reportProblems(e.stdout, problems, circular, failed)122+}123+124+// save saves a workbook, keeping as much of its notebook outputs as the125+// config's nu-save-cell-kb and nu-save-notebook-kb allow, as the screen126+// saves it.127+func save(f *headless.File, e env) error {128+ cfg, err := loadConfig(e, nil)129+ if err != nil {130+ return err131+ }132+ f.Book.SetOutputCaps(notebook.Caps{Cell: cfg.Int("nu-save-cell-kb") << 10, Total: cfg.Int("nu-save-notebook-kb") << 10})133+ return f.Save()134+}135+136+// reportProblems lists the cells showing errors, one a line, and says137+// how many; the status is 1 when there are any or a region failed.138+func reportProblems(w io.Writer, problems []headless.Problem, circular bool, failed evalFailures) error {139+ for _, p := range problems {140+ line := p.At() + " " + p.Value141+ switch {142+ case p.JEV:143+ line += " JEV functions need --jev and an API key to be answered"144+ case p.Why != "":145+ line += " " + p.Why146+ }147+ fmt.Fprintln(w, line)148+ }149+ if circular {150+ fmt.Fprintln(w, "circular reference: a formula reads its own cell, directly or through others")151+ }152+ if len(problems) > 0 || circular {153+ return &exitError{code: 1, err: fmt.Errorf("%d %s showing errors", len(problems), plural(len(problems), "cell", "cells"))}154+ }155+ return failed.err()156+}157+158+func plural(n int, one, many string) string {159+ if n == 1 {160+ return one161+ }162+ return many163+}164+165+// evalFailures are what --notebooks and --jev couldn't do: a reason166+// nothing ran at all (fatal), or notebook cells that failed.167+type evalFailures struct {168+ stop error169+ cells []string170+}171+172+func (f evalFailures) fatal() error { return f.stop }173+174+// err is the status once the command's output is written: 1 when a175+// notebook cell failed, each already reported.176+func (f evalFailures) err() error {177+ if len(f.cells) == 0 {178+ return nil179+ }180+ return &exitError{code: 1, err: errors.New(fmt.Sprint(len(f.cells)) + " notebook " + plural(len(f.cells), "cell", "cells") + " failed")}181+}
A cmd/012/scripts_test.go +212 -0
@@ -0,0 +1,212 @@
1+package main2+3+import (4+ "bytes"5+ "context"6+ "io"7+ "os"8+ "path/filepath"9+ "strings"10+ "testing"11+12+ "github.com/FelineStateMachine/012/internal/notebook"13+ "github.com/FelineStateMachine/012/internal/nushell"14+ "github.com/FelineStateMachine/012/internal/sheet"15+)16+17+// scriptEnv is testEnv with standard error apart from standard output.18+func scriptEnv(t *testing.T) (env, *bytes.Buffer, *bytes.Buffer) {19+ t.Helper()20+ e, out, _ := testEnv(t, nil)21+ errOut := &bytes.Buffer{}22+ e.stderr = errOut23+ return e, out, errOut24+}25+26+// status runs 012 with args and returns its exit status, as main would.27+func status(e env, args ...string) (int, error) {28+ err := run(args, e)29+ if err == nil {30+ return 0, nil31+ }32+ code, _ := exitCode(err)33+ return code, err34+}35+36+func TestGetSetRecalc(t *testing.T) {37+ e, out, errOut := scriptEnv(t)38+ path := filepath.Join(t.TempDir(), "book.012")39+ if code, err := status(e, "set", path, "A1", "Item", "B1", "Price", "A2", "Apple", "B2", "-1.5", "B3", "=SUM(B2:B2)*2"); code != 0 {40+ t.Fatal(err)41+ }42+ for _, c := range []struct {43+ args []string44+ want string45+ }{46+ {[]string{"B3"}, "-3\n"},47+ {[]string{"Sheet1!B3", "--input"}, "=SUM(B2:B2)*2\n"},48+ {[]string{"A1:B2", "--format", "nuon"}, "[[Item, Price]; [Apple, -1.5]]\n"},49+ {[]string{"--format=json", "B3"}, "-3\n"},50+ } {51+ out.Reset()52+ if code, err := status(e, append([]string{"get", path}, c.args...)...); code != 0 || out.String() != c.want {53+ t.Errorf("get %v: %d %v %q, want %q", c.args, code, err, out, c.want)54+ }55+ }56+ for _, c := range []struct {57+ args []string58+ code int59+ want string60+ }{61+ {[]string{"get", path, "Q3!A1"}, 1, `no sheet named "Q3"`},62+ {[]string{"get", path, "A1", "--format", "xml"}, 2, "--format xml: 012 get writes text, csv, tsv, json, nuon"},63+ {[]string{"get", path, "A1", "--bogus"}, 2, "unknown flag --bogus"},64+ {[]string{"get", path, "A1", "--trust"}, 2, "--trust goes with --notebooks"},65+ {[]string{"get", filepath.Join(t.TempDir(), "none.012"), "A1"}, 1, "no such file"},66+ {[]string{"set", path, "A1"}, 2, "usage: 012 set"},67+ {[]string{"set", path, "B4", "=SUM(B1"}, 1, "Sheet1!B4: Expected , or ) in SUM, at character 8 of =SUM(B1; " + path + " is unchanged"},68+ {[]string{"recalc"}, 2, "usage: 012 recalc"},69+ } {70+ if code, err := status(e, c.args...); code != c.code || err == nil || !strings.Contains(err.Error(), c.want) {71+ t.Errorf("%v: %d %v, want %d and %q", c.args, code, err, c.code, c.want)72+ }73+ }74+75+ out.Reset()76+ if code, err := status(e, "recalc", path); code != 0 || out.Len() > 0 {77+ t.Errorf("recalc without errors: %d %v %q", code, err, out)78+ }79+ status(e, "set", path, "C1", "=1/0", "--", "C2", "--")80+ code, err := status(e, "recalc", path)81+ if code != 1 || err.Error() != "1 cell showing errors" || !strings.HasPrefix(out.String(), "Sheet1!C1 #DIV/0! ") {82+ t.Errorf("recalc with an error: %d %v %q", code, err, out)83+ }84+ out.Reset()85+ status(e, "get", path, "C2", "--input")86+ if out.String() != "--\n" {87+ t.Errorf("-- as a value after --: %q", out)88+ }89+ if errOut.Len() > 0 {90+ t.Errorf("stderr: %s", errOut)91+ }92+}93+94+func TestExport(t *testing.T) {95+ e, _, errOut := scriptEnv(t)96+ dir := t.TempDir()97+ path := filepath.Join(dir, "book.012")98+ status(e, "set", path, "A1", "n", "A2", "1", "A3", "=A2+1")99+ if code, err := status(e, "export", path, filepath.Join(dir, "out.csv")); code != 0 {100+ t.Fatal(err)101+ }102+ if data, _ := os.ReadFile(filepath.Join(dir, "out.csv")); string(data) != "n\n1\n2\n" {103+ t.Errorf("out.csv: %q", data)104+ }105+ if !strings.Contains(errOut.String(), "1 formula saved as values") {106+ t.Errorf("notes: %q", errOut)107+ }108+ if code, err := status(e, "export", path, filepath.Join(dir, "out.txt"), "A2:A3", "--format", "json"); code != 0 {109+ t.Fatal(err)110+ }111+ if data, _ := os.ReadFile(filepath.Join(dir, "out.txt")); string(data) != "[\n{\"1\":2}\n]\n" {112+ t.Errorf("out.txt: %q", data)113+ }114+ if code, err := status(e, "export", path, filepath.Join(dir, "out.xlsx")); code != 0 {115+ t.Fatal(err)116+ }117+ for _, c := range []struct {118+ args []string119+ want string120+ }{121+ {[]string{path, filepath.Join(dir, "copy.012")}, "is a workbook: copy the file instead"},122+ {[]string{path, filepath.Join(dir, "out.wk1")}, "012 reads WK1 files but doesn't write them"},123+ {[]string{path, filepath.Join(dir, "out")}, "name the format with --format"},124+ {[]string{path, path, "--format", "csv"}, "is the workbook being exported"},125+ } {126+ if _, err := status(e, append([]string{"export"}, c.args...)...); err == nil || !strings.Contains(err.Error(), c.want) {127+ t.Errorf("export %v: %v, want %q", c.args, err, c.want)128+ }129+ }130+}131+132+// fakeNu answers every command with one table.133+type fakeNu struct{ ran int }134+135+func (f *fakeNu) Run(_ context.Context, _ nushell.Job, _ string, stdout io.Writer) error {136+ f.ran++137+ _, err := io.WriteString(stdout, "[[name]; [a.txt]]")138+ return err139+}140+141+// notebookFile writes a workbook whose notebook's cell files = ls is142+// sent to Sheet1!A1, made on the computer origin.143+func notebookFile(t *testing.T, origin string) string {144+ t.Helper()145+ w := sheet.NewBook()146+ nb, _ := w.AddNotebook("Notebook", w.Sheet(0))147+ nb.SetNotebookCells("add cell", []notebook.Cell{{Kind: notebook.Code, Source: "files = ls"}})148+ a1, _ := sheet.ParseAddr("A1")149+ if err := w.Sheet(0).AddRegion(sheet.Region{Name: "files", At: a1, Output: true}); err != nil {150+ t.Fatal(err)151+ }152+ w.SetMacroOrigin(origin)153+ path := filepath.Join(t.TempDir(), "nb.012")154+ var b bytes.Buffer155+ w.Write(&b)156+ os.WriteFile(path, b.Bytes(), 0o644)157+ return path158+}159+160+func TestNotebooksNeedTrust(t *testing.T) {161+ e, out, errOut := scriptEnv(t)162+ nu := &fakeNu{}163+ e.nu = nu164+ path := notebookFile(t, "another computer")165+166+ status(e, "get", path, "A2")167+ if nu.ran != 0 || out.String() != "\n" || !strings.Contains(errOut.String(), "012: note: Sheet1 shows no rows for files, whose output the file doesn't hold; --notebooks runs the notebook\n") {168+ t.Errorf("without --notebooks: ran %d, %q %q", nu.ran, out, errOut)169+ }170+ if code, err := status(e, "get", path, "A2", "--notebooks"); code != 1 || nu.ran != 0 || !strings.Contains(err.Error(), "saved on another computer") {171+ t.Errorf("untrusted: %d %v, ran %d", code, err, nu.ran)172+ }173+ out.Reset()174+ if code, err := status(e, "get", path, "A2", "--notebooks", "--trust"); code != 0 || nu.ran != 1 || out.String() != "a.txt\n" {175+ t.Errorf("--trust: %d %v, ran %d, %q", code, err, nu.ran, out)176+ }177+ // get saves nothing, so the file still asks; recalc saves the trust,178+ // and the output, which get then reads without running anything.179+ if _, err := status(e, "recalc", path, "--notebooks"); err == nil {180+ t.Error("get --trust saved the trust")181+ }182+ if code, err := status(e, "recalc", path, "--notebooks", "--trust"); code != 0 {183+ t.Fatal(err)184+ }185+ out.Reset()186+ errOut.Reset()187+ if code, err := status(e, "get", path, "A2"); code != 0 || nu.ran != 2 || out.String() != "a.txt\n" || errOut.Len() > 0 {188+ t.Errorf("the output saved: %d %v, ran %d, %q %q", code, err, nu.ran, out, errOut)189+ }190+ if code, err := status(e, "get", path, "A2", "--notebooks"); code != 0 || nu.ran != 3 {191+ t.Errorf("trusted by recalc: %d %v, ran %d", code, err, nu.ran)192+ }193+194+ writeConfig(t, "shell = off\n")195+ if _, err := status(e, "get", path, "A1", "--notebooks", "--trust"); err == nil || !strings.Contains(err.Error(), "shell = off") {196+ t.Errorf("shell = off: %v", err)197+ }198+ writeConfig(t, "shell = on\n")199+ other := notebookFile(t, "elsewhere")200+ if code, err := status(e, "get", other, "A1", "--notebooks"); code != 0 {201+ t.Errorf("shell = on: %v", err)202+ }203+}204+205+func TestJEVNeedsKey(t *testing.T) {206+ e, _, _ := scriptEnv(t)207+ path := filepath.Join(t.TempDir(), "book.012")208+ status(e, "set", path, "A1", "1")209+ if _, err := status(e, "get", path, "A1", "--jev"); err == nil || !strings.Contains(err.Error(), "--jev needs an API key") {210+ t.Errorf("--jev without a key: %v", err)211+ }212+}
M demos/follow-log.tape +8 -7
M demos/notebook.tape +47 -15
@@ -1,7 +1,8 @@
1-# Notebooks: 012 nu opens a notebook at the nu❯ prompt. Each pipeline's2-# table becomes a named region; a second reads the first as $sales, and3-# giving sales a new command runs it again and then the region reading4-# it. Needs nu on the PATH.1+# Notebooks: 012 nu opens a notebook, its first cell ready to type in.2+# A code cell reads a CSV as sales, a second reads it as $sales, and its3+# output goes to a sheet (G) where a formula sums it; with the notebook4+# reactive, editing sales runs both again and the sheet follows. The terminal (ttyd) sends Shift+Enter as Enter, so5+# cells run with Esc then r. Needs nu on the PATH.56 Output out/notebook.gif67 Source lib/setup.tape78
@@ -11,22 +12,53 @@ Sleep 2s
1112 Show1213 1314 Sleep 800ms14-Type "sales = open data/sales.csv | where Region in [North South] | select Region Quarter Units"15+Type@25ms "sales = open data/sales.csv | where Region in [North South] | select Region Quarter Units"1516 Sleep 500ms16-Enter17+Escape18+Type "r"1719 Sleep 1.5s18-Type "$sales | where Units > 250 | sort-by Units --reverse"19-Sleep 500ms20+Type "b"2021 Enter22+Type@25ms "top = $sales | where Units > 250 | sort-by Units --reverse"23+Sleep 500ms24+Escape25+Type "r"2126 Sleep 2s22-Screenshot out/stills/notebook-regions.png23-Type "sales = open data/sales.csv | where Quarter == Q4 | select Region Quarter Units"27+Screenshot out/stills/notebook-cells.png28+Type "G"29+Sleep 800ms30+Enter31+Sleep 1.2s32+Ctrl+K33+Type@60ms "next sheet"2434 Sleep 500ms2535 Enter26-Sleep 2.5s36+Sleep 600ms37+Right Right Right Right38+Type "=SUM(nu.top)" Enter39+Sleep 1.5s40+Screenshot out/stills/notebook-sent.png41+Ctrl+K42+Type@60ms "previous sheet"43+Sleep 500ms44+Enter45+Sleep 600ms46+Ctrl+K47+Type@80ms "reactive"48+Sleep 600ms49+Enter50+Sleep 400ms51+Up Up52+Sleep 300ms53+Enter54+Type@30ms " | where Quarter != Q1"2755 Escape56+Type "r"57+Sleep 2.5s58+Screenshot out/stills/notebook-reactive.png59+Ctrl+K60+Type@60ms "next sheet"2861 Sleep 500ms29-Down Down Down30-Sleep 1.5s31-Screenshot out/stills/notebook-refreshed.png32-Sleep 1.5s62+Enter63+Sleep 2s64+Screenshot out/stills/notebook-sum.png
M demos/nu-recipe.tape +19 -10
M docs/README.md +2 -2
M docs/contributing/architecture.md +101 -66
@@ -9,7 +9,7 @@ sidebar_position: 2
99 Lip Gloss v2.1010 1111 ```12-cmd/012 entry point: flags and config, 012 config, 012 serve, JEV setup, opening or importing a file, 012 - and --pipe on the terminal12+cmd/012 entry point: flags and config, 012 config, 012 serve, JEV setup, opening or importing a file, 012 - and --pipe on the terminal, the commands without the screen1313 internal/config the config file and the registry of options1414 internal/sheet the engine: cells, recalculation, undo, files, names, pivots1515 internal/functions the function library: the FuncDef table, evaluation, decimal arithmetic, JEV questions
@@ -20,11 +20,14 @@ internal/locale the locales: separators, date order, currency, formula separato
2020 internal/fileio import and export: CSV, TSV, XLSX, SQLite, Parquet, Lotus .wk1, JSON, NUON; tables on streams2121 internal/nuon nushell's object notation: typed values, tables read a row at a time, written back2222 internal/live the sources of linked regions: files followed as they grow or are rewritten23+internal/headless workbook files without the screen: references, get, set, recalc, notebooks and JEV when asked, atomic saves24+internal/diff workbooks compared and merged cell by cell, from their files' fields2325 internal/chart chart layout, text rendering and kitty image encoding2426 internal/jev the API key's resolution, answer cache and TypeSafe client2527 internal/keyring the OS credential store the API key lives in2628 internal/macro macros: the Starlark scripting API, recorded actions as scripts, step limits27-internal/nushell a notebook's commands: nu run as a process, tables in as NUON files, a table back29+internal/notebook a notebook's document: code and note cells, names, what each reads, run orders, stale outputs, save caps30+internal/nushell a notebook cell's run: nu as a process, tables in as NUON files, NUON back2831 module 012.nu, the nushell module with sheet, embedded for 012 nu --module and --install-module2932 internal/telemetry opt-in JSON log and OTLP export of spans, events and frame stats3033 internal/serve the SSH server: auth, host key, a Model per session (charm.land/wish/v2)
@@ -35,10 +38,10 @@ internal/ui the Bubble Tea model: modes, menus, overlays, rendering
3538 rowtext laying out a row of cell text across the columns on screen3639 formula reading the formula being typed (F4, the word and call at the caret)3740 overlay the contract of what takes over input: Overlay, boxes, mouse events, lists38- lineedit the one-line editor every text field shares41+ lineedit the one-line editor every text field shares, and the multi-line one of notebook cells3942 picker the searchable list behind the palette and every picker4043 cmdline the : command line and its completions41- nuprompt the nushell prompt on the formula bar, its history and completions44+ nbview a notebook tab: its cells, outputs, command and edit modes, the code editor and its language providers4245 findbar find and replace, a bar on the context line4346 themepicker File > Settings > Theme, previewing as it moves4447 rules the conditional formatting and data validation panel
@@ -67,6 +70,7 @@ flowchart TD
6770 direction LR6871 ui[internal/ui] ~~~ fileio[internal/fileio] ~~~ live[internal/live] ~~~ nushell[internal/nushell]6972 chart[internal/chart] ~~~ jev[internal/jev] ~~~ stress[internal/stress] ~~~ oracle[oracle]73+ headless[internal/headless] ~~~ diff[internal/diff]7074 end7175 callers --> sheet[internal/sheet]7276 sheet --> functions[internal/functions]
@@ -227,35 +231,35 @@ style.
227231 `#REF!` with the reason. `Set` refuses spilled cells; files keep only228232 the anchor.229233 - **Regions.** A region (`region.go`) is a block of cells whose values230- come from outside the engine, of two kinds: a command region, a231- notebook's nushell command (opaque to the engine) with a label line232- above its table, and a linked file (`linked.go`), a file the UI233- follows, its table at its anchor. Both are one list per sheet, the234- sheet's `regionState`, replaced whole on every change so undo steps235- keep the definitions; what isn't undone (a label's status, the cells236- written, how a linked file's reading goes) is kept beside it237- (`regionMeta`). Their cells have one write path, `writeTable`238- (`regionwrite.go`): a header and rows, written as spilled cells are,239- derived and outside the undo history, only those that differ, with240- the region's cells it no longer needs cleared; a table that would241- overwrite other contents isn't shown, its label or first cell saying242- where, and clearing that cell shows it. Rows arrive as live243- operations, the change stream below. What undo keeps of the rows is a244- policy per kind: a command region's table (`RegionData`, handed over245- by `ShowRegion`) is in the undo state, since a run was the user's246- action and its output can't be had again, and is written again from247- there whenever its region moves; a linked file's rows are only in its248- cells, since undo can't bring back what a file held, so a linked file249- undo brings back or moves is emptied and read again. Placing a cell in250- a region (a format, undo) keeps the region's value. On a notebook251- sheet a command region's table that grows or shrinks inserts or252- deletes rows under it inside the same step, so the rows below move as253- they would by hand (`regionshape.go`). Formulas name a region's table254- as `nu.name`, resolved when evaluated as named ranges are; commands255- read regions as `$name`, which makes a dependency graph256- (`regiongraph.go`) giving refresh and run orders, cycles refused, and257- a linked file in it never runs. The file keeps the definitions258- (`regionfile.go`).234+ come from outside the engine, of two kinds: a linked file235+ (`linked.go`), a file the UI follows, and a notebook cell's output sent236+ to a sheet (`Region.Output`), each its table at its anchor. Both are237+ one list per sheet, the sheet's `regionState`, replaced whole on every238+ change so undo steps keep the definitions; what isn't undone (the239+ cells written, how the rows arrive) is kept beside it (`regionMeta`).240+ Their cells have one write path, `writeTable` (`regionwrite.go`): a241+ header and rows, written as spilled cells are, derived and outside the242+ undo history, only those that differ, with the region's cells it no243+ longer needs cleared; a table that would overwrite other contents244+ isn't shown, its first cell saying where, and clearing that cell shows245+ it. Rows arrive only as live operations, the change stream below, and246+ are never part of the undo state, since undo can't bring back what a247+ file held or a run printed: a region undo brings back or moves is248+ emptied and marked stale, and the UI sends its rows again, the file249+ read again or the cell's output as it is. Placing a cell in a region250+ (a format, undo) keeps the region's value. Formulas name a region's251+ table as `nu.name`, resolved when evaluated as named ranges are. The252+ file keeps the definitions (`regionfile.go`), and converts the command253+ regions of earlier files into notebook cells (`notebookfile.go`).254+- **Notebooks.** A notebook tab is a sheet whose `regionState` holds255+ cells (`notebook.go`), so adding, editing, moving and deleting cells256+ are undo steps as any change is, and the tab is named, moved, hidden257+ and deleted as a sheet is. The document itself, cells, names, what a258+ cell reads, run orders and stale outputs, is `internal/notebook`,259+ which knows nothing of sheets. Outputs aren't undone: the workbook260+ keeps each cell's last `notebook.Output` by the cell's ID, its NUON as261+ nu printed it, and the file keeps them up to the caps262+ (`notebookfile.go`).259263 - **Rules.** A sheet's conditional formats and data validation260264 (`rules.go`, `condfmt.go`, `validation.go`) are lists of rules on261265 ranges, replaced whole on every change so undo steps keep them as they
@@ -276,19 +280,18 @@ style.
276280 277281 ### The change stream278282 279-Rows reach a region through the regions' one write path, `writeTable`.280-A linked file's arrive as `sheet.LiveOp`s, each applied at once by281-`Workbook.ApplyLive`; a command region's table is written the same way282-from its `RegionData`, which a run puts in the undo state:283+Rows reach a region as `sheet.LiveOp`s, each applied at once by284+`Workbook.ApplyLive` through the regions' one write path, `writeTable`:285+what follows a linked file sends them, and the UI sends a notebook286+cell's output to the region it was sent to whenever the cell runs.283287 284288 ```mermaid285289 flowchart TD286290 file[a linked file] -->|polled| source[live.Source]287291 source -->|live.Update| op[sheet.LiveOp]292+ cell[a notebook cell's run] -->|its output, fileio.NUONRows| op288293 op --> apply[Workbook.ApplyLive]289- run[a notebook command's run] -->|ShowRegion, an undo step| data[RegionData]290294 apply --> write[writeTable: the cells that differ, outside the undo history]291- data --> write292295 write --> recalc[recalculation: what reads them, pivots and spills included]293296 recalc --> frame[the next frame draws the cells on screen]294297 ```
@@ -309,8 +312,7 @@ the cells recalculates when the step ends. An op carries whole
309312 values and names nothing but its region, so applying the same ops in the310313 same order to the same workbook makes the same cells: it is what a311314 session following another's over `012 serve` would be sent, beside the312-steps `Batch` records (a command region's run is one of those, since313-its table is in the undo state). `OnLive` times each op for telemetry.315+steps `Batch` records. `OnLive` times each op for telemetry.314316 315317 ### Functions316318
@@ -479,7 +481,7 @@ draw. The components:
479481 | mouse | `mouseState` | drags, hover, double clicks, the fill handle (`mouse.go`, `fill.go`) |480482 | import | `transfer.Transfer`, `pipeState` | the import in progress, its progress display and cancelling (package `transfer`); choosing and placing imports (`transfer.go`, `importplace.go`); standard input read as a sheet, and what a pipeline gets on quitting (`pipe.go`) |481483 | linked files | `followState` | a source (`live.Source`) for each linked region, polled one poll at a time on a command every `live.Interval` and reconciled with the workbook after every update, so undo, opening a file and unlinking need nothing of their own; trust in files outside a workbook's folder (`follow.go`); Data > Linked file and Import's Follow the file (`linked.go`) |482-| notebooks | `nuprompt.Prompt`, `shellState` | the prompt (package `nuprompt`); the commands running one at a time in the background, their queue and what they said (`nurun.go`); the notebook's commands, keys, lines and trust (`notebook.go`) |484+| notebooks | `nbview.View`, `nbState` | a view of each notebook tab, its selection, mode, scroll and editor (package `nbview`); the tab on the screen (`nbscreen.go`); the notebook's commands and keys (`notebook.go`); cells running one at a time in the background, their queue, stale outputs, trust (`nbrun.go`); what a run reads (`nbjob.go`); outputs sent to sheets (`nbsend.go`) |483485 | macros | `recorder`, `macroState` | a recording in progress (`macrorec.go`); a macro running, trust in the file's macros (`macrorun.go`); what scripts act on (`macrohost.go`, `macrohostnav.go`); Data > Macros and the manager (`macro.go`, `macromanage.go`) |484486 | others | `clipboard`, `trace`, `chartState`, `jevRunner`, `terminal`, `session` | what Ctrl+V pastes, a trace being shown, chart commands' target, JEV questions in flight, what the terminal supports and the chart images sent to it, what outlasts the file open (the `:` history, whether keys can be held: `keyboard.go`) |485487
@@ -506,7 +508,7 @@ methods off `ui.Model`'s exported API:
506508 | `sortbar` | `sortbar.Host` | theme, size, the sheet, close, sort (recorded as the bar's command) (5) |507509 | `filterpick` | `filterpick.Host` | theme, size, the edit line, close, the locale (5); what applying and cancelling do are callbacks, as the sheet's filter and a pivot's differ |508510 | `cmdline` | `cmdline.Host` | theme, size, the edit line, close, the commands to complete, run a line, fail, the session's history (8) |509-| `nuprompt` | `nuprompt.Host` | theme, size, the edit line, close, run a line, the words to complete, the workbook's history, what's running, stop it, what the last run said (10) |511+| `nbview` | `nbview.Host` | theme, locale, the cells, a cell's output, how its run stands, run a command, keep a cell's new source (7); the language's highlighter, completer and checker are `nbview.Providers`, small interfaces of their own |510512 | `suggest` | `suggest.Host` | theme, size, the edit line, whether an entry is being typed, the entry's sheet, the formula as parsed, where the formula bar's text starts (7) |511513 | `themepicker` | `themepicker.Host` | a picker's host, and the current theme, the themes directory, preview, keep, whether keys can be held (10) |512514 | `rules` | `rules.Host` | theme, size, the edit line, close, the sheet and selection, save a conditional format or a validation rule, follow a rule removed or moved (each recorded as the commands that do it), the terminal's palette colors (10) |
@@ -552,7 +554,7 @@ hints) are what every overlay is drawn with.
552554 553555 **Packages.** `theme`, `rowtext`, `formula`, `overlay` and `lineedit`554556 depend on nothing in `ui`, so they can be tested and measured alone. The555-components in `picker`, `cmdline`, `nuprompt`, `themepicker`, `findbar`, `rules`,557+components in `picker`, `cmdline`, `nbview`, `themepicker`, `findbar`, `rules`,556558 `sortbar`, `filterpick`, `choicebar`, `shortcuts`, `suggest`, `tabstrip`557559 and `transfer` build on them and reach the model only through their558560 hosts, with unit tests of their own against fake hosts. A component moves out of package
@@ -597,7 +599,7 @@ file followed as it grows: the importers' readers run on a goroutine of
597599 its own over a pipe that waits for the next piece, so a record cut off598600 at the end of one waits for the rest, and rows come out typed as an599601 import types them (`tail.go`). A `live.Source` is anything that yields a600-table's rows over time, which a nushell region following a pipeline602+table's rows over time, which a notebook cell following a pipeline601603 would be too.602604 603605 XLSX is read by 012's own SpreadsheetML reader on `archive/zip` and
@@ -619,6 +621,23 @@ written a row at a time. One package suits formats that share this much
619621 translation); a format that grew its own dependencies would move to a620622 subpackage behind the same table row.621623 624+`internal/headless` is the engine without the screen, for `012 get`,625+`set`, `recalc` and `export` ([Scripts](../files/scripts.md)): it626+resolves references, writes values through the same snapshots and627+encoders as downloads, types entries through `Sheet.Set` in one628+`Batch`, and saves with a write-then-rename. It sends the outputs a629+file kept to their sheets as the screen does on opening, runs notebook630+cells through `internal/nushell` and asks `internal/jev` only when its631+caller says so; `cmd/012` decides, by the flags and the macro-origin632+trust.633+634+`internal/diff` reads a `.012` file as its JSON fields: the workbook's,635+and each sheet's with its cells by address. Comparing and merging go636+field by field, so a field added to the format is compared and merged637+without teaching them about it, and the engine is used only to compute638+values (`Compare`) and to check and write the merged workbook639+(`Merge`), which it reads back as 012 would open it.640+622641 ## Macros623642 624643 Macros are Starlark scripts (`go.starlark.net`, pure Go) kept in the
@@ -659,34 +678,50 @@ the user's editor (`AllowEditor`), since that starts a program.
659678 660679 ## Notebooks661680 662-`internal/nushell` runs a notebook's command: a `Job` holds the command663-and the NUON of the tables it reads, and the script `nu -c` runs binds664-each table from a file named by an environment variable665-(`let r1 = (open --raw $env.NU012_TABLE_0 | from nuon)`), feeds the666-selection as `$in` on standard input, and ends in `to nuon`, so no data667-is ever spliced into the command. What nu prints is read as it's668-written by the NUON importer, capped at max-cells:681+`internal/nushell` runs a code cell: a `Job` holds the pipeline and the682+NUON of what it reads, and the script `nu -c` runs binds each from a683+file named by an environment variable684+(`let files = (open --raw $env.NU012_TABLE_0 | from nuon)`) and ends in685+`to nuon`, so no data is ever spliced into the pipeline; a range of a686+sheet (`$sheet.A1:C9`) is renamed to a variable of its own first687+(`notebook.Bind`). What nu prints is kept as it is, NUON, the cell's688+output: a later cell reads it byte for byte as `$name`, `nbview` parses689+it once to draw it, and `fileio.NUONRows` reads it as a region's rows to690+send to a sheet:669691 670692 ```mermaid671693 sequenceDiagram672694 participant UI as internal/ui673695 participant R as nushell.Runner674696 participant Nu as nu -c675- UI->>R: a Job: the command, the NUON of the tables it reads676- R->>R: each table to a file, named in NU012_TABLE_0, 1, ...677- R->>Nu: the script, with the selection on standard input678- Nu->>Nu: let r1 = (open --raw $env.NU012_TABLE_0 | from nuon)679- Nu-->>R: the command's table, to nuon680- R-->>UI: rows read as they're written, up to max-cells681- UI->>UI: ShowRegion: the table, one undo step697+ UI->>R: a Job: the pipeline, the NUON of what it reads698+ R->>R: each to a file, named in NU012_TABLE_0, 1, ...699+ R->>Nu: the script700+ Nu->>Nu: let files = (open --raw $env.NU012_TABLE_0 | from nuon)701+ Nu-->>R: the pipeline's value, to nuon702+ R-->>UI: the NUON, up to 512 MB703+ UI->>UI: the cell's output, not an undo step; sent on to its region682704 ```683- `Runner` is the684-seam: tests hand the UI a fake, the e2e screens a stand-in `nu`685-script. The UI runs one command at a time as a `tea.Cmd` with a686-context Esc cancels and a timeout, and shows each table as an undo687-step when it arrives; the queue a refresh makes comes from the688-engine's graph. Trust is the macros': a file's commands run only once689-they were made or trusted on this computer, or the user agrees.705+706+`Runner` is the seam: tests hand the UI a fake, the e2e707+screens a stand-in `nu` script. The UI runs one cell at a time as a708+`tea.Cmd` with a context Stop cancels and a timeout; the queue a run709+makes comes from `notebook.Order` (each cell after those it reads) and710+`notebook.Inputs` (what it reads that hasn't run), and a reactive711+notebook adds `notebook.Dependents`. An output records the source it712+ran and the `Seq` of each output it read, so `notebook.Stale` tells713+which outputs may be out of date without running anything. Trust is714+the macros': a file's cells run only once they were made or trusted on715+this computer, or the user agrees.716+717+`nbview` draws each cell as a block of lines, working out only how many718+lines each takes and drawing those on screen, so an expanded output of719+ten thousand rows costs the rows showing; a table's column widths are720+fitted once, when its output is parsed. The code editor721+(`lineedit.Area`) wraps at the cell's width, before pipes where it can;722+highlighting, completion and diagnostics come from `nbview.Providers`,723+asked in the background once typing pauses, with a tokenizer and the724+notebook's names as the built-in answers.690725 691726 ## Charts692727
M docs/contributing/testing.md +39 -0
@@ -92,6 +92,45 @@ formatted values with 012's. Known differences between Sheets and Excel,
9292 and places where excelize departs from Excel, are listed as skips with a9393 reason.9494 95+## The Sheets corpus96+97+012 follows Google Sheets' formula results where people rely on them98+([UX](ux.md)), and `internal/fileio/testdata/sheets_corpus.tsv` holds99+about 300 formulas that pin it down: dates and times, text, rounding and100+floating point, errors and how they propagate, coercion of text,101+booleans and blanks, lookups and criteria, array sizes, LET and LAMBDA,102+and `TEXT` formats. Each line is a formula, 012's result, Sheets'103+result and a note, separated by tabs. Results read `"text"` quoted,104+`12.5`, `TRUE`, `#N/A`, or `(blank)`. Formulas read a small fixture on a105+sheet named `Data`, and each computes one value: an array is measured106+with `ROWS`, `COLUMNS`, `INDEX` or `TEXTJOIN`, so its size is checked107+and nothing spills.108+109+`TestSheetsCorpus` holds 012 to the results recorded for it, and fails110+on a row where Sheets' result differs unless its note starts111+`on purpose:` and says why; that reason also goes in the docs page for112+the feature. Sheets' result reads `unconfirmed` until it is checked in113+Sheets:114+115+```sh116+# Add formulas as lines of their own, then record 012's results.117+go test ./internal/fileio -run SheetsCorpus -update-corpus118+# Write the corpus as a workbook with 012's XLSX exporter.119+go test ./internal/fileio -run SheetsCorpus -corpus-xlsx=$HOME/012-sheets-corpus.xlsx120+# In Sheets (locale United States): File > Import the workbook, then121+# File > Download > CSV of the Corpus sheet. Record Sheets' results:122+go test ./internal/fileio -run SheetsCorpus -update-from="$HOME/Downloads/012-sheets-corpus - Corpus.csv"123+```124+125+In the workbook, column C holds each formula, which Sheets recalculates126+on import; D and E read its result as text and its kind with formulas127+both 012 and Sheets compute, F holds 012's result and G says whether128+they agree (for two errors, compare C and F by eye). The exporter saves129+formulas Excel has nothing like (`SPLIT`, `REGEXMATCH`, `SORTN`,130+`FLATTEN`, `ARRAYFORMULA` inside a formula) as values, so those rows131+leave C empty and say so in the note: type the formula from B into C132+before downloading, or `-update-from` leaves them unconfirmed.133+95134 ## The XLSX differential test96135 97136 `TestXLSXDifferential` (`internal/fileio/xlsx_diff_test.go`) imports a
M docs/contributing/ux.md +2 -2
@@ -99,12 +99,12 @@ there.
9999 | Entries failing validation | A dotted underline, and `Invalid:` with the rule on the context line |100100 | Values an array spilled | Italic, and `Spilled from B2` on the context line |101101 | Linked files' rows, following, paused or failing | Italic; `●`, `‖` or `!` beside the tab's name and on the context line, with the state in words (`Following`, `Paused`, the error) |102-| A notebook's regions | The label line's text (`r1 ls`, and `Running…` or `failed`) underlined as wide as the table, and `Region r1 of ls` on the context line |102+| A notebook's cells and outputs | `❯` before the tab's name; the selected cell's head in reverse video; the head's words for its state (`[*]` and `running`, `waiting`, `failed`, `stale`); `×` before an error; an output table's header bold and underlined; an output sent to a sheet in italic, with `Output of files` on the context line; a problem nu finds in the cell being written curly-underlined, in words on the context line with the caret on it |103103 | Pivot table results | Their headings (`SUM of Units`, `Grand Total`), and a note in words when an edit is refused |104104 | Protected ranges | A question in words before an edit (`A1:B2 is protected.`) |105105 | Notes | A `▝` in the cell's top-right corner |106106 | Macro recording | `REC` beside the mode indicator |107-| Mode | The mode indicator's word (`READY`, `ENTER`, `POINT`, `MENU`) |107+| Mode | The mode indicator's word (`READY`, `ENTER`, `POINT`, `MENU`, and on a notebook `NOTEBOOK`, `EDIT` or `OUTPUT`) |108108 | Search matches, traced cells | Reverse video, with a count or the list on the context line |109109 | The copied range | A dashed underline |110110 | Dropdowns, checkboxes, active filters | `▾`, or a chip in reverse video between `▐` and `▌`; `[ ]` and `[✓]`; `▼` instead of `▾` |
M docs/files/README.md +2 -0
M docs/files/following.md +6 -5
M docs/files/format.md +53 -27
@@ -113,9 +113,9 @@ save), so they raise no version:
113113 | `conditionalFormats`, `validations` | a sheet | [Rules](#conditional-formats-and-data-validation), one per line |114114 | `arithmetic` | the workbook | `decimal` for [decimal arithmetic](../formulas/decimal.md) |115115 | `locale` | the workbook | The [locale](../sheets/locale.md) it's typed and shown in (`"de-DE"`), when File > Settings > Locale chose one; without it, the file follows the reader's `locale` setting. Cells are stored the same way in every locale: `input` is always as typed in en-US (`1,234.5`, `9/26/2026`, `=ROUND(A1,2)`) |116-| `macros`, `macroOrigin` | the workbook | Macros as Starlark scripts, and the computer they were made or trusted on (macros', shell regions' and [linked files'](following.md#files-from-elsewhere) alike): see [Macro scripting API](../reference/macro-api.md#in-the-file). Opening a file never runs them |117-| `notebook`, `regions` | a sheet | A [notebook sheet](../nushell/notebooks.md), and its shell regions and [linked files](following.md): see [Regions](#regions). Older builds open the sheet without them |118-| `shellHistory` | the workbook | The lines typed at its [notebook prompt](../nushell/notebooks.md#the-prompt), oldest first, for Up and Down |116+| `macros`, `macroOrigin` | the workbook | Macros as Starlark scripts, and the computer they were made or trusted on (macros', notebook cells' and [linked files'](following.md#files-from-elsewhere) alike): see [Macro scripting API](../reference/macro-api.md#in-the-file). Opening a file never runs them |117+| `regions` | a sheet | Its [linked files](following.md) and notebook cells' [outputs sent there](../nushell/notebooks.md#send-to-a-sheet): see [Regions](#regions). Older builds open the sheet without them |118+| `tab`, `reactive`, `notebookCells` | a sheet | A [notebook](../nushell/notebooks.md) tab and its cells: see [Notebooks](#notebooks). Older builds open it as an empty sheet |119119 120120 ## Column and row formats121121
@@ -188,39 +188,65 @@ with its data.
188188 189189 ## Regions190190 191-A sheet's regions, a [notebook's](../nushell/notebooks.md)192-shell regions and [linked files](following.md), are a `regions` list193-after its cells, charts and rules, one per line, in the order they were194-made; a notebook sheet also has `"notebook": true`. The file keeps what195-each region reads, never its table: reopening shows shell regions as not196-run, and nothing runs until you run it; linked files are read again.191+A sheet's regions, [linked files](following.md) and notebook cells'192+[outputs sent there](../nushell/notebooks.md#send-to-a-sheet), are a193+`regions` list after its cells, charts and rules, one per line, in the194+order they were made. The file keeps where each region is and what feeds195+it, never its table: opening the file reads linked files again and sends196+each output from its cell's saved output.197197 198198 ```json199- "notebook": true,200199 "regions": [201- {"name":"r1","command":"ls","at":"A1","rows":12,"cols":4},202- {"name":"big","command":"$r1 | where size > 1kb","at":"A16","rows":3,"cols":4,"reads":["r1"],"sort":[{"column":3,"desc":true}]},200+ {"name":"big","at":"A1","output":true},203201 {"name":"app","at":"F1","path":"logs/app.csv","window":500}204202 ]205203 ```206204 207-- `name` is what commands read it as (`$r1`) and formulas name it by208- (`nu.r1`); `command` is a shell region's nushell pipeline.209-- `at` is its label line's cell; `rows` and `cols` the size of its table210- when saved, header row included, which it keeps until it runs.211-- `reads` are the regions its command reads, `input` the range it read212- as `$in` (`Sheet1!A1:C9`), and `sort` the columns its table is sorted213- by, counting from 1 at its first column, with `desc` for Z to A.214-- A linked file has `path` instead of a command: the file, relative to215- the workbook's folder when it's saved there or below, and otherwise as216- it was linked; `at` is its table's first cell. `format` names the217- file's format (`CSV`, `TSV`, `JSON`, `NUON`, `XLSX`, `SQLite`,218- `Parquet`) when the extension doesn't tell it, `table` or `query` say219- what to read from a SQLite database, and `window` keeps the last rows220- under the header.205+- `name` is what formulas name it by (`nu.big`, `nu.app`) and notebook206+ cells read a linked file as (`$app`); an output's is its cell's.207+- `at` is its table's first cell, the header row's.208+- `output` marks a notebook cell's output.209+- A linked file has `path`: the file, relative to the workbook's folder210+ when it's saved there or below, and otherwise as it was linked.211+ `format` names the file's format (`CSV`, `TSV`, `JSON`, `NUON`,212+ `XLSX`, `SQLite`, `Parquet`) when the extension doesn't tell it,213+ `table` or `query` say what to read from a SQLite database, and214+ `window` keeps the last rows under the header.221215 222216 The table's cells are saved only for their formatting and notes, as a223-spill's are.217+spill's are. A region with a `command`, which the notebook sheets of218+earlier versions wrote (with `input`, the range it read as `$in`, and219+`rows`, `cols`, `reads` and `sort`, which opening it doesn't need),220+opens as a code cell of the workbook's notebook, its table sent where it221+was ([Notebook sheets](../nushell/notebooks.md#notebook-sheets)).222+223+## Notebooks224+225+A notebook tab is a sheet with `"tab": "notebook"`, `"reactive": true`226+when it's [reactive](../nushell/notebooks.md#stale-outputs-and-reactive-notebooks),227+and its cells in `notebookCells`, one per line:228+229+```json230+ "tab": "notebook",231+ "notebookCells": [232+ {"kind":"note","source":"# Files"},233+ {"source":"files = ls","output":"[[name, size]; [go.mod, 812b]]"},234+ {"source":"$files | nope","error":"Command `nope` not found","detail":"help: ..."},235+ {"source":"ls **/*","unsaved":true}236+ ]237+```238+239+- `kind` is `note` for Markdown, left out for code; `source` is what the240+ cell holds, a code cell's name and all, as `files` above.241+- `output` is what the cell's last run printed, as NUON text, kept when242+ it fits the `nu-save-cell-kb` and `nu-save-notebook-kb` caps243+ ([Configuration](../reference/config.md#nushell-notebooks)); `unsaved`244+ marks one left out for its size.245+- `error` and `detail` are a failed run's message and help line, `note`246+ what an output left out.247+248+Run counts, timings and stale marks aren't saved: an output read from249+the file is what its cell's source and inputs gave.224250 225251 ## Conditional formats and data validation226252
A docs/files/git.md +152 -0
@@ -0,0 +1,152 @@
1+---2+title: "Diff and merge in git"3+sidebar_position: 74+---5+6+# Diff and merge in git7+8+`012 diff` compares two workbooks cell by cell, and `012 merge-driver`9+merges them, so sheets kept in git are reviewed and merged like code.10+11+## 012 diff12+13+```sh14+012 diff budget.012 budget-v2.01215+```16+17+```18+sheet Q4 renamed from Q319+sheet Notes added20+Q4!B7 input =SUM(A1:A5) → =SUM(A1:A6)21+Q4!B7 value 15 → 2122+Q4!C2 value 15 → 2123+Q4!D9 input - 4224+Q4!A12 input + Total25+Q4!B3 format currency, decimals 2 → currency, bold, decimals 226+Q4!B3 note check → checked27+Q4 widths C 10 → 1428+Notebook cell 2 source ls → ls -a29+name Sales range Q3!B2:B20 → Q4!B2:B2130+```31+32+A line says where, what about it changed, and `old → new`, `+ new` for33+something added or `- old` for something removed. On a terminal (and in34+git's pager) the old side is red and the new green; `--color never` or35+`NO_COLOR` turns that off, `--color always` on.36+37+What it compares:38+39+| Change | Lines |40+|---|---|41+| Sheets | `added`, `removed`, `renamed from`, `moved from 3 to 1`. A sheet under a new name that holds at least half the cells of one gone is that sheet renamed |42+| Cells | `input` (what was typed: a formula or an entry), `value` (what a formula computes, or an array spilled), `format` (the cell's formatting, by its fields in the file), `note` |43+| Notebooks | Each [notebook](../nushell/notebooks.md) cell, lined up by its source: `added`, `removed`, or which field changed (`source`, `output`, `error`) |44+| Regions | Each [linked file](following.md) or notebook output sent to a sheet, by name: added, removed, or which field changed |45+| Layout | Every other field of a sheet, by its name in [the file](format.md): `widths`, `heights` and `lines` key by key, the rest (`charts`, `conditionalFormats`, `freeze`) whole |46+| Workbook | Named ranges, macros, the locale and decimal arithmetic |47+48+A value is shown only for formulas and cells an array filled, since a49+typed entry's value is its input. Values that change on every50+recalculation (`NOW()`, `RAND()` and what reads them, on any sheet)51+aren't compared, and neither is anything that needs a notebook cell or52+JEV run: `012 diff` only reads the two files, whose notebook outputs are53+compared as they were saved. The sheet shown, the54+file's version, the notebook prompt's history and which computer55+trusted its commands aren't changes either.56+57+`--format json` and `--format nuon` write the changes as a table with58+the columns `kind`, `sheet`, `item`, `field`, `old` and `new`, values59+with their types:60+61+```nu62+012 diff a.012 b.012 --format nuon | from nuon | where kind == cell and field == value63+```64+65+The exit status is diff(1)'s: 0 when the workbooks are the same, 1 when66+they differ, 2 on trouble (a file that can't be read).67+68+## Git69+70+Tell git which files are workbooks, in `.gitattributes` at the top of71+the repository (committed, so everyone's clone knows):72+73+```74+*.012 diff=012 merge=01275+```76+77+and what `012` means for them, in each clone or in your global git78+config (git doesn't take commands from a repository):79+80+```sh81+git config diff.012.command '012 diff'82+git config diff.012.textconv '012 diff --textconv'83+git config merge.012.name '012 cell by cell'84+git config merge.012.driver '012 merge-driver %O %A %B %P'85+```86+87+- **`diff.012.command`** makes `git diff`, `git show` and `git log -p`88+ show 012's diff for each workbook, under a `diff --012 a/budget.01289+ b/budget.012` header. Git calls it with its own seven arguments, which90+ `012 diff` recognizes; it exits 0 then, as git expects.91+- **`diff.012.textconv`** is for what doesn't use the external diff:92+ `git diff --no-ext-diff`, `git log -p --no-ext-diff`, `git blame`,93+ `git grep` and hosting sites that honor textconv. `012 diff --textconv94+ file` writes the workbook as one line per cell and field, each naming95+ its sheet (`Q3!B7 =SUM(A1:A6) = 21 [currency, decimals 2]`), and git96+ diffs those lines. Either can be set alone.97+- **`merge.012.driver`** merges workbooks cell by cell, below.98+99+## Merging100+101+`012 merge-driver base ours theirs [path]` merges theirs into ours from102+their common base and writes the result over ours, which is what git103+asks of a merge driver (`%O %A %B %P`). Each part of a workbook merges104+on its own:105+106+```mermaid107+flowchart TD108+ part["A part: a cell's input, format or note; a sheet's name; a column's width; a region; a notebook's cells; a named range"]109+ part --> same{"Did both sides change it?"}110+ same -- "One side, or neither" --> take["Take the side that changed it"]111+ same -- "Both, alike" --> take112+ same -- "Both, differently" --> conflict["Conflict: keep ours, note what theirs had"]113+```114+115+So two people editing different cells of one sheet, or one formatting a116+cell while the other retypes it, merge without conflicts. Sheets are117+matched by name, or by their cells when renamed (as `012 diff` matches118+them): a sheet renamed on one side and edited on the other merges both.119+A sheet removed on one side goes, unless the other side changed it,120+which is a conflict: ours stays (kept when ours changed it, gone when121+ours removed it). Sheets added on both sides under one name merge cell122+by cell. A notebook's cells are one part, their sources compared: the123+side that changed them brings its cells and their outputs, and a124+notebook run on both sides with the same cells keeps ours' outputs.125+126+On conflicts the merge keeps ours for each, adds to the cell's note what127+theirs had (`Merge conflict, kept ours; theirs had input 11`), so the128+cells show the note's mark in the grid, lists them on standard error and129+exits 1:130+131+```132+012 merge-driver: 1 conflict in budget.012, where ours is kept; a cell's note says what theirs had:133+ Sheet1!B2 input: ours 12, theirs 11, base 10134+```135+136+Git then reports the file as conflicted. Open it in 012, fix the cells137+and delete the notes, then `git add` it; or take one side whole with138+`git checkout --ours` or `--theirs`. A merge that can't be made at all139+(a side that isn't a workbook, or a result the engine refuses, such as a140+named range left on a sheet the other side removed) leaves ours as it141+was and exits 2, which git also takes as a conflict.142+143+Two limits:144+145+- Inserting or deleting rows or columns moves every cell below or to146+ the right, which a merge by address can't tell from edits to those147+ cells. Merge such a change in 012 instead: open both versions and copy148+ across.149+- Trust in a file's commands follows [macros' rule](../sheets/macros.md#macros-from-other-computers):150+ the merged file keeps ours' trust only when its macros, notebook cells151+ and linked files are ours', or theirs were trusted on the same152+ computer. Otherwise the next time the file runs one, 012 asks first.
A docs/files/scripts.md +153 -0
@@ -0,0 +1,153 @@
1+---2+title: "Scripts"3+sidebar_position: 64+---5+6+# Scripts7+8+Four commands read and change a `.012` workbook without the screen, for9+shell scripts, cron jobs, Makefiles and nushell pipelines:10+11+```sh12+012 get budget.012 B7 # a cell's value, as the sheet shows it13+012 get budget.012 'Q3 plan'!A1:C9 --format csv14+012 set budget.012 B7 '=SUM(A1:A6)' B8 1200 # type entries, then save15+012 recalc budget.012 # recalculate, save, list the errors16+012 export budget.012 budget.xlsx # what File > Download writes17+```18+19+None of them runs a program or reaches the network unless a flag asks:20+see [Notebooks and JEV functions](#notebooks-and-jev-functions). The flags21+and exit statuses of every command are in22+[Command line](../reference/command-line.md).23+24+## References25+26+A reference names cells as a formula does, with the sheet in front when27+it isn't the one shown when the file was saved:28+29+| Reference | Names |30+|---|---|31+| `B7`, `A1:C9`, `A:A`, `$B$7` | Cells of the sheet shown when the file was saved |32+| `Q3!B7`, `'Q3 plan'!A1:C9` | Cells of the sheet named, quoted as in formulas when the name has spaces (the shell needs quotes around it too) |33+| `Sales` | A [named range](../formulas/references.md) |34+| `Q3`, `Q3!` | The whole sheet, from A1 to its last cell with contents |35+36+A reference that names nothing says what the workbook has: `no sheet37+named "Q4" (sheets: Q1, Q2, Q3)`.38+39+## get40+41+`012 get file ref` writes the value of a cell, or a table of a range or42+sheet (the sheet shown when there's no reference). `--format` picks the43+form:44+45+| Format | One cell | A range or sheet |46+|---|---|---|47+| `text`, the default | As the cell shows it: `$1,200.00`, `9/29/2026`, `#DIV/0!` | Aligned columns, numbers to the right |48+| `csv`, `tsv` | One line | Rows as shown, in the workbook's [locale](../sheets/locale.md), as a CSV download |49+| `json` | The value: a number, a string, `true`, `null` for a blank cell | A list of records named by the range's first row |50+| `nuon` | The value with its nushell type: a date, a file size, a duration | A nushell table named by the range's first row |51+52+JSON and NUON type values by their cells' formats as a53+[NUON download](../nushell/types.md) does, and take the columns' names54+from the range's first row, as a table in 012 has its header there.55+`--no-header` names the columns by their letters instead (`A`, `B`) and56+makes the first row a record too. A filter doesn't hide rows from `get`:57+a script reads all the range holds.58+59+`--input` writes what was typed rather than what it computes: a60+formula, or an entry as the file stores it (numbers and dates in61+en-US's form whatever the workbook's locale), so62+`012 get budget.012 B7 --input` prints `=SUM(A1:A6)`.63+64+In nushell, NUON comes back as a table with its types:65+66+```nu67+012 get budget.012 A1:C9 --format nuon | from nuon | where amount > 10068+012 get budget.012 B7 --format nuon | from nuon # a number, not a string69+```70+71+## set72+73+`012 set file ref input [ref input ...]` types each input into its cell,74+in order, as one change, and saves the file atomically: into a temporary75+file beside it, renamed over it once complete. An empty input clears the76+cell's contents, keeping its formatting and note, as Delete does. A file77+that doesn't exist is created, as `012 budget.012` does.78+79+Inputs are what the file stores, not what the sheet's locale types: `1.5`80+and `=ROUND(A1, 2)` in every workbook, so a script means the same in a81+de-DE workbook as in an en-US one. Entries that imply a format (`$1,200`,82+`12%`, `2026-09-29`) set it, as typing them does.83+84+`set` stops, and leaves the file as it was, on the first input a cell85+can't take, naming the cell:86+87+| Refused | Says |88+|---|---|89+| A formula that doesn't parse | `Sheet1!B7: Expected , or ) in SUM, at character 11 of =SUM(A1:A6` |90+| An entry a validation rule rejects | the rule's help, as the context line would |91+| A cell of an array's result, a pivot table, a linked file or a notebook's output | the same message as on the screen |92+| A cell in a [protected range](../sheets/notes-protection.md) | `Sheet1!B7 is protected (B1:B9): --force sets it anyway` |93+| A reference to a range, not one cell | `A1:B2 is the range A1:B2: name one cell` |94+95+An entry a rule only marks invalid is set, with a warning on standard96+error. An input that starts with `--` (a flag's shape) comes after `--`:97+`012 set budget.012 -- A1 --`.98+99+## recalc100+101+`012 recalc file` recomputes every formula, saves the file (leaving it102+untouched when nothing it stores changed) and lists each formula showing103+an error, one a line, with what the screen's context line says about it:104+105+```106+Q3!B7 #DIV/0! Division by zero in B6/C6107+Q3!D2 #N/A JEV functions need --jev and an API key to be answered108+```109+110+It exits 1 when any cell shows an error or a formula reads its own cell,111+so `012 recalc budget.012 && deploy` stops on a broken sheet.112+113+## export114+115+`012 export file out [ref]` writes the workbook in another format, as116+File > Download does: the format by `out`'s extension or `--format`117+(`csv`, `tsv`, `xlsx`, `sqlite`, `json`, `nuon`). XLSX gets every sheet;118+the others get the sheet shown, or `ref`'s range or sheet. SQLite writes119+a table named by `--table`, replacing one of that name. Formulas are120+written as their values where the format has no formulas, and a note on121+standard error says how many. CSV, TSV, JSON and NUON leave out the rows122+a filter hides, as a download does.123+124+## Notebooks and JEV functions125+126+A `.012` file stores what was typed and computes the rest, so a few127+things need more than the file:128+129+- **[Notebook](../nushell/notebooks.md) outputs** are kept in the file130+ as their cells last left them, and these commands read them as saved,131+ sent to sheets as on the screen; `get` notes on standard error when132+ the sheet it reads has an output the file doesn't hold (never run, or133+ too large to save). `--notebooks` runs every notebook's134+ code cells first, each after the cells it reads, as F9 does; a cell135+ that fails stops the rest of its notebook, and is reported. A cell136+ reading `$selection` fails, having no selection to read.137+- As on the screen, a notebook138+ [saved on another computer](../nushell/notebooks.md#saving-and-trust)139+ asks first: `--notebooks` refuses it unless `--trust` gives the answer140+ Run would, and `recalc --notebooks --trust` saves that trust in the141+ file as the screen does, so later runs don't need it. `shell = off` in142+ the [configuration](../reference/config.md#shell) turns `--notebooks`143+ off, `shell = on` runs every file's cells, each cell stops after144+ `nu-timeout`, and saving keeps as much of the outputs as145+ `nu-save-cell-kb` and `nu-save-notebook-kb` allow.146+- **[Linked files](following.md)** are followed only on the screen; their147+ regions are empty here.148+- **[JEV functions](../formulas/jev.md)** ask a hosted model over the149+ network, and show `#N/A` here unless `--jev` asks it, with the API key150+ from the environment, the credential store or `jev-api-key-command`.151+152+`get`, `recalc` and `export` take these flags; `set` computes values only153+to check entries.
M docs/getting-started/install.md +8 -1
M docs/media/follow-log.gif +0 -0

Binary file.

M docs/media/notebook.gif +0 -0

Binary file.

M docs/media/nu-recipe.gif +0 -0

Binary file.

M docs/nushell/README.md +21 -11
@@ -9,8 +9,8 @@ sidebar_position: 1
99 tables: `ls`, `ps`, `open data.csv` and `http get` all hand the next1010 command rows and columns, with file sizes, durations and dates as values1111 of their own type. 012 is a spreadsheet for those tables. A table can go12-through 012 in the middle of a pipeline, and nushell can run inside 012,13-each pipeline's table a live part of the sheet.12+through 012 in the middle of a pipeline, and nushell can run inside 01213+in a notebook, its cells' outputs live parts of the sheets.1414 1515 ![In nushell, ls's table goes through 012, three of its rows are selected and sent on, and nu keeps filtering them by size](../media/pipeline.gif)1616
@@ -19,13 +19,23 @@ each pipeline's table a live part of the sheet.
1919 | Way | Looks like | For |2020 |---|---|---|2121 | [A stage in a pipeline](pipelines.md) | `ls \| sheet \| where size > 1kb` | Looking at a table on the terminal, editing it, and sending it on to the next command |22-| [A notebook](notebooks.md) | `012 nu`, then `ls \| select name size` at the `nu❯` prompt | Pipelines you keep: each one's table is a named region of the sheet that formulas, charts and other pipelines read, run again when you ask |22+| [A notebook](notebooks.md) | `sheet nu`, then `ls \| select name size` in a code cell named `files` | Pipelines you keep, as in Jupyter: cells run when you ask, each output under its cell, read by later cells as `$files` and, sent to a sheet, by formulas and charts |2323 | [A followed file](../files/following.md) | Data > Linked file, then `$app \| where status >= 500` | A log or export that keeps growing: its rows come in as they're written, and pipelines read the rows it has now |2424 25+```mermaid26+flowchart TD27+ pipe["a nushell pipeline: ls | sheet"] --> stage["012 as a stage: edit the table"]28+ stage -->|what you send back| next["the next command"]29+ nb["a notebook: sheet nu"] -->|code cells run nu| out["outputs under their cells"]30+ out -->|G| region["a live region on a sheet: nu.name"]31+ file["a file that grows"] -->|Data > Linked file| linked["a linked region: nu.app"]32+ linked -->|$app| nb33+```34+2535 The first needs nothing but nushell. The other two run `nu` from inside2636 012, so it has to be installed ([Installing27-nushell](https://www.nushell.sh/book/installation.html)); without it the28-prompt says so, and the rest of 012 works as before.37+nushell](https://www.nushell.sh/book/installation.html)); without it a38+cell's output says so, and the rest of 012 works as before.2939 3040 ## Install the `sheet` command3141
@@ -67,27 +77,27 @@ shells run it as `012`.
6777 # A table in, edited, and out again, with its types6878 ls | select name size | sheet | where size > 1kb6979 70-# A notebook: each pipeline's table lands in the grid80+# A notebook of nushell cells, their outputs under them7181 sheet nu work.0127282 ```7383 74-At the notebook's `nu❯` prompt:84+In the notebook's cells, each run with Shift+Enter:7585 7686 ```nu7787 sales = open sales.csv | update Revenue { str replace -ar '[$,]' '' | into float }7888 $sales | group-by Region --to-table | update items { get Revenue | math sum }7989 ```8090 81-The second region reads the first as `$sales`; typing a new command for82-`sales` runs it and then the region that reads it. In a formula on any83-sheet, `=SUM(nu.sales)` reads the same table.91+The second cell reads the first's output as `$sales`; running `sales`92+again marks the second `stale` until it runs too. `G` sends an output to93+a sheet, where `=SUM(nu.sales)` reads it and follows each run.8494 8595 ## Pages8696 8797 | Page | For |8898 |---|---|8999 | [Pipelines](pipelines.md) | `sheet`, `012 -` and `012 --pipe`: tables in on standard input and out on standard output, sending back, the exit status |90-| [Notebooks](notebooks.md) | `012 nu` and the `nu❯` prompt: regions, `$r1` and `$in`, `nu.r1` in formulas, refreshing, saving and trust |100+| [Notebooks](notebooks.md) | `sheet nu` and `012 nu`: code and note cells, their keys, running, `$name`, `$selection` and `$sheet.A1:C9`, outputs sent to sheets as `nu.name`, saving and trust |91101 | [Types](types.md) | How each nushell type becomes a cell and goes back, and what doesn't survive the trip |92102 | [Cookbook](cookbook.md) | Worked examples: disk usage, a log followed live, a CSV cleaned and summarized, an API, processes, git history |93103
M docs/nushell/cookbook.md +53 -26
@@ -5,31 +5,37 @@ sidebar_position: 5
55 66 # Cookbook77 8-Short worked examples. Pipelines that end in `^012` run in a nushell9-session; lines that start with a region's name and `=` are typed at a10-[notebook's](notebooks.md) `nu❯` prompt (`012 nu`, or **Data > Shell** in any workbook). Nushell's own8+Short worked examples. Pipelines that end in `sheet` or `sheet view`9+run in a nushell session, with 012's module installed ([Install the10+`sheet` command](README.md#install-the-sheet-command)); lines that start11+with a name and `=` are code cells of a [notebook](notebooks.md)12+(`sheet nu`, or **Data > Shell > Open notebook** in any workbook), one13+cell a line, each run with Shift+Enter. Nushell's own1114 [cookbook](https://www.nushell.sh/cookbook/) has more pipelines to start1215 from.1316 1417 ## A CSV cleaned, then summarized1518 1619 Money written as text (`"$2,252.50"`) becomes numbers in nushell, and a17-second region totals it by region, with a chart that follows it.20+second cell totals it by region, sent to a sheet with a chart that21+follows it.1822 19-![Nushell turns a CSV's money text into numbers, a second region totals it by region, and a column chart of the totals follows the table](../media/nu-recipe.gif)23+![Nushell turns a CSV's money text into numbers, a second cell totals it by region, and its output, sent to a sheet, feeds a column chart](../media/nu-recipe.gif)2024 2125 ```nu2226 sales = open sales.csv | update Revenue { str replace -ar '[$,]' '' | into float }2327 totals = $sales | group-by Region --to-table | update items { get Revenue | math sum } | rename Region Revenue2428 ```2529 26-To chart it, select the totals table, its header row and rows, and27-**Insert > Chart**. Change `sales`'s command (F2 on its label) to read28-another file, and `totals` runs again after it and the chart redraws.30+To chart it, `G` on `totals` sends its output to a new sheet; there,31+**Insert > Chart** charts the table around the pointer. Edit `sales` to32+read another file and run it: `totals` shows `stale` until it runs too33+(at once in a [reactive notebook](notebooks.md#stale-outputs-and-reactive-notebooks)),34+and then the sheet and the chart follow.2935 30-To pivot rather than total, select the `sales` table and31-**Data > Pivot table** ([Pivot tables](../sheets/pivots.md)): Region in32-rows, Quarter in columns, Revenue in values.36+To pivot rather than total, send `sales` to a sheet and **Data > Pivot37+table** there ([Pivot tables](../sheets/pivots.md)): Region in rows,38+Quarter in columns, Revenue in values.3339 3440 Nushell:3541 [`update`](https://www.nushell.sh/commands/docs/update.html),
@@ -42,20 +48,20 @@ Nushell:
4248 4349 A JSON lines log (`app.jsonl`) linked with **Data > Linked file > Link a4450 table** takes in rows as they're written ([Following45-files](../files/following.md)). A region reads it as `$app`:51+files](../files/following.md)). A notebook's cells read it as `$app`:4652 47-![A JSON lines log grows in a linked region; a shell region keeps its errors, and refreshing it later picks up the errors written since](../media/follow-log.gif)53+![A JSON lines log grows in a linked region; a notebook cell keeps its errors, and running it again later picks up the errors written since](../media/follow-log.gif)4854 4955 ```nu5056 errors = $app | where status >= 500 | select time path status ms5157 by_path = $errors | group-by path --to-table | update items { length } | rename path errors5258 ```5359 54-The linked region grows by itself; Enter on `errors`'s label reads the55-rows the log has now, and `by_path` follows. A log in CSV or TSV works56-the same; a plain text log needs a line of nushell to become a table,57-such as [`parse`](https://www.nushell.sh/commands/docs/parse.html) in a58-region's command:60+The linked region grows by itself; running `errors` again (Ctrl+Enter)61+reads the rows the log has now, and a reactive notebook runs `by_path`62+after it. A log in CSV or TSV works the same; a plain text log needs a63+line of nushell to become a table, such as64+[`parse`](https://www.nushell.sh/commands/docs/parse.html) in a cell:5965 6066 ```nu6167 lines = open --raw app.log | lines | parse "{time} {level} {message}"
@@ -67,23 +73,24 @@ lines = open --raw app.log | lines | parse "{time} {level} {message}"
6773 usage = du * | select path physical | update path { path basename } | sort-by physical --reverse6874 ```6975 70-Sizes arrive in the Size format ([Types](types.md)), so a chart's axis71-and `=SUM(nu.usage)` read them as bytes. Select the table and72-**Insert > Chart** for a column chart; Enter on the label runs `du`73-again.76+Sizes arrive in the Size format ([Types](types.md)), so once `G` sends77+the output to a sheet, a chart's axis and `=SUM(nu.usage)` read them as78+bytes: **Insert > Chart** there for a column chart. Ctrl+Enter on the79+cell runs `du` again, and the sheet follows.7480 7581 Nushell: [`du`](https://www.nushell.sh/commands/docs/du.html).7682 7783 ## An API response as a table7884 7985 ```nu80-http get https://api.github.com/repos/nushell/nushell/issues | select number title user.login comments created_at | update created_at { into datetime } | to nuon | ^012 -86+http get https://api.github.com/repos/nushell/nushell/issues | select number title user.login comments created_at | update created_at { into datetime } | sheet view8187 ```8288 8389 Sort by comments, filter by author, or make a pivot table of issues by8490 author, in the sheet. In a notebook, the same pipeline without85-`| to nuon | ^012 -` is a region that fetches the list again when86-refreshed; `nu-timeout` stops a request that hangs.91+`| sheet view` is a cell that fetches the list again each time it runs;92+Enter on its output opens it full-screen to sort and filter, and93+`nu-timeout` stops a request that hangs.8794 8895 Nushell: [`http get`](https://www.nushell.sh/commands/docs/http_get.html),8996 [`into datetime`](https://www.nushell.sh/commands/docs/into_datetime.html),
@@ -93,7 +100,7 @@ page.
93100 ## Processes, sorted, filtered and sent back94101 95102 ```nu96-ps | where cpu > 1 | sort-by cpu --reverse | select pid name cpu mem | to nuon | ^012 --pipe | from nuon | get pid | each {|pid| kill $pid }103+ps | where cpu > 1 | sort-by cpu --reverse | select pid name cpu mem | sheet | get pid | each {|pid| kill $pid }97104 ```98105 99106 In 012, sort the list and filter it down to the processes to stop
@@ -115,3 +122,23 @@ authors = $commits | group-by author --to-table | update items { length } | rena
115122 works on it in the sheet. The pattern is the one nushell's cookbook116123 explains in [Parsing git117124 log](https://www.nushell.sh/cookbook/parsing_git_log.html).125+126+## A workbook read and written from a script127+128+[`012 get` and `012 set`](../files/scripts.md) work on a workbook file129+without the screen, so a nushell script can read a range with its types,130+compute, and write results back:131+132+```nu133+let sales = ^012 get budget.012 'Q3!A1:C40' --format nuon | from nuon134+let total = $sales | where region == West | get amount | math sum135+^012 set budget.012 'Summary!B2' ($total | into string) 'Summary!B3' (date now | format date '%Y-%m-%d')136+^012 recalc budget.012137+```138+139+`012 recalc` exits 1 when a formula shows an error, so a script stops140+there. What changed between two versions comes back as a table too:141+142+```nu143+^012 diff last-week.012 budget.012 --format nuon | from nuon | where field == value144+```
M docs/nushell/notebooks.md +296 -134
@@ -5,177 +5,339 @@ sidebar_position: 3
55 66 # Notebooks77 8-A notebook is a sheet where you type nushell pipelines and each one's9-table lands in the grid, named, typed and kept live. Written like a10-REPL, kept like a spreadsheet.8+A notebook is a tab of the workbook, beside its sheets, that holds cells9+rather than a grid: code cells of nushell, each with its output under it,10+and note cells of Markdown. You run the cells you choose, in the order11+you choose, as in Jupyter; any output can go to a sheet, where formulas,12+charts and pivots read it and it follows the cell's next run.1113 1214 ```nu13-sales = open sales.csv | where Region in [North South] | select Region Quarter Units14-$sales | where Units > 250 | sort-by Units --reverse15+files = ls | where type == file16+$files | where size > 1kb | sort-by size --reverse1517 ```1618 17-![Two pipelines at the nu❯ prompt become regions, the second reading the first as $sales; giving sales a new command runs both again](../media/notebook.gif)19+![A notebook: a cell reads a CSV as sales, a second reads it as $sales, its output is sent to a sheet and summed there, and with the notebook reactive, editing sales runs both again and the sum follows](../media/notebook.gif)1820 19-`012 nu` opens one, at its prompt:21+Open one from a shell with `012 nu`, or from nushell with `sheet nu`22+(`sheet` comes from 012's nushell module: [Install the `sheet`23+command](README.md#install-the-sheet-command)):2024 2125 ```sh22-012 nu # a new notebook23-012 nu work.012 # open one, or make it26+012 nu # a new notebook, its first cell ready to type in27+012 nu work.012 # the workbook's notebook, made if it has none2428 ```2529 26-In any workbook, **Data > Shell** (or the palette, or `!` on a notebook27-sheet) opens the same prompt, writing into the workbook's notebook28-sheet, or a new one named Shell 1 at the first command. On other sheets29-`!` starts an entry, as typing does. Commands run `nu` as a separate30-process, so [nushell](https://www.nushell.sh/book/installation.html)31-must be installed; without it, the prompt says so and the rest of 01230+In any workbook, **Data > Shell > Open notebook** (or the palette)31+shows the workbook's notebook, or adds one after the sheet shown. A32+notebook's tab is marked `❯`. Cells run `nu` as a separate process, so33+[nushell](https://www.nushell.sh/book/installation.html) must be34+installed; without it, a cell's output says so and the rest of 0123235 works as before.3336 34-## The prompt37+## Cells3538 36-The formula bar becomes `nu❯`, and the mode indicator says `NU`:39+A cell is its head, its source and, for a code cell, its output:3740 38-| Key | Does |39-|---|---|40-| Enter | Run the line; the prompt stays open for the next |41-| Tab, Shift+Tab | Complete the word at the caret: a region (`$r1`) or one of nu's commands, from the box under the context line; again for the next |42-| Up, Down | The workbook's earlier lines, those starting with what's typed; the last 100 are saved with the file |43-| Esc | Stop the command running; otherwise, back to the grid |44-45-A name and `=` before the pipeline name the region; without one it's46-`r1`, `r2` and so on:41+```42+ [2] big ✓ <1s43+│ big = $files | where size > 1kb44+│ | sort-by size --reverse45+ name size46+ README.md 9.8 kB47+ 012 4.2 kB48+```4749 48-```nu49-sizes = ls | select name size50+The head says how many runs came before this one (`[2]`, `[*]` while it51+runs, `[ ]` before it has), the cell's name, and at the right how its run52+stands, `saved` for an output read from the file:53+54+```mermaid55+stateDiagram-v256+ notrun: not run57+ waiting: waiting58+ running: running59+ done: ✓ and how long it took60+ failed: failed, and nu's message under it61+ stale: stale62+ [*] --> notrun63+ notrun --> waiting: Shift+Enter, Ctrl+Enter, F964+ waiting --> running: the cell before it ends65+ running --> done66+ running --> failed: an error, or ii67+ done --> stale: a cell it reads runs again, or its source changes68+ stale --> waiting: run again69+ failed --> waiting: run again70+ done --> waiting: run again5071 ```5172 52-A name is letters, digits and `_`, not starting with a digit, as a53-nushell [variable's](https://www.nushell.sh/book/variables.html) is;54-`in`, `env`, `nu` and `it` are nushell's own. Typing a name that exists55-gives that region a new command and runs it again, as does F2 on its56-label.57-58-## Regions59-60-Each command's table is a region: a label line with its name and61-command, dimmed and underlined as wide as the table, then the table,62-its first row the column names. Regions stack down the sheet in the63-order they ran, a row between them; when a table grows or shrinks, the64-rows below it move, as if you'd inserted or deleted rows.65-66-A region is still cells of the grid. Its values are67-[typed](types.md) (file sizes in the Size format, durations, dates),68-formulas read them, and you can format them, filter them69-(**Data > Create a filter**), sort them (the sort is kept, so the table70-stays sorted when it runs again), chart them and make pivot tables of71-them: from any of its cells, or its label, with nothing selected, those72-commands take the region's table, from its header row down. Go to73-(Ctrl+G) takes a region's name as formulas write it (`nu.r1`). Like an74-[array's spill](../formulas/arrays.md#spilled-cells), they can't be75-typed over: change the command, or freeze the region. Copies paste76-values; downloads and XLSX hold the values; undo takes back a run (the77-table it replaced comes back) or the region itself.78-79-The context line says what's under the pointer: on a label, the keys80-that refresh and edit it, and why it failed if it did; on its table,81-which region the cell is part of. A label's end says what it's doing:82-`Running…`, `waiting` for a region it reads, `failed`, `stopped`,83-`not run`, or `no rows`.84-85-**Data > Shell regions** acts on the region under the pointer:86-87-| Command | Key | Does |88-|---|---|---|89-| Refresh region | Enter on its label | Runs its command again, then every region that reads it |90-| Run all regions | F9 | Runs every region, each after the regions it reads |91-| Edit region's command | F2 on its label | Opens its command at the prompt, to change it and run it again |92-| Freeze region | | Turns its table into plain values you can edit, and stops running its command |93-| Delete region | | Removes the region, its command and its table |94-| Stop shell command | Esc | Stops the command running, and those waiting after it |73+A source is shown whole: a line too long for the screen wraps before a74+pipe where it can, its next rows indented.75+76+Outputs are drawn by what they are, with values formatted as cells77+([Types](types.md)):9578 96-## Tables between regions79+| Output | Shows |80+|---|---|81+| A table (a list of records) | Its columns fitted to their values, numbers right-aligned, its first 10 rows and how many more there are |82+| A record | Its fields, one a line: `key value` |83+| A list | Its items, numbered from 0, as nushell numbers them |84+| Text | Its lines, wrapped |85+| A value | As a cell shows it: `4.2 kB`, `9/27/2026 11:27:31` |86+| An error | `×` and nushell's message, then its help line |87+88+`o` shows a long output whole, and again its first rows. **Enter** on an89+output opens it full-screen: arrows (or `h` `j` `k` `l`) move, `s` sorts90+by the pointer's column and `S` in descending order (again for the91+output's own order), `/` keeps the rows holding what's typed, and Esc92+goes back. The output itself doesn't change.93+94+Note cells are Markdown: headings, **bold**, *italic*, `code`, links (the95+terminal opens them), lists and quotes.96+97+## Keys98+99+Like Jupyter, a notebook has two modes. In command mode (the mode100+indicator says `NOTEBOOK`) keys act on cells; in edit mode (`EDIT`) they101+type into the selected cell.102+103+```mermaid104+stateDiagram-v2105+ [*] --> Command106+ Command --> Edit: Enter, !107+ Edit --> Command: Esc108+ Edit --> Command: Shift+Enter or Ctrl+Enter runs109+ Command --> Output: Enter on an output110+ Output --> Command: Esc111+```97112 98-`$r1` in a command is region r1's table, and `$in` is the range99-selected when the prompt was opened, on a sheet that isn't the notebook,100-read again each time the region runs:113+| Key | In command mode |114+|---|---|115+| Up, Down, `j`, `k` | Move between cells, stopping at each output |116+| Home, End, PgUp, PgDn | The first cell, the last, a screen up or down |117+| Enter | Edit the cell; on an output, open it full-screen |118+| Shift+Enter | Run the cell and select the next, adding one at the end |119+| Ctrl+Enter, `r` | Run the cell, staying on it |120+| Alt+Enter | Run the cell and add a code cell under it |121+| F9 | Run every cell |122+| `a`, `b` | Add a code cell above, below |123+| `!` | Add a code cell below and edit it |124+| `dd` | Delete the cell |125+| `z` | Undo, bringing back a deleted cell (as Ctrl+Z) |126+| `m`, `y` | Make the cell a note (Markdown), or code |127+| `c`, `x`, `v` | Copy, cut, paste a cell below |128+| `n` | Name the cell |129+| `o` | Show the output whole, or its first rows |130+| `G` | Send the output to a sheet |131+| `ii` | Stop: kill the cell running, and forget those waiting |132+| `00` | Restart: stop, clear every output, count runs from 1 |133+134+| Key | In edit mode |135+|---|---|136+| Esc | Keep what's typed and go back to command mode |137+| Shift+Enter, Ctrl+Enter, Alt+Enter | Run, as in command mode |138+| Enter | A new line, as indented as the one before |139+| Up, Down, Home, End | Move by the lines on screen, wrapped lines too |140+| Ctrl+A, Ctrl+E | The start, the end of the line |141+| Tab | Complete the word at the caret: a cell's `$name`, `$selection`, a linked file, then what nu completes there (commands, flags, paths); one completion goes in at once ([Writing a cell](#writing-a-cell)) |142+143+A terminal without the kitty keyboard protocol sends Shift+Enter and144+Ctrl+Enter as Enter ([Keys the terminal has to tell145+apart](../reference/keys.md#keys-the-terminal-has-to-tell-apart)): there146+Esc then `r` runs the cell, and Alt+Enter runs it and adds one under it.147+148+Every action is a command, in **Data > Shell**, the palette and the149+shortcuts (Ctrl+/), and File, Edit and the sheet tabs work as anywhere;150+commands for a sheet's cells are off on a notebook's tab. Changes to151+cells are undo steps, as any edit is.152+153+## Writing a cell154+155+The cell being written is read by nu itself, as nushell's own prompt156+reads what you type: once typing pauses, 012 asks `nu --ide-ast` what157+each word is and colors it (commands, strings, variables, numbers,158+keywords, operators; flags stay plain), and `nu --ide-check` what's159+wrong. A problem is underlined with a curly line, and with the caret on160+it the context line says what nu said:101161 102-```nu103-big = $r1 | where size > 1kb | sort-by size --reverse104-$in | group-by region --to-table105162 ```163+ [ ] not run164+│ $files | sort-by size --revrse165+ ~~~~~~~~166+The `sort-by` command doesn't have flag `revrse`.167+```168+169+Tab asks `nu --ide-complete` too, after the notebook's own names, so it170+completes flags, subcommands and paths as well as cells and commands.171+nu doesn't know the variables a notebook binds, so it's asked about the172+cell with `$name` for each named cell and linked file, `$selection` and173+`$sheet` declared before it; a `$sheet.A1:C9` range reads as one174+variable, and `name =` isn't part of what nu reads.175+176+Questions to nu run in the background and never on each key: one177+process for each question after a pause, at most two at a time, each178+stopped once the text changes and after a second and a half. Until nu179+answers, the cell shows 012's own highlighting, and what's unchanged180+keeps nu's colors, so nothing flickers or moves. 012 falls back to its181+own highlighting and names, without saying so, when:182+183+- nu isn't installed, or is older than 0.100;184+- nu timed out three times in a row (asked again in the next session);185+- the cells couldn't run without asking: a file from another computer186+ not yet trusted ([Saving and trust](#saving-and-trust)), `shell =187+ off`, or [012 serve](../terminal/ssh.md#notebooks) without188+ `serve-shell`.189+190+nu reads the cell without your `config.nu` or its standard library, so191+your own commands aren't known to it; they still run when the cell does192+with `nu-config`.106193 107-Both reach nu as NUON in a file it reads, never as text spliced into108-the command: `$r1` is a nushell variable holding a table, and `$in` is109-the pipeline's input, as nushell's own110-[`$in`](https://www.nushell.sh/book/pipelines.html#pipeline-input-and-the-special-in-variable)111-is.194+## Running195+196+Cells run in the background, one at a time, so the screen stays live:197+the one running shows `[*]` and those after it `waiting`. **Data >198+Shell** runs one cell, every cell (F9), the cells above the selected199+one or it and those below; each runs after the cells it reads. A cell200+that fails stops the rest.112201 113-A region reading another depends on it. Refreshing a region runs it114-again and then every region that reads it, directly or through others,115-each after what it reads; a region it reads that hasn't run yet runs116-first. A command reading itself, directly or through others, is117-refused.202+Each cell runs as `nu --no-config-file -c`, so your aliases and custom203+commands aren't there unless `nu-config` is on204+([nushell's configuration](https://www.nushell.sh/book/configuration.html)).205+A cell stops after `nu-timeout` (30 seconds unless set); `ii` stops it206+sooner.118207 119-### Linked files208+## Names and $name120209 121-A [linked file](../files/following.md) is a region too, named after the122-file ([The region](../files/following.md#the-region)), so commands read123-`app.csv` as `$app`. A region reading it doesn't run as rows124-arrive: refreshing it runs its command on the rows the file has now.125-The linked file itself has no command to run.210+A cell that starts `name =` gives its output that name; `n` names a211+cell for you. Later cells read the output as `$name`, a nushell212+variable holding the value, and formulas on any sheet as `nu.name` once213+it's [sent to a sheet](#send-to-a-sheet):126214 127215 ```nu128-errors = $app | where status >= 500 | select time path status ms216+files = ls217+big = $files | where size > 1kb218+$big | get name | str join ", "129219 ```130220 131-[A log file followed live](cookbook.md#a-log-file-followed-live) shows132-it working.133-134-### In formulas221+A name is letters, digits and `_`, not starting with a digit or `__`;222+`in`, `env`, `nu`, `it`, `selection` and `sheet` are taken. Two cells223+can't share a name: the second says so and doesn't run until it's224+renamed.225+226+What a cell reads decides the order cells run in: each after the cells227+it reads, and a cell that hasn't run first when one reading it runs. A228+cell reading itself, directly or through others, doesn't run.229+230+```mermaid231+flowchart TD232+ files["files = ls"] -->|$files| big["big = $files | where size > 1kb"]233+ app["a linked file, app.csv"] -->|$app| errors["errors = $app | where status >= 500"]234+ big -->|G sends it to a sheet| region["the region big, on a sheet"]235+ region --> sum["=SUM(nu.big)"]236+```135237 136-Other sheets read a region by name with `nu.` in front, as a named range137-of its table, header included: `=SUM(nu.big)`, `=VLOOKUP("go.mod",138-nu.r1, 3, FALSE)`, `=COUNTIF(nu.app, 503)`. Formulas follow the table as139-it grows, shrinks or moves, and show `#REF!` while it has none.238+A cell reads the sheets two ways, each a table:140239 141-## Running240+| In a cell | Is |241+|---|---|242+| `$selection` | The range selected on the sheet shown last before the notebook, with its first row as the header |243+| `$sheet.A1:C9`, `$sheet.Sales!A1:C9`, `$sheet.'Q1 data'!B2:B40` | That range, of the sheet shown last or the sheet named |244+| `$app` | A [linked file](../files/following.md) named `app`, its rows as they are when the cell runs |245+246+They reach nu as NUON in a file it reads, never as text spliced into247+the pipeline, so types survive ([Types](types.md)): sizes stay sizes,248+dates stay dates.249+250+### Stale outputs and reactive notebooks251+252+Running a cell again makes the outputs of the cells that read it,253+directly or through others, `stale`: they were made from what it printed254+before. So is a cell's own output once its source is changed.255+256+```mermaid257+flowchart TD258+ run["files runs again"] --> stale["big, and cells reading big: stale"]259+ stale -->|reactive off| you["run them when you want"]260+ stale -->|reactive on| again["they run again, each after what it reads"]261+ you --> fresh["fresh outputs"]262+ again --> fresh263+```142264 143-Commands run in the background, one at a time: the screen stays live,144-and Esc stops the one running and those waiting after it. Each runs as145-`nu --no-config-file -c`, so your aliases and custom commands aren't146-there unless `nu-config` is on147-([nushell's configuration](https://www.nushell.sh/book/configuration.html)).148-A command stops after `nu-timeout` (30 seconds unless set), and keeps at149-most `max-cells` cells, whole rows, saying how many it left out.265+**Data > Shell > Reactive notebook** (off unless turned on, saved266+with the notebook) runs them again whenever a cell they read runs.267+268+## Send to a sheet269+270+`G` sends the selected cell's output to a sheet: a new sheet named after271+the cell, or a cell of a sheet you pick. There it's a region named after272+the cell, italic as a linked file's rows are, that formulas read as273+`nu.name`, header row included (`=SUM(nu.big)`, `=VLOOKUP("go.mod",274+nu.files, 3, FALSE)`), and charts and pivot tables use. A cell without a275+name is given one first.276+277+```mermaid278+sequenceDiagram279+ participant C as Cell big280+ participant R as Region big on a sheet281+ participant F as =SUM(nu.big)282+ C->>R: G sends the output's rows283+ R->>F: recalculates284+ C->>C: runs again285+ C->>R: the new rows replace the old286+ R->>F: recalculates287+```150288 151-What a command prints is read as a table: a record is one row, a list152-of values that aren't records one column named `value`, and a single153-value that column with one row. When a command fails, its label says154-so and the context line gives nushell's message.289+Like an [array's spill](../formulas/arrays.md#spilled-cells), its cells290+can't be typed over: **Data > Shell > Freeze output** turns them into291+plain values, and **Remove output** takes them off the sheet. Undo takes292+back sending it, and the rows come back from the cell's output whenever293+undo brings the region back.155294 156295 ## Saving and trust157296 158-The file keeps each region's command, name, place and what it reads,159-not its table ([format](../files/format.md#regions)), and the lines160-typed at the prompt. Opening a notebook never runs anything: its161-regions say `not run`, and F9 runs them all, each after what it reads.162-163-Like [macros](../sheets/macros.md#macros-from-other-computers), a164-file's commands run as you, with your files, so a notebook saved on165-another computer asks once before they run:166-167-```168-Run this file's shell commands? Enter Run Esc Cancel297+The file keeps the cells and their outputs as NUON298+([format](../files/format.md#notebooks)), so opening it shows every299+output without running anything; the heads say `saved`. An output larger300+than `nu-save-cell-kb` (1 MB), or past `nu-save-notebook-kb` (8 MB) for301+all of them, is left out: the cell shows `not saved; run to see`, and302+saving says which.303+304+Opening a file never runs its cells. Like305+[macros](../sheets/macros.md#macros-from-other-computers), a file's cells306+run as you, with your files, so a notebook saved on another computer307+asks once before its cells run:308+309+```mermaid310+sequenceDiagram311+ participant You312+ participant N as The notebook313+ participant Nu as nu314+ You->>N: open a file saved on another computer315+ N-->>You: its cells and saved outputs, nothing run316+ You->>N: Shift+Enter, or F9317+ N-->>You: Run this file's notebook cells?318+ You->>N: Enter319+ N->>Nu: the cells, one at a time320+ Nu-->>N: their outputs169321 ```170322 171-What you type at the prompt runs without asking. The options, in172-[Configuration](../reference/config.md#nushell-notebooks):323+Cells you write in a notebook made here run without asking. The options,324+in [Configuration](../reference/config.md#nushell-notebooks):173325 174326 | Option | Default | Does |175327 |---|---|---|176-| `shell` | `ask` | `ask` as above; `on` never asks; `off` runs nothing |177-| `nu-timeout` | `30s` | Stops a command that runs longer; `0` lets it run until Esc |178-| `nu-config` | `false` | Runs commands with your `config.nu` and `env.nu` |179-180-[012 serve](../terminal/ssh.md#notebooks) shows notebooks but runs no181-commands unless the server's `serve-shell` is on.328+| `shell` | `ask` | `ask` as above; `on` never asks; `off` runs no cells |329+| `nu-timeout` | `30s` | Stops a cell that runs longer; `0` lets it run until `ii` |330+| `nu-config` | `false` | Runs cells with your `config.nu` and `env.nu` |331+| `nu-save-cell-kb`, `nu-save-notebook-kb` | `1024`, `8192` | How much of the outputs a file keeps |332+333+[012 serve](../terminal/ssh.md#notebooks) shows notebooks and their334+saved outputs, but runs no cells unless the server's `serve-shell` is on.335+336+### Notebook sheets337+338+A file with the notebook sheets of earlier versions of 012, whose339+pipelines were typed at a prompt on the formula bar, opens with each of340+those pipelines as a code cell of the workbook's notebook, named as the341+region was. Their tables stay where they were on the sheet, as the342+cells' outputs sent there, so formulas reading `nu.r1` keep working once343+the cells run; a note on opening says which sheets were converted.
M docs/nushell/pipelines.md +17 -0
M docs/nushell/types.md +1 -1
M docs/reference/README.md +1 -0
A docs/reference/command-line.md +63 -0
@@ -0,0 +1,63 @@
1+---2+title: "Command line"3+sidebar_position: 64+---5+6+# Command line7+8+Every way to start 012, with the page that describes each. A file named9+like a command (`get`, `diff`) opens as `./get`.10+11+## With the screen12+13+| Command | Does |14+|---|---|15+| `012 [flags] [file]` | Opens a `.012` workbook (created on saving when it doesn't exist), or imports another format: [Files](../files/README.md) |16+| `012 [flags] -` | A table from standard input: [Pipelines](../nushell/pipelines.md) |17+| `012 [flags] --pipe [--to nuon\|json\|csv\|tsv] [--send ask\|selection\|sheet] [file]` | On quitting, sends a table to standard output: [Pipelines](../nushell/pipelines.md) |18+| `012 nu [flags] [file]` | A nushell notebook, at its prompt: [Notebooks](../nushell/notebooks.md) |19+| `012 serve [flags] [dir]` | Serves the sheets in a directory over SSH: [Serving over SSH](../terminal/ssh.md) |20+21+The flags are the options' flags, listed in [Configuration](config.md#options).22+23+## Without the screen24+25+| Command | Does |26+|---|---|27+| `012 get file.012 [ref]` | Writes a cell's value, or a range or sheet as a table: [Scripts](../files/scripts.md#get) |28+| `012 set file.012 ref input [ref input ...]` | Types entries into cells and saves: [Scripts](../files/scripts.md#set) |29+| `012 recalc file.012` | Recalculates, saves and lists the cells showing errors: [Scripts](../files/scripts.md#recalc) |30+| `012 export file.012 out [ref]` | Writes the workbook, a sheet or a range in another format: [Scripts](../files/scripts.md#export) |31+| `012 diff a.012 b.012` | Lists what changed, cell by cell: [Diff and merge in git](../files/git.md#012-diff) |32+| `012 diff --textconv file.012` | Writes a workbook as lines, for git's textconv: [Diff and merge in git](../files/git.md#git) |33+| `012 merge-driver base ours theirs [path]` | Merges theirs into ours, for git: [Diff and merge in git](../files/git.md#merging) |34+| `012 nu --module`, `012 nu --install-module [--force] [path]` | Prints or installs the nushell module: [Nushell](../nushell/README.md#install-the-sheet-command) |35+| `012 config [path\|edit\|default\|themes\|set-key\|delete-key]` | The settings: [Configuration](config.md#commands) |36+| `012 version` | Prints the version |37+38+`get`, `set`, `recalc`, `export`, `diff` and `merge-driver` take their39+flags anywhere after the command's name, and `--help` prints their40+usage. `--` ends the flags, so what follows is taken as it is41+(`012 set book.012 -- A1 --`), and an argument such as `-5` is never a42+flag.43+44+| Flag | Commands | Does |45+|---|---|---|46+| `--format text\|csv\|tsv\|json\|nuon` | `get` | The form of what's written; text by default |47+| `--input` | `get` | What was typed (a formula) rather than its value |48+| `--no-header` | `get` | JSON and NUON columns named by their letters, the first row a record |49+| `--force` | `set` | Sets cells in protected ranges |50+| `--format kind`, `--table name` | `export` | The format when the file's extension doesn't say; SQLite's table |51+| `--notebooks` | `get`, `recalc`, `export` | Runs the notebooks' cells first, when the file is trusted here |52+| `--trust` | with `--notebooks` | Runs the cells of a file saved on another computer |53+| `--jev` | `get`, `recalc`, `export` | Asks JEV for the JEV functions' answers first |54+| `--format text\|json\|nuon` | `diff` | The form of the changes; text by default |55+| `--color auto\|always\|never` | `diff` | Colors the text; auto colors on a terminal or git's pager unless `NO_COLOR` is set |56+57+## Exit status58+59+| Status | Means |60+|---|---|61+| 0 | Done; for `diff`, the workbooks are the same |62+| 1 | An error, said on standard error; for `recalc`, cells show errors (after saving); for `diff`, the workbooks differ; for `merge-driver`, conflicts; for `get`, `recalc` and `export` with `--notebooks`, a notebook cell failed (after writing) |63+| 2 | The command was used wrongly (its usage is printed); for `diff` and `merge-driver`, trouble reading or merging the files |
M docs/reference/config.md +28 -4
@@ -77,6 +77,8 @@ start.
7777 | [`shell`](#shell) | `ask` | `O12_SHELL` |7878 | [`nu-timeout`](#nu-timeout) | `30s` | `O12_NU_TIMEOUT` |7979 | [`nu-config`](#nu-config) | `false` | `O12_NU_CONFIG` |80+| [`nu-save-cell-kb`](#nu-save-cell-kb) | `1024` | `O12_NU_SAVE_CELL_KB` |81+| [`nu-save-notebook-kb`](#nu-save-notebook-kb) | `8192` | `O12_NU_SAVE_NOTEBOOK_KB` |8082 | [`jev-api-key-command`](#jev-api-key-command) | | |8183 | [`jev-credential-store`](#jev-credential-store) | `true` | `O12_JEV_CREDENTIAL_STORE` |8284 | [`jev-base-url`](#jev-base-url) | | `TYPESAFE_BASE_URL` |
@@ -168,7 +170,7 @@ The most cells an import keeps, and a paste or fill writes at once. Numbers and
168170 169171 #### `shell`170172 171-Whether notebook sheets run nushell commands (docs/nushell/notebooks.md). `ask` runs what you type and asks once before running the commands of a file made on another computer; `on` never asks; `off` runs none. Opening a file never runs its commands.173+Whether notebooks run their code cells (docs/nushell/notebooks.md). `ask` runs what you write and asks once before running the cells of a file made on another computer; `on` never asks; `off` runs none. Opening a file never runs its cells.172174 173175 | | |174176 |---|---|
@@ -179,7 +181,7 @@ Whether notebook sheets run nushell commands (docs/nushell/notebooks.md). `ask`
179181 180182 #### `nu-timeout`181183 182-Stop a notebook's command that runs longer than this; 0 lets it run until Esc stops it.184+Stop a notebook cell that runs longer than this; 0 lets it run until Stop (i i) stops it.183185 184186 | | |185187 |---|---|
@@ -190,7 +192,7 @@ Stop a notebook's command that runs longer than this; 0 lets it run until Esc st
190192 191193 #### `nu-config`192194 193-Run notebook commands with your nushell config files (config.nu, env.nu) rather than `nu --no-config-file`, for your own commands and aliases.195+Run notebook cells with your nushell config files (config.nu, env.nu) rather than `nu --no-config-file`, for your own commands and aliases.194196 195197 | | |196198 |---|---|
@@ -199,6 +201,28 @@ Run notebook commands with your nushell config files (config.nu, env.nu) rather
199201 | Environment | `O12_NU_CONFIG` |200202 | Applies | File > Settings > Reload config |201203 204+#### `nu-save-cell-kb`205+206+The largest output of one cell a saved file keeps, in kilobytes of NUON; a larger one is left out and shows `not saved; run to see` when the file is opened.207+208+| | |209+|---|---|210+| Type | number |211+| Default | `1024` |212+| Environment | `O12_NU_SAVE_CELL_KB` |213+| Applies | File > Settings > Reload config |214+215+#### `nu-save-notebook-kb`216+217+How much of all its cells' outputs a saved file keeps, in kilobytes of NUON: the outputs that fit, in the notebook's order.218+219+| | |220+|---|---|221+| Type | number |222+| Default | `8192` |223+| Environment | `O12_NU_SAVE_NOTEBOOK_KB` |224+| Applies | File > Settings > Reload config |225+202226 ### JEV functions203227 204228 #### `jev-api-key-command`
@@ -335,7 +359,7 @@ How many 012 serve sessions may run at once; more are turned away.
335359 336360 #### `serve-shell`337361 338-Let 012 serve sessions run notebooks' nushell commands, as the user 012 serve runs as, following the shell option. Off, served notebooks show their regions but run nothing.362+Let 012 serve sessions run notebooks' code cells, as the user 012 serve runs as, following the shell option. Off, served notebooks show their cells and saved outputs but run nothing.339363 340364 | | |341365 |---|---|
M docs/reference/keys.md +35 -6
@@ -35,6 +35,9 @@ mouse and the vim keymap added.
3535 | Ctrl+Enter | Enter the same entry in every selected cell |3636 | Arrows, Shift+arrows | While typing a formula, after an operator: point at a cell, or a range |3737 | F4 | While typing a formula: cycle the reference at the caret through `A1`, `$A$1`, `A$1`, `$A1` |38+| Ctrl+Left, Ctrl+Right (Option+arrows on macOS; Alt+arrows outside a cell) | While editing any text: move by word; in ENTER mode Ctrl+arrows commit and jump, as arrows do |39+| Ctrl+Backspace, Option+Backspace, Ctrl+W; Alt+D, Ctrl+Delete | While editing any text: delete the word before the caret; the word after it |40+| Ctrl+U, Cmd+Backspace | While editing any text: delete to the start of the line |3841 | Up, Down; Tab, Enter | While suggestions for a function, range or sheet name show: pick one; insert it (a sheet as `Summary!`, then arrows point into it). Esc hides them |3942 4043 ## Selecting
@@ -128,13 +131,38 @@ range, F2 renames or repoints it and Ctrl+D deletes it.
128131 129132 ## Notebooks130133 131-| Key | Action |134+On a [notebook's](../nushell/notebooks.md#keys) tab, as in Jupyter:135+command mode (`NOTEBOOK`) acts on cells, edit mode (`EDIT`) types into136+one. **Data > Shell > Open notebook** opens the workbook's notebook;137+on a sheet, `!` starts an entry as any character does.138+139+| Key | In command mode |140+|---|---|141+| Up, Down, `j`, `k`, Home, End, PgUp, PgDn | Move between cells and their outputs |142+| Enter | Edit the cell; on an output, open it full-screen |143+| Shift+Enter, Ctrl+Enter, `r`, Alt+Enter | Run the cell and select the next; run it in place (`r` too); run it and add a cell under it |144+| F9 | Run every cell |145+| `a`, `b`, `!` | Add a code cell above, below; add one below and edit it |146+| `dd`, `z` | Delete the cell; undo |147+| `m`, `y` | Make the cell a note, or code |148+| `c`, `x`, `v` | Copy, cut, paste a cell |149+| `n`, `o`, `G` | Name the cell; show all of its output or its first rows; send the output to a sheet |150+| `ii`, `00` | Stop what's running; restart, clearing every output |151+152+| Key | In edit mode |153+|---|---|154+| Esc | Back to command mode, keeping what was typed |155+| Shift+Enter, Ctrl+Enter, Alt+Enter | Run, as in command mode |156+| Enter, Tab | A new line; complete the word at the caret, with what nu completes there ([Writing a cell](../nushell/notebooks.md#writing-a-cell)) |157+| Up, Down, Home, End, Ctrl+A, Ctrl+E | Move by the lines on screen; the start, the end of the line |158+| Word keys | As [anywhere text is edited](#entering-data); Ctrl+U deletes to the start of the line, not the cell |159+160+| Key | On an output full-screen |132161 |---|---|133-| ! | On a notebook sheet, open the nushell prompt on the formula bar (the mode indicator says NU); on other sheets `!` starts an entry, and **Data > Shell** opens the prompt ([Notebooks](../nushell/notebooks.md)) |134-| Enter, Tab, Up, Down, Esc | At the prompt: run the line; complete a region or command; the workbook's earlier lines; stop a command running, or else go back to the grid |135-| Enter, F2 | On a region's label line: run it again (and what reads it); edit its command at the prompt |136-| F9 | Run all regions, each after those it reads |137-| Esc | Stop a region's command while it runs |162+| Arrows, `h`, `j`, `k`, `l`, PgUp, PgDn, `g`, `G` | Move |163+| `s`, `S` | Sort by the pointer's column, A to Z or Z to A; again for the output's order |164+| `/` | Keep the rows holding what's typed; Esc clears it |165+| Esc, `q` | Back to the notebook |138166 139167 ## Keys the terminal has to tell apart140168
@@ -148,6 +176,7 @@ tells them apart and says when a key is let go, and there 012 also does:
148176 |---|---|---|149177 | Shift+Enter | Accept an entry and move up, as in Sheets | As Enter: accept and move down |150178 | Ctrl+I | Italic | As Tab: move right |179+| Shift+Enter, Ctrl+Enter | In a notebook, run the cell (and select the next) | As Enter: edit the cell, or a new line in it; Esc then `r` runs it, and Alt+Enter runs it and adds a cell under it |151180 | Space, held | On a selected chart, show it across the grid until Space is let go; in File > Settings > Theme with nothing typed, hide the list to see the whole sheet in the highlighted theme | Space does what it does there: deselects the chart and starts an entry, or types a space |152181 153182 ## Vim keys
M docs/terminal/ssh.md +10 -8
@@ -88,7 +88,7 @@ the authorized keys. Everything else is closed.
8888 others.8989 - **What a session can do.** Run 012, nothing else; no command is9090 executed, unless the server's config turns on notebooks' nushell91- commands (see [Notebooks](#notebooks)). A shell request starts 012 on a new sheet. An exec request91+ cells (see [Notebooks](#notebooks)). A shell request starts 012 on a new sheet. An exec request9292 is accepted only with a terminal (`ssh -t`) and only when it is one9393 word, which is taken as a file name and never run: it resolves inside9494 the served directory like a name typed in File > Open, and anything
@@ -152,13 +152,15 @@ network or clock access in any case.
152152 ## Notebooks153153 154154 A session opens [notebooks](../nushell/notebooks.md) and shows their155-regions, but runs none of their commands: a command is a program on the156-server, with the server's user and every file it can reach, well157-beyond the served directory. `serve-shell = on` in the server's config158-lets sessions run them, as the user 012 serve runs as, following the159-`shell` option as the local app does; a file's commands still ask once160-per session before they run. Only turn it on when everyone holding an161-authorized key may run programs on the server.155+cells and saved outputs, but runs none of their cells: a cell is a156+program on the server, with the server's user and every file it can157+reach, well beyond the served directory. `serve-shell = on` in the158+server's config lets sessions run them, as the user 012 serve runs as,159+following the `shell` option as the local app does; a file's cells still160+ask once per session before they run. Only turn it on when everyone holding an161+authorized key may run programs on the server. The same setting decides162+whether nu is started to [highlight and check](../nushell/notebooks.md#writing-a-cell)163+the cell being written; without it, 012's own highlighting answers.162164 163165 ## Unsaved work164166
M e2e/notebook_test.go +83 -26
@@ -8,8 +8,10 @@ import (
88 "testing"99 )1010 11-// Notebooks with the real nu: a region, one reading it, saving, and the12-// file reopened with its regions not run until F9.11+// Notebooks: cells written and run with the real nu, one reading12+// another's output, saved with their outputs and opened again without13+// running; an output sent to a sheet and kept up to date; the selection14+// read as $selection; and a long pipeline wrapped, never cut.1315 1416 func needNu(t *testing.T) {1517 t.Helper()
@@ -21,41 +23,96 @@ func needNu(t *testing.T) {
2123 func TestNotebookWithNu(t *testing.T) {2224 needNu(t)2325 dir := t.TempDir()24- s := startWith(t, options{dir: dir, startsOn: "nu❯"}, "nu", "book.012")25- s.keys("[[name size]; [a 2kb] [b 10b] [c 5mb]]", "<enter>")26- s.waitFor("r1: 3 rows")26+ s := startWith(t, options{dir: dir, startsOn: "EDIT"}, "nu", "book.012")27+ s.keys("sizes = [[name size]; [a 2kb] [b 10b] [c 5mb]]", "<shift+enter>")28+ s.waitFor("[1] sizes")2729 s.waitFor("5.0 MB")28- s.keys("big = $r1 | where size > 1kb | sort-by size --reverse", "<enter>")29- s.waitFor("big: 2 rows")30- s.keys("nope-not-a-command", "<enter>")31- s.waitFor("r2 failed:")32- s.keys("<esc>", "<ctrl+s>")30+ s.keys("<enter>", "$sizes | where size > 1kb | sort-by size --reverse", "<shift+enter>")31+ s.waitFor("[2]")32+ s.keys("<enter>", "nope-not-a-command", "<ctrl+enter>")33+ s.waitFor("failed")34+ s.keys("<ctrl+s>")3335 s.eventually("saved notebook", func() bool {3436 data, err := os.ReadFile(filepath.Join(dir, "book.012"))35- return err == nil && strings.Contains(string(data), `"reads":["r1"]`)37+ return err == nil && strings.Contains(string(data), `"notebookCells"`) && strings.Contains(string(data), "5000000b")3638 })3739 s.keys("<ctrl+q>")3840 s.waitExit()3941 40- s = startWith(t, options{dir: dir, startsOn: "nu❯"}, "nu", "book.012")41- s.waitFor("r1 [[name size]; [a 2kb] [b 10b] [c 5mb]] not run")42- s.keys("<esc>", "<f9>")43- s.waitFor("big $r1 | where size > 1kb | sort-by size --reverse\n")44- s.waitFor("5.0 MB")42+ s = startWith(t, options{dir: dir, startsOn: "NOTEBOOK"}, "nu", "book.012")43+ s.waitFor("5.0 MB") // the outputs, saved, show without running44+ s.waitFor("saved")45+ s.keys("<f9>")46+ s.waitFor("[1] sizes")47+}48+49+func TestNotebookSendsToSheetWithNu(t *testing.T) {50+ needNu(t)51+ s := startWith(t, options{startsOn: "EDIT"}, "nu")52+ s.keys("n = [[k]; [1] [2] [3]]", "<ctrl+enter>")53+ s.waitFor("[1] n")54+ s.keys("G", "<enter>")55+ s.waitFor("Sent n to")56+ s.keys("<ctrl+pgdown>", "<ctrl+home>", "<right>", "<right>", "=SUM(nu.n)", "<enter>")57+ s.waitFor(" 6")58+ s.keys("<ctrl+pgup>", "<enter>", " | append {k: 10}", "<ctrl+enter>")59+ s.waitFor("[2] n")60+ s.keys("<ctrl+pgdown>")61+ s.waitFor(" 16")62+}63+64+// With the real nu, a cell being written is highlighted by nu's shapes65+// once typing pauses, and Tab completes what only nu knows: a flag.66+func TestNotebookHighlightsAndCompletesWithNu(t *testing.T) {67+ needNu(t)68+ s := startWith(t, options{startsOn: "EDIT"}, "nu")69+ s.keys("ls | where size > 1kb | sort-by size")70+ s.eventually("nu's highlighting", func() bool { return nuHighlighted(s.html(), "sort-by", "size") })71+ s.keys(" --rev", "<tab>")72+ s.waitFor("sort-by size --reverse")4573 }4674 47-// Data > Shell in an ordinary workbook makes a Shell 1 sheet, and $in is48-// the selection. (! types itself there; it opens the prompt only on a49-// notebook sheet.)50-func TestShellFromWorkbook(t *testing.T) {75+// Data > Shell from a workbook makes a Notebook tab; $selection is76+// the range selected on the sheet shown before it.77+func TestNotebookReadsSelection(t *testing.T) {5178 needNu(t)5279 s := start(t, "")5380 s.keys("n", "<enter>", "3", "<enter>", "4", "<enter>", "<up>", "<up>", "<up>", "<shift+down>", "<shift+down>", "<shift+down>")5481 s.waitFor(" A1:A4 ")55- s.keys("<ctrl+k>", "Shell")56- s.waitFor("Run a nushell pipeline")57- s.keys("<enter>", "$in | math sum", "<enter>")58- s.waitFor("r1: 1 row")59- s.waitFor("Shell 1")60- s.waitFor(" 7")82+ s.keys("<ctrl+k>", "Open notebook", "<enter>")83+ s.waitFor("NOTEBOOK")84+ s.keys("!", "$selection | get n | math sum", "<ctrl+enter>")85+ s.waitFor("[1]")86+ s.waitFor(" 7")87+ if !strings.Contains(s.screen(), "Notebook") {88+ t.Errorf("no Notebook tab:\n%s", s.screen())89+ }90+}91+92+// A 200-character pipeline at 80 columns is on the screen whole, wrapped93+// before its pipes, while it's edited and once it isn't.94+func TestNotebookLongPipeline(t *testing.T) {95+ s := startWith(t, options{cols: 80, rows: 24, startsOn: "EDIT", env: fakeNu}, "nu")96+ var b strings.Builder97+ b.WriteString("ls")98+ for i := 0; b.Len() < 200; i++ {99+ b.WriteString(" | where size > " + strings.Repeat("9", i%4+1) + "kb")100+ }101+ src := b.String()102+ s.keys(src)103+ whole := func() bool {104+ var got []string105+ for _, l := range strings.Split(s.screen(), "\n") {106+ if rest, ok := strings.CutPrefix(l, "│ "); ok {107+ got = append(got, strings.Fields(rest)...)108+ }109+ }110+ return strings.Join(got, " ") == strings.Join(strings.Fields(src), " ")111+ }112+ s.eventually("the whole pipeline, edited", whole)113+ s.keys("<esc>")114+ s.waitFor("NOTEBOOK")115+ if !whole() {116+ t.Errorf("the pipeline isn't whole after editing:\n%s", s.screen())117+ }61118 }
M e2e/screennotebook_test.go +93 -29
@@ -3,12 +3,17 @@ package e2e
33 import (44 "os"55 "path/filepath"6+ "regexp"7+ "strings"68 )79 8-// Golden screens for notebooks: two regions, the second reading the9-// first; the prompt completing a command; a command that failed. Each is10-// recorded on a light terminal and in the high-contrast theme too. nu is11-// testdata/nu/nu, which answers what the screens type with fixed tables.10+// Golden screens for notebooks: cells with a note and table outputs, a11+// cell edited with a long pipeline wrapped (at 80 and 60 columns), a12+// failure, nu's highlighting and a problem nu found, a cell running and13+// one waiting, stale outputs, an output full-screen, and an output sent14+// to a sheet. Each is recorded on a light terminal and in the15+// high-contrast theme too. nu is testdata/nu/nu, which answers what the16+// screens run, and what the code editor asks, with fixed outputs.1217 1318 // fakeNu puts the stand-in nu first on the PATH.1419 var fakeNu = func() []string {
@@ -19,39 +24,98 @@ var fakeNu = func() []string {
1924 return []string{"PATH=" + dir + string(os.PathListSeparator) + os.Getenv("PATH")}2025 }()2126 22-// twoRegions runs ls and a region reading it, then goes back to the grid.23-func twoRegions(s *session) {24- s.keys("ls", "<enter>")25- s.waitFor("r1: 5 rows")26- s.keys("big = $r1 | where size > 1kb", "<enter>")27- s.waitFor("big: 3 rows")28- s.keys("<esc>")29- s.waitFor("READY")27+// nuHighlighted reports whether the line of screen html holding mark28+// draws word twice in two styles, as nu's shapes of `where size > 1kb29+// | sort-by size` do (a column, then a string) where the tokenizer30+// leaves both plain.31+func nuHighlighted(html, mark, word string) bool {32+ for line := range strings.SplitSeq(html, "\n") {33+ if !strings.Contains(line, mark) {34+ continue35+ }36+ var styles []string37+ for _, m := range regexp.MustCompile(`<span style="([^"]*)">`+regexp.QuoteMeta(word)+`</span>`).FindAllStringSubmatch(line, -1) {38+ styles = append(styles, m[1])39+ }40+ return len(styles) == 2 && styles[0] != styles[1]41+ }42+ return false43+}44+45+// longPipeline is a pipeline too long for one line at 80 columns.46+const longPipeline = "$files | where size > 1kb | where type == file | sort-by size --reverse | select name size | first 10 | rename file bytes"47+48+// filesNotebook turns 012 nu's first cell into a note, then writes and49+// runs a cell listing files and one reading it, leaving a new cell50+// selected.51+func filesNotebook(s *session) {52+ s.keys("<esc>", "m", "<enter>", "# Files", "<enter>", "What's big in the repo, from `ls`.", "<esc>")53+ s.keys("b", "<enter>", "files = ls", "<shift+enter>")54+ s.waitFor("[1] files")55+ s.keys("<enter>", "$files | where size > 1kb", "<shift+enter>")56+ s.waitFor("[2]")57+ s.waitFor("NOTEBOOK")3058 }3159 3260 var notebookScreens = []screen{33- {name: "notebook-regions", setup: func(s *session) {34- twoRegions(s)35- s.keys("<down>", "<down>", "<right>", "<right>")36- s.waitFor("Region big")37- }},38- {name: "notebook-completion", setup: func(s *session) {39- s.keys("ls", "<enter>")40- s.waitFor("r1: 5 rows")41- s.keys("$r1 | so")42- s.waitFor("sort-by")43- }},44- {name: "notebook-failed", setup: func(s *session) {45- s.keys("ls", "<enter>")46- s.waitFor("r1: 5 rows")47- s.keys("lss -a", "<enter>")48- s.waitFor("r2 failed: Command `lss` not found")61+ {name: "notebook-cells", setup: func(s *session) {62+ filesNotebook(s)63+ s.keys("<up>", "<up>")64+ s.waitFor("reads $files")65+ }},66+ {name: "notebook-wrap", opts: options{cols: 80, rows: 24}, setup: func(s *session) {67+ filesNotebook(s)68+ s.keys("<enter>", longPipeline)69+ s.waitFor("rename file bytes")70+ }},71+ {name: "notebook-wrap-narrow", opts: options{cols: 60, rows: 24}, setup: func(s *session) {72+ filesNotebook(s)73+ s.keys("<enter>", longPipeline, "<ctrl+enter>")74+ s.waitFor("[3]")75+ }},76+ {name: "notebook-error", setup: func(s *session) {77+ filesNotebook(s)78+ s.keys("<enter>", "lss -a", "<ctrl+enter>")79+ s.waitFor("× Command `lss` not found")80+ }},81+ {name: "notebook-running", setup: func(s *session) {82+ filesNotebook(s)83+ s.keys("<enter>", "sleep 10min", "<esc>", "b", "<enter>", "{name: 1}", "<esc>", "<f9>")84+ // F9 queues every cell; the first two finish, then sleep runs.85+ s.waitFor("running 1, 1 waiting")86+ }},87+ {name: "notebook-stale", setup: func(s *session) {88+ filesNotebook(s)89+ s.keys("<up>", "<up>", "<up>", "<up>", "<enter>", " | first 4", "<esc>")90+ s.waitFor("stale")91+ }},92+ {name: "notebook-output", setup: func(s *session) {93+ filesNotebook(s)94+ s.keys("<up>", "<up>", "<up>", "<enter>")95+ s.waitFor("full-screen")96+ }},97+ {name: "notebook-nu-highlight", setup: func(s *session) {98+ filesNotebook(s)99+ s.keys("<enter>", "big = $files | where size > 1kb | sort-by size --reverse # largest")100+ s.eventually("nu's highlighting", func() bool { return nuHighlighted(s.html(), "largest", "size") })101+ }},102+ {name: "notebook-nu-error", setup: func(s *session) {103+ filesNotebook(s)104+ s.keys("<enter>", "$files | sort-by size --revrse")105+ s.waitFor("doesn't have flag `revrse`")106+ }},107+ {name: "notebook-sent", setup: func(s *session) {108+ filesNotebook(s)109+ s.keys("<up>", "<up>", "<up>", "<up>", "G", "<enter>")110+ s.waitFor("Sent files to")111+ s.keys("<ctrl+pgdown>", "<down>")112+ s.waitFor("Output of files")49113 }},50114 }51115 52116 func init() {53117 for _, sc := range notebookScreens {54- sc.args, sc.opts.startsOn, sc.opts.env = []string{"nu"}, "nu❯", fakeNu118+ sc.args, sc.opts.startsOn, sc.opts.env = []string{"nu"}, "EDIT", fakeNu55119 light, hc := sc, sc56120 light.name += "-light"57121 light.opts.light = true
A e2e/testdata/nu/ide/error.ast.json +1 -0
A e2e/testdata/nu/ide/error.check.jsonl +4 -0
A e2e/testdata/nu/ide/error.nu +4 -0
A e2e/testdata/nu/ide/highlight.ast.json +1 -0
@@ -0,0 +1 @@
1+[{"type":"ast","span":{"start":0,"end":3},"shape":"shape_internalcall","content":"let"},{"type":"ast","span":{"start":4,"end":13},"shape":"shape_vardecl","content":"selection"},{"type":"ast","span":{"start":21,"end":23},"shape":"shape_list","content":"[]"},{"type":"ast","span":{"start":24,"end":27},"shape":"shape_internalcall","content":"let"},{"type":"ast","span":{"start":28,"end":33},"shape":"shape_vardecl","content":"sheet"},{"type":"ast","span":{"start":41,"end":43},"shape":"shape_record","content":"{}"},{"type":"ast","span":{"start":44,"end":47},"shape":"shape_internalcall","content":"let"},{"type":"ast","span":{"start":48,"end":53},"shape":"shape_vardecl","content":"files"},{"type":"ast","span":{"start":61,"end":63},"shape":"shape_list","content":"[]"},{"type":"ast","span":{"start":70,"end":76},"shape":"shape_variable","content":"$files"},{"type":"ast","span":{"start":77,"end":78},"shape":"shape_pipe","content":"|"},{"type":"ast","span":{"start":79,"end":84},"shape":"shape_internalcall","content":"where"},{"type":"ast","span":{"start":85,"end":89},"shape":"shape_string","content":"size"},{"type":"ast","span":{"start":85,"end":89},"shape":"shape_variable","content":"size"},{"type":"ast","span":{"start":90,"end":91},"shape":"shape_operator","content":">"},{"type":"ast","span":{"start":92,"end":93},"shape":"shape_int","content":"1"},{"type":"ast","span":{"start":93,"end":95},"shape":"shape_string","content":"kb"},{"type":"ast","span":{"start":96,"end":97},"shape":"shape_pipe","content":"|"},{"type":"ast","span":{"start":98,"end":105},"shape":"shape_internalcall","content":"sort-by"},{"type":"ast","span":{"start":106,"end":110},"shape":"shape_string","content":"size"},{"type":"ast","span":{"start":111,"end":120},"shape":"shape_flag","content":"--reverse"}]
A e2e/testdata/nu/ide/highlight.nu +4 -0
M e2e/testdata/nu/nu +40 -11
@@ -1,21 +1,50 @@
11 #!/bin/sh2-# A stand-in for nu in the golden screens: it answers the commands the3-# screens type with fixed tables, so the screens don't depend on nu being4-# installed or on what ls finds. The script 012 runs is the last argument.2+# A stand-in for nu in the golden screens: it answers the pipelines the3+# screens run with fixed outputs, so the screens don't depend on nu being4+# installed or on what ls finds. The script 012 runs is the last5+# argument; the pipeline is between its "do {" and "} | to nuon".56 for arg; do script=$arg; done6-case $script in7+# The code editor's questions (--ide-ast and the rest, about the file8+# named last) are answered as nu 0.116 answered them about the same9+# text, recorded in ide/: name.nu, and its name.ast.json,10+# name.check.jsonl or name.complete.json. Text not recorded fails, and11+# 012 falls back to its own highlighting.12+case " $* " in13+*' --version '*)14+ echo 0.116.015+ exit 0 ;;16+*' --ide-ast '*) answer=ast.json ;;17+*' --ide-check '*) answer=check.jsonl ;;18+*' --ide-complete '*) answer=complete.json ;;19+esac20+if [ -n "$answer" ]; then21+ for nu in "$(dirname "$0")"/ide/*.nu; do22+ if cmp -s "$nu" "$script" && [ -f "${nu%.nu}.$answer" ]; then23+ cat "${nu%.nu}.$answer"24+ exit 025+ fi26+ done27+ echo "Error: not recorded" >&228+ exit 129+fi30+cmd=$(printf '%s\n' "$script" | sed -n '/^do {$/,/^} | to nuon$/p' | sed '1d;$d')31+case $cmd in732 *'help commands'*)8- echo '["ls", "where", "sort-by", "first", "get", "str join", "window"]' ;;9-*'$r1 | where size > 1kb'*)10- echo '[[name, type, size]; ["012", file, 4.2kb], [README.md, file, 9.8kb], [demos, dir, 3.1kb]]' ;;11-*'do {12-ls13-}'*)33+ echo '["ls", "where", "sort-by", "first", "get", "str join", "window", "sleep"]' ;;34+'ls')1435 echo '[[name, type, size]; ["012", file, 4.2kb], [CLAUDE.md, file, 1kb], [README.md, file, 9.8kb], [demos, dir, 3.1kb], [go.mod, file, 812b]]' ;;36+'$files | where size > 1kb'*)37+ echo '[[name, type, size]; ["012", file, 4.2kb], [README.md, file, 9.8kb], [demos, dir, 3.1kb]]' ;;38+'$files | get name | str join ", "')39+ echo '"012, CLAUDE.md, README.md, demos, go.mod"' ;;40+'{'*)41+ echo '{name: "012", files: 5, largest: README.md, total: 18.9kb}' ;;42+'sleep'*)43+ sleep 600 ;;1544 *)16- cmd=$(printf '%s\n' "$script" | sed -n '/^do {$/{n;p;q;}')1745 echo "Error: nu::parser::extra_positional" >&21846 echo "" >&21947 echo " × Command \`${cmd%% *}\` not found" >&248+ echo " help: commands are listed by \`help commands\`" >&22049 exit 1 ;;2150 esac
M e2e/testdata/screens/help-light.html +1 -1
@@ -25,6 +25,6 @@
2525 <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">21</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-8)">│</span> Deselect <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">Esc</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-8)">│</span>2626 <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">22</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-8)">│</span> Select all <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">Ctrl+A</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-8)">│</span>2727 <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">23</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-8)">│</span> Select columns <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">Ctrl+Space</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-8)">│</span>28-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">24</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-8)">└─────────────────────────────────────────────────────────</span> <span style="color:var(--vt-palette-0)">1-25</span> <span style="color:var(--vt-palette-0)">of</span> <span style="color:var(--vt-palette-0)">100</span> <span style="color:var(--vt-palette-8)">─┘</span>28+<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">24</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-8)">└─────────────────────────────────────────────────────────</span> <span style="color:var(--vt-palette-0)">1-25</span> <span style="color:var(--vt-palette-0)">of</span> <span style="color:var(--vt-palette-0)">120</span> <span style="color:var(--vt-palette-8)">─┘</span>2929 <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">25</span><span style="background:var(--vt-palette-7)"> </span>3030 <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">Up/Down</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">scroll</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">Esc</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">close</span></pre>\ No newline at end of file
M e2e/testdata/screens/help-narrow.html +1 -1
@@ -11,6 +11,6 @@
1111 <span style="color:var(--vt-palette-8)">│</span> Last used cell <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+End</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>1212 <span style="color:var(--vt-palette-8)">│</span> Go to <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+G</span><span style="background:var(--vt-palette-8)"> </span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">F5</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>1313 <span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-8)">│</span>14-<span style="color:var(--vt-palette-8)">└───────────────────────────────────────────</span> <span style="color:var(--vt-palette-8)">1-11</span> <span style="color:var(--vt-palette-8)">of</span> <span style="color:var(--vt-palette-8)">100</span> <span style="color:var(--vt-palette-8)">─┘</span>14+<span style="color:var(--vt-palette-8)">└───────────────────────────────────────────</span> <span style="color:var(--vt-palette-8)">1-11</span> <span style="color:var(--vt-palette-8)">of</span> <span style="color:var(--vt-palette-8)">120</span> <span style="color:var(--vt-palette-8)">─┘</span>1515 <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">11</span><span style="background:var(--vt-palette-8)"> </span>1616 <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Up/Down</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">scroll</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Esc</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">close</span></pre>\ No newline at end of file
M e2e/testdata/screens/help-wide.html +40 -40
@@ -1,45 +1,45 @@
11 <pre class="screen"> <span style="text-decoration:underline">F</span>ile <span style="text-decoration:underline">E</span>dit <span style="text-decoration:underline">V</span>iew <span style="text-decoration:underline">I</span>nsert F<span style="text-decoration:underline">o</span>rmat <span style="text-decoration:underline">D</span>ata <span style="text-decoration:underline">H</span>elp <span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">HELP</span><span style="background:var(--vt-palette-6)"> </span>22 <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">A1</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">┌─</span> <span style="font-weight:bold">Keyboard</span> <span style="font-weight:bold">shortcuts</span> <span style="color:var(--vt-palette-8)">───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐</span>33 <span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-8)">│</span>4-<span style="background:var(--vt-palette-8)"> </span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">A</span><span style="background:var(--vt-palette-6)"> </span><span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">B</span><span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-8)">│</span> <span style="font-weight:bold">Moving</span> <span style="font-weight:bold">around</span> <span style="font-weight:bold">Edit</span> <span style="color:var(--vt-palette-8)">│</span><span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">R</span><span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">S</span><span style="background:var(--vt-palette-8)"> </span>5-<span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">1</span><span style="background:var(--vt-palette-6)"> </span> <span style="color:var(--vt-palette-8)">│</span> Move <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Arrows</span><span style="background:var(--vt-palette-8)"> </span> Undo <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+Z</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>6-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">2</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Jump to the edge of the data <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+arrows</span><span style="background:var(--vt-palette-8)"> </span> Redo <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+Shift+Z</span><span style="background:var(--vt-palette-8)"> </span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+Y</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>7-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">3</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Right, left <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Tab</span><span style="background:var(--vt-palette-8)"> </span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Shift+Tab</span><span style="background:var(--vt-palette-8)"> </span> Cut <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+X</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>8-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">4</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Screen up, down <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">PgUp</span><span style="background:var(--vt-palette-8)"> </span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">PgDn</span><span style="background:var(--vt-palette-8)"> </span> Copy <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+C</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>9-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">5</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Screen left, right <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Alt+PgUp</span><span style="background:var(--vt-palette-8)"> </span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Alt+PgDn</span><span style="background:var(--vt-palette-8)"> </span> Paste <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+V</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>10-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">6</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Column A, cell A1 <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Home</span><span style="background:var(--vt-palette-8)"> </span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+Home</span><span style="background:var(--vt-palette-8)"> </span> Paste values only <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+Shift+V</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>11-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">7</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Last used cell <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+End</span><span style="background:var(--vt-palette-8)"> </span> Fill down <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+D</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>12-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">8</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Go to <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+G</span><span style="background:var(--vt-palette-8)"> </span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">F5</span><span style="background:var(--vt-palette-8)"> </span> Fill right <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+R</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>13-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">9</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Find <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+F</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>14-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">10</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> <span style="font-weight:bold">Sheets</span> Find and replace <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+H</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>15-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">11</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Show it; double-click renames <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Click</span><span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">a</span><span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">tab</span><span style="background:var(--vt-palette-8)"> </span> Clear <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Del</span><span style="background:var(--vt-palette-8)"> </span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Backspace</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>16-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">12</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Next sheet <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+PgDn</span><span style="background:var(--vt-palette-8)"> </span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Alt+Right</span><span style="background:var(--vt-palette-8)"> </span> Delete rows or columns <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+Alt+-</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>17-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">13</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Previous sheet <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+PgUp</span><span style="background:var(--vt-palette-8)"> </span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Alt+Left</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>18-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">14</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> New sheet <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Shift+F11</span><span style="background:var(--vt-palette-8)"> </span> <span style="font-weight:bold">Insert</span> <span style="color:var(--vt-palette-8)">│</span>19-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">15</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Go to sheet <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Alt+Shift+K</span><span style="background:var(--vt-palette-8)"> </span> Insert rows or columns <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+Alt+=</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>20-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">16</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Note <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Shift+F2</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>21-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">17</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> <span style="font-weight:bold">Selecting</span> <span style="color:var(--vt-palette-8)">│</span>22-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">18</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Extend the selection <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Shift+arrows</span><span style="background:var(--vt-palette-8)"> </span> <span style="font-weight:bold">Format</span> <span style="color:var(--vt-palette-8)">│</span>23-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">19</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Extend to the edge of the data <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+Shift+arrows</span><span style="background:var(--vt-palette-8)"> </span> Number <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+Shift+1</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>24-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">20</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Select by dragging or Shift+click <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Mouse</span><span style="background:var(--vt-palette-8)"> </span> Percent <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+Shift+5</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>25-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">21</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Deselect <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Esc</span><span style="background:var(--vt-palette-8)"> </span> Scientific <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+Shift+6</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>26-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">22</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Select all <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+A</span><span style="background:var(--vt-palette-8)"> </span> Currency <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+Shift+4</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>27-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">23</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Select columns <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+Space</span><span style="background:var(--vt-palette-8)"> </span> Date <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+Shift+3</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>28-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">24</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Select rows <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Shift+Space</span><span style="background:var(--vt-palette-8)"> </span> Time <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+Shift+2</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>29-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">25</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Bold <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+B</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>30-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">26</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> <span style="font-weight:bold">Entering</span> <span style="font-weight:bold">data</span> Italic <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+I</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>31-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">27</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Replace the cell; = starts a formula <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Type</span><span style="background:var(--vt-palette-8)"> </span> Underline <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+U</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>32-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">28</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Accept and move down, right <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Enter</span><span style="background:var(--vt-palette-8)"> </span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Tab</span><span style="background:var(--vt-palette-8)"> </span> Strikethrough <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Alt+Shift+5</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>33-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">29</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Pick cells for a formula <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Arrows</span><span style="background:var(--vt-palette-8)"> </span> Left <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+Shift+L</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>34-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">30</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Cancel the entry <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Esc</span><span style="background:var(--vt-palette-8)"> </span> Center <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+Shift+E</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>35-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">31</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Edit cell <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">F2</span><span style="background:var(--vt-palette-8)"> </span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Enter</span><span style="background:var(--vt-palette-8)"> </span> Right <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+Shift+R</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>36-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">32</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Outer borders <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Alt+Shift+7</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>37-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">33</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> <span style="font-weight:bold">Macros</span> Top border <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Alt+Shift+1</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>38-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">34</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Run the macro with that shortcut <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+Alt+Shift+0-9</span><span style="background:var(--vt-palette-8)"> </span> Bottom border <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Alt+Shift+3</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>39-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">35</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Stop a macro while it runs <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Esc</span><span style="background:var(--vt-palette-8)"> </span> Left border <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Alt+Shift+4</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>40-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">36</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Right border <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Alt+Shift+2</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>41-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">37</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> <span style="font-weight:bold">Menus</span> <span style="font-weight:bold">and</span> <span style="font-weight:bold">search</span> Clear borders <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Alt+Shift+6</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>42-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">38</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Open a menu by its underlined letter <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Alt+letter</span><span style="background:var(--vt-palette-8)"> </span> Clear formatting <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+\</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>43-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">39</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">└───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────</span> <span style="color:var(--vt-palette-8)">1-40</span> <span style="color:var(--vt-palette-8)">of</span> <span style="color:var(--vt-palette-8)">51</span> <span style="color:var(--vt-palette-8)">─┘</span>4+<span style="background:var(--vt-palette-8)"> </span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">A</span><span style="background:var(--vt-palette-6)"> </span><span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">B</span><span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-8)">│</span> <span style="font-weight:bold">Moving</span> <span style="font-weight:bold">around</span> <span style="font-weight:bold">Menus</span> <span style="font-weight:bold">and</span> <span style="font-weight:bold">search</span> <span style="color:var(--vt-palette-8)">│</span><span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">R</span><span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">S</span><span style="background:var(--vt-palette-8)"> </span>5+<span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">1</span><span style="background:var(--vt-palette-6)"> </span> <span style="color:var(--vt-palette-8)">│</span> Move <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Arrows</span><span style="background:var(--vt-palette-8)"> </span> Open a menu by its underlined letter <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Alt+letter</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>6+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">2</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Jump to the edge of the data <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+arrows</span><span style="background:var(--vt-palette-8)"> </span> Search the menus <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+K</span><span style="background:var(--vt-palette-8)"> </span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+Shift+P</span><span style="background:var(--vt-palette-8)"> </span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Alt+/</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>7+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">3</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Right, left <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Tab</span><span style="background:var(--vt-palette-8)"> </span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Shift+Tab</span><span style="background:var(--vt-palette-8)"> </span> Menu <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">F10</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>8+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">4</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Screen up, down <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">PgUp</span><span style="background:var(--vt-palette-8)"> </span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">PgDn</span><span style="background:var(--vt-palette-8)"> </span> Cell menu <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Shift+F10</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>9+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">5</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Screen left, right <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Alt+PgUp</span><span style="background:var(--vt-palette-8)"> </span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Alt+PgDn</span><span style="background:var(--vt-palette-8)"> </span> Keyboard shortcuts <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+/</span><span style="background:var(--vt-palette-8)"> </span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">F1</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>10+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">6</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Column A, cell A1 <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Home</span><span style="background:var(--vt-palette-8)"> </span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+Home</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>11+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">7</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Last used cell <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+End</span><span style="background:var(--vt-palette-8)"> </span> <span style="font-weight:bold">File</span> <span style="color:var(--vt-palette-8)">│</span>12+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">8</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Go to <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+G</span><span style="background:var(--vt-palette-8)"> </span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">F5</span><span style="background:var(--vt-palette-8)"> </span> Open <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+O</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>13+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">9</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Save <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+S</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>14+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">10</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> <span style="font-weight:bold">Sheets</span> Quit <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+Q</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>15+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">11</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Show it; double-click renames <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Click</span><span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">a</span><span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">tab</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>16+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">12</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Next sheet <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+PgDn</span><span style="background:var(--vt-palette-8)"> </span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Alt+Right</span><span style="background:var(--vt-palette-8)"> </span> <span style="font-weight:bold">Edit</span> <span style="color:var(--vt-palette-8)">│</span>17+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">13</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Previous sheet <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+PgUp</span><span style="background:var(--vt-palette-8)"> </span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Alt+Left</span><span style="background:var(--vt-palette-8)"> </span> Undo <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+Z</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>18+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">14</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> New sheet <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Shift+F11</span><span style="background:var(--vt-palette-8)"> </span> Redo <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+Shift+Z</span><span style="background:var(--vt-palette-8)"> </span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+Y</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>19+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">15</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Go to sheet <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Alt+Shift+K</span><span style="background:var(--vt-palette-8)"> </span> Cut <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+X</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>20+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">16</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Copy <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+C</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>21+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">17</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> <span style="font-weight:bold">Selecting</span> Paste <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+V</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>22+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">18</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Extend the selection <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Shift+arrows</span><span style="background:var(--vt-palette-8)"> </span> Paste values only <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+Shift+V</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>23+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">19</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Extend to the edge of the data <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+Shift+arrows</span><span style="background:var(--vt-palette-8)"> </span> Fill down <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+D</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>24+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">20</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Select by dragging or Shift+click <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Mouse</span><span style="background:var(--vt-palette-8)"> </span> Fill right <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+R</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>25+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">21</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Deselect <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Esc</span><span style="background:var(--vt-palette-8)"> </span> Find <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+F</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>26+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">22</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Select all <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+A</span><span style="background:var(--vt-palette-8)"> </span> Find and replace <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+H</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>27+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">23</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Select columns <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+Space</span><span style="background:var(--vt-palette-8)"> </span> Clear <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Del</span><span style="background:var(--vt-palette-8)"> </span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Backspace</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>28+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">24</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Select rows <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Shift+Space</span><span style="background:var(--vt-palette-8)"> </span> Delete rows or columns <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+Alt+-</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>29+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">25</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-8)">│</span>30+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">26</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> <span style="font-weight:bold">Entering</span> <span style="font-weight:bold">data</span> <span style="font-weight:bold">Insert</span> <span style="color:var(--vt-palette-8)">│</span>31+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">27</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Replace the cell; = starts a formula <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Type</span><span style="background:var(--vt-palette-8)"> </span> Insert rows or columns <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+Alt+=</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>32+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">28</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Accept and move down, right <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Enter</span><span style="background:var(--vt-palette-8)"> </span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Tab</span><span style="background:var(--vt-palette-8)"> </span> Note <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Shift+F2</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>33+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">29</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Pick cells for a formula <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Arrows</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>34+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">30</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Cancel the entry <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Esc</span><span style="background:var(--vt-palette-8)"> </span> <span style="font-weight:bold">Format</span> <span style="color:var(--vt-palette-8)">│</span>35+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">31</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Edit cell <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">F2</span><span style="background:var(--vt-palette-8)"> </span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Enter</span><span style="background:var(--vt-palette-8)"> </span> Number <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+Shift+1</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>36+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">32</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Percent <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+Shift+5</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>37+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">33</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> <span style="font-weight:bold">Macros</span> Scientific <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+Shift+6</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>38+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">34</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Run the macro with that shortcut <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+Alt+Shift+0-9</span><span style="background:var(--vt-palette-8)"> </span> Currency <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+Shift+4</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>39+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">35</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Stop a macro while it runs <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Esc</span><span style="background:var(--vt-palette-8)"> </span> Date <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+Shift+3</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>40+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">36</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Time <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+Shift+2</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>41+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">37</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> <span style="font-weight:bold">Notebooks</span> Bold <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+B</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>42+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">38</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Move between cells and outputs <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Up</span><span style="background:var(--vt-palette-8)"> </span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Down</span><span style="background:var(--vt-palette-8)"> </span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">j</span><span style="background:var(--vt-palette-8)"> </span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">k</span><span style="background:var(--vt-palette-8)"> </span> Italic <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+I</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>43+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">39</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">└───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────</span> <span style="color:var(--vt-palette-8)">1-40</span> <span style="color:var(--vt-palette-8)">of</span> <span style="color:var(--vt-palette-8)">61</span> <span style="color:var(--vt-palette-8)">─┘</span>4444 <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">40</span><span style="background:var(--vt-palette-8)"> </span>4545 <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Up/Down</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">scroll</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Esc</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">close</span></pre>\ No newline at end of file
M e2e/testdata/screens/help.html +1 -1
@@ -25,6 +25,6 @@
2525 <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">21</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Deselect <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Esc</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>2626 <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">22</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Select all <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+A</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>2727 <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">23</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Select columns <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+Space</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>28-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">24</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">└─────────────────────────────────────────────────────────</span> <span style="color:var(--vt-palette-8)">1-25</span> <span style="color:var(--vt-palette-8)">of</span> <span style="color:var(--vt-palette-8)">100</span> <span style="color:var(--vt-palette-8)">─┘</span>28+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">24</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">└─────────────────────────────────────────────────────────</span> <span style="color:var(--vt-palette-8)">1-25</span> <span style="color:var(--vt-palette-8)">of</span> <span style="color:var(--vt-palette-8)">120</span> <span style="color:var(--vt-palette-8)">─┘</span>2929 <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">25</span><span style="background:var(--vt-palette-8)"> </span>3030 <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Up/Down</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">scroll</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Esc</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">close</span></pre>\ No newline at end of file
M e2e/testdata/screens/macro-menu-light.html +5 -5
@@ -22,9 +22,9 @@
2222 <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">18</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-8)">├─────────────────────│</span> Record macro with relative references <span style="color:var(--vt-palette-8)">│</span>2323 <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">19</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-8)">│</span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6)">Macros</span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-8)">Stop</span> <span style="color:var(--vt-palette-8)">and</span> <span style="color:var(--vt-palette-8)">save</span> <span style="color:var(--vt-palette-8)">recording</span> <span style="color:var(--vt-palette-8)">│</span>2424 <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">20</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-8)">│</span> Shell <span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-8)">Discard</span> <span style="color:var(--vt-palette-8)">recording</span> <span style="color:var(--vt-palette-8)">│</span>25-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">21</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-8)">│</span> Shell regions <span style="color:var(--vt-palette-8)">├──────────────────────────────────────────┤</span>26-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">22</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-8)">│</span> Linked file <span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-8)">Run</span> <span style="color:var(--vt-palette-8)">macro</span> <span style="color:var(--vt-palette-8)">│</span>27-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">23</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-8)">├─────────────────────│</span> Manage macros <span style="color:var(--vt-palette-8)">│</span>28-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">24</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-8)">Ask</span> <span style="color:var(--vt-palette-8)">JEV</span> <span style="color:var(--vt-palette-8)">again</span> <span style="color:var(--vt-palette-8)">│</span> Write a macro <span style="color:var(--vt-palette-8)">│</span>29-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">25</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-8)">└─────────────────────└──────────────────────────────────────────┘</span>25+<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">21</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-8)">│</span> Linked file <span style="color:var(--vt-palette-8)">├──────────────────────────────────────────┤</span>26+<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">22</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-8)">├─────────────────────│</span> <span style="color:var(--vt-palette-8)">Run</span> <span style="color:var(--vt-palette-8)">macro</span> <span style="color:var(--vt-palette-8)">│</span>27+<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">23</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-8)">Ask</span> <span style="color:var(--vt-palette-8)">JEV</span> <span style="color:var(--vt-palette-8)">again</span> <span style="color:var(--vt-palette-8)">│</span> Manage macros <span style="color:var(--vt-palette-8)">│</span>28+<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">24</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-8)">└─────────────────────│</span> Write a macro <span style="color:var(--vt-palette-8)">│</span>29+<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">25</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-8)">└──────────────────────────────────────────┘</span>3030 Record what you do as a macro that selects the same ce… <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">Arrows</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">move</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">Enter</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">choose</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">Esc</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">close</span></pre>\ No newline at end of file
M e2e/testdata/screens/macro-menu.html +5 -5
@@ -22,9 +22,9 @@
2222 <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">18</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">├─────────────────────│</span> Record macro with relative references <span style="color:var(--vt-palette-8)">│</span>2323 <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">19</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6)">Macros</span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-8)">Stop</span> <span style="color:var(--vt-palette-8)">and</span> <span style="color:var(--vt-palette-8)">save</span> <span style="color:var(--vt-palette-8)">recording</span> <span style="color:var(--vt-palette-8)">│</span>2424 <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">20</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Shell <span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-8)">Discard</span> <span style="color:var(--vt-palette-8)">recording</span> <span style="color:var(--vt-palette-8)">│</span>25-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">21</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Shell regions <span style="color:var(--vt-palette-8)">├──────────────────────────────────────────┤</span>26-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">22</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Linked file <span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-8)">Run</span> <span style="color:var(--vt-palette-8)">macro</span> <span style="color:var(--vt-palette-8)">│</span>27-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">23</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">├─────────────────────│</span> Manage macros <span style="color:var(--vt-palette-8)">│</span>28-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">24</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-8)">Ask</span> <span style="color:var(--vt-palette-8)">JEV</span> <span style="color:var(--vt-palette-8)">again</span> <span style="color:var(--vt-palette-8)">│</span> Write a macro <span style="color:var(--vt-palette-8)">│</span>29-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">25</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">└─────────────────────└──────────────────────────────────────────┘</span>25+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">21</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Linked file <span style="color:var(--vt-palette-8)">├──────────────────────────────────────────┤</span>26+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">22</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">├─────────────────────│</span> <span style="color:var(--vt-palette-8)">Run</span> <span style="color:var(--vt-palette-8)">macro</span> <span style="color:var(--vt-palette-8)">│</span>27+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">23</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-8)">Ask</span> <span style="color:var(--vt-palette-8)">JEV</span> <span style="color:var(--vt-palette-8)">again</span> <span style="color:var(--vt-palette-8)">│</span> Manage macros <span style="color:var(--vt-palette-8)">│</span>28+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">24</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">└─────────────────────│</span> Write a macro <span style="color:var(--vt-palette-8)">│</span>29+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">25</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">└──────────────────────────────────────────┘</span>3030 Record what you do as a macro that selects the same ce… <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Arrows</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">move</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Enter</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">choose</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Esc</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">close</span></pre>\ No newline at end of file
A e2e/testdata/screens/notebook-cells-high-contrast.html +30 -0
@@ -0,0 +1,30 @@
1+<pre class="screen"><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">F</span><span style="color:#ffffff;background:#1f1f1f">ile</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">E</span><span style="color:#ffffff;background:#1f1f1f">dit</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">V</span><span style="color:#ffffff;background:#1f1f1f">iew</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">I</span><span style="color:#ffffff;background:#1f1f1f">nsert</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f">F</span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">o</span><span style="color:#ffffff;background:#1f1f1f">rmat</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">D</span><span style="color:#ffffff;background:#1f1f1f">ata</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">H</span><span style="color:#ffffff;background:#1f1f1f">elp</span><span style="background:#1f1f1f"> </span><span style="background:#40e0ff"> </span><span style="color:#000000;background:#40e0ff;font-weight:bold">NOTEBOOK</span><span style="background:#40e0ff"> </span>2+<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">cell</span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">3</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">reads</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">$files</span><span style="background:#000000"> </span>3+<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">Enter</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">edit</span><span style="background:#000000"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">Shift+Enter</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">run</span><span style="background:#000000"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">a</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">add</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">above</span><span style="background:#000000"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">b</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">below</span><span style="background:#000000"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">dd</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">delete</span><span style="background:#000000"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">G</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">send</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">to</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">a</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">sheet</span><span style="background:#000000"> </span>4+<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">Notebook</span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">4</span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">cells</span><span style="background:#3a3a3a"> </span>5+<span style="background:#000000"> </span><span style="color:#959595;background:#000000">Markdown</span><span style="background:#000000"> </span>6+<span style="color:#757575;background:#000000">│</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold;text-decoration:underline">Files</span><span style="background:#000000"> </span>7+<span style="color:#757575;background:#000000">│</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">What&#39;s</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">big</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">in</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">the</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">repo,</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">from</span><span style="background:#000000"> </span><span style="color:#40e0ff;background:#000000">ls</span><span style="color:#ffffff;background:#000000">.</span><span style="background:#000000"> </span>8+<span style="background:#000000"> </span>9+<span style="background:#000000"> </span><span style="color:#959595;background:#000000">[1]</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">files</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">✓</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">&lt;1s</span><span style="background:#000000"> </span>10+<span style="color:#757575;background:#000000">│</span><span style="background:#000000"> </span><span style="color:#ff80ff;background:#000000">files</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold">=</span><span style="background:#000000"> </span><span style="color:#40e0ff;background:#000000">ls</span><span style="background:#000000"> </span>11+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold;text-decoration:underline">name</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold;text-decoration:underline">type</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold;text-decoration:underline">size</span><span style="background:#000000"> </span>12+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">012</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">4.2</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>13+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">CLAUDE.md</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">1.0</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>14+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">README.md</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">9.8</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>15+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">demos</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">dir</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">3.1</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>16+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">go.mod</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">812</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">B</span><span style="background:#000000"> </span>17+<span style="background:#000000"> </span>18+<span style="background:#0033cc"> </span><span style="color:#ffffff;background:#0033cc">[2]</span><span style="background:#0033cc"> </span><span style="color:#ffffff;background:#0033cc">✓</span><span style="background:#0033cc"> </span><span style="color:#ffffff;background:#0033cc">&lt;1s</span><span style="background:#0033cc"> </span><span style="background:#000000"> </span>19+<span style="color:#757575;background:#000000">│</span><span style="background:#000000"> </span><span style="color:#ff80ff;background:#000000">$files</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold">|</span><span style="background:#000000"> </span><span style="color:#40e0ff;background:#000000">where</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">size</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold">&gt;</span><span style="background:#000000"> </span><span style="color:#ffe040;background:#000000">1kb</span><span style="background:#000000"> </span>20+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold;text-decoration:underline">name</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold;text-decoration:underline">type</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold;text-decoration:underline">size</span><span style="background:#000000"> </span>21+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">012</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">4.2</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>22+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">README.md</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">9.8</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>23+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">demos</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">dir</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">3.1</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>24+<span style="background:#000000"> </span>25+<span style="background:#000000"> </span><span style="color:#959595;background:#000000">[</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">]</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">not</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">run</span><span style="background:#000000"> </span>26+<span style="color:#757575;background:#000000">│</span><span style="background:#000000"> </span>27+<span style="background:#000000"> </span>28+<span style="background:#000000"> </span>29+<span style="background:#000000"> </span>30+<span style="background:#40e0ff"> </span><span style="color:#000000;background:#40e0ff;font-weight:bold">❯</span><span style="background:#40e0ff"> </span><span style="color:#000000;background:#40e0ff;font-weight:bold">Notebook</span><span style="background:#40e0ff"> </span><span style="background:#1f1f1f"> </span><span style="color:#959595;background:#1f1f1f">+</span><span style="background:#1f1f1f"> </span><span style="color:#757575;background:#1f1f1f">│</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f">untitled</span><span style="background:#1f1f1f"> </span><span style="color:#959595;background:#1f1f1f">modified</span><span style="background:#1f1f1f"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">Ctrl+K</span><span style="background:#3a3a3a"> </span><span style="background:#1f1f1f"> </span><span style="color:#959595;background:#1f1f1f">search</span><span style="background:#1f1f1f"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">Ctrl+/</span><span style="background:#3a3a3a"> </span><span style="background:#1f1f1f"> </span><span style="color:#959595;background:#1f1f1f">shortcuts</span><span style="background:#1f1f1f"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">F10</span><span style="background:#3a3a3a"> </span><span style="background:#1f1f1f"> </span><span style="color:#959595;background:#1f1f1f">menu</span></pre>\ No newline at end of file
A e2e/testdata/screens/notebook-cells-light.html +30 -0
@@ -0,0 +1,30 @@
1+<pre class="screen"> <span style="text-decoration:underline">F</span>ile <span style="text-decoration:underline">E</span>dit <span style="text-decoration:underline">V</span>iew <span style="text-decoration:underline">I</span>nsert F<span style="text-decoration:underline">o</span>rmat <span style="text-decoration:underline">D</span>ata <span style="text-decoration:underline">H</span>elp <span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">NOTEBOOK</span><span style="background:var(--vt-palette-6)"> </span>2+<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">cell</span><span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">3</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">reads</span> <span style="color:var(--vt-palette-0)">$files</span>3+<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">Enter</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">edit</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">Shift+Enter</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">run</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">a</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">add</span> <span style="color:var(--vt-palette-0)">above</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">b</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">below</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">dd</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">delete</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">G</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">send</span> <span style="color:var(--vt-palette-0)">to</span> <span style="color:var(--vt-palette-0)">a</span> <span style="color:var(--vt-palette-0)">sheet</span>4+ Notebook 4 cells5+ <span style="color:var(--vt-palette-0)">Markdown</span>6+<span style="color:var(--vt-palette-8)">│</span> <span style="font-weight:bold;text-decoration:underline">Files</span>7+<span style="color:var(--vt-palette-8)">│</span> What&#39;s big in the repo, from <span style="color:var(--vt-palette-4)">ls</span>.8+9+ <span style="color:var(--vt-palette-0)">[1]</span> <span style="color:var(--vt-palette-0)">files</span> <span style="color:var(--vt-palette-0)">✓</span> <span style="color:var(--vt-palette-0)">&lt;1s</span>10+<span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-5)">files</span> <span style="font-weight:bold">=</span> <span style="color:var(--vt-palette-4)">ls</span>11+ <span style="font-weight:bold;text-decoration:underline">name</span> <span style="font-weight:bold;text-decoration:underline">type</span> <span style="font-weight:bold;text-decoration:underline">size</span>12+ 012 file 4.2 kB13+ CLAUDE.md file 1.0 kB14+ README.md file 9.8 kB15+ demos dir 3.1 kB16+ go.mod file 812 B17+18+<span style="background:var(--vt-palette-4)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-4)">[2]</span><span style="background:var(--vt-palette-4)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-4)">✓</span><span style="background:var(--vt-palette-4)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-4)">&lt;1s</span><span style="background:var(--vt-palette-4)"> </span>19+<span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-5)">$files</span> <span style="font-weight:bold">|</span> <span style="color:var(--vt-palette-4)">where</span> size <span style="font-weight:bold">&gt;</span> <span style="color:var(--vt-palette-1)">1kb</span>20+ <span style="font-weight:bold;text-decoration:underline">name</span> <span style="font-weight:bold;text-decoration:underline">type</span> <span style="font-weight:bold;text-decoration:underline">size</span>21+ 012 file 4.2 kB22+ README.md file 9.8 kB23+ demos dir 3.1 kB24+25+ <span style="color:var(--vt-palette-0)">[</span> <span style="color:var(--vt-palette-0)">]</span> <span style="color:var(--vt-palette-0)">not</span> <span style="color:var(--vt-palette-0)">run</span>26+<span style="color:var(--vt-palette-8)">│</span>27+28+29+30+<span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">❯</span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">Notebook</span><span style="background:var(--vt-palette-6)"> </span> <span style="color:var(--vt-palette-0)">+</span> <span style="color:var(--vt-palette-8)">│</span> untitled <span style="color:var(--vt-palette-0)">modified</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">Ctrl+K</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">search</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">Ctrl+/</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">shortcuts</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">F10</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">menu</span></pre>\ No newline at end of file
A e2e/testdata/screens/notebook-cells.html +30 -0
@@ -0,0 +1,30 @@
1+<pre class="screen"> <span style="text-decoration:underline">F</span>ile <span style="text-decoration:underline">E</span>dit <span style="text-decoration:underline">V</span>iew <span style="text-decoration:underline">I</span>nsert F<span style="text-decoration:underline">o</span>rmat <span style="text-decoration:underline">D</span>ata <span style="text-decoration:underline">H</span>elp <span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">NOTEBOOK</span><span style="background:var(--vt-palette-6)"> </span>2+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">cell</span><span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">3</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">reads</span> <span style="color:var(--vt-palette-8)">$files</span>3+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Enter</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">edit</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Shift+Enter</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">run</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">a</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">add</span> <span style="color:var(--vt-palette-8)">above</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">b</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">below</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">dd</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">delete</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">G</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">send</span> <span style="color:var(--vt-palette-8)">to</span> <span style="color:var(--vt-palette-8)">a</span> <span style="color:var(--vt-palette-8)">sheet</span>4+ Notebook 4 cells5+ <span style="color:var(--vt-palette-8)">Markdown</span>6+<span style="color:var(--vt-palette-8)">│</span> <span style="font-weight:bold;text-decoration:underline">Files</span>7+<span style="color:var(--vt-palette-8)">│</span> What&#39;s big in the repo, from <span style="color:var(--vt-palette-6)">ls</span>.8+9+ <span style="color:var(--vt-palette-8)">[1]</span> <span style="color:var(--vt-palette-8)">files</span> <span style="color:var(--vt-palette-8)">✓</span> <span style="color:var(--vt-palette-8)">&lt;1s</span>10+<span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-5)">files</span> <span style="font-weight:bold">=</span> <span style="color:var(--vt-palette-6)">ls</span>11+ <span style="font-weight:bold;text-decoration:underline">name</span> <span style="font-weight:bold;text-decoration:underline">type</span> <span style="font-weight:bold;text-decoration:underline">size</span>12+ 012 file 4.2 kB13+ CLAUDE.md file 1.0 kB14+ README.md file 9.8 kB15+ demos dir 3.1 kB16+ go.mod file 812 B17+18+<span style="background:var(--vt-palette-4)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-4)">[2]</span><span style="background:var(--vt-palette-4)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-4)">✓</span><span style="background:var(--vt-palette-4)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-4)">&lt;1s</span><span style="background:var(--vt-palette-4)"> </span>19+<span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-5)">$files</span> <span style="font-weight:bold">|</span> <span style="color:var(--vt-palette-6)">where</span> size <span style="font-weight:bold">&gt;</span> <span style="color:var(--vt-palette-3)">1kb</span>20+ <span style="font-weight:bold;text-decoration:underline">name</span> <span style="font-weight:bold;text-decoration:underline">type</span> <span style="font-weight:bold;text-decoration:underline">size</span>21+ 012 file 4.2 kB22+ README.md file 9.8 kB23+ demos dir 3.1 kB24+25+ <span style="color:var(--vt-palette-8)">[</span> <span style="color:var(--vt-palette-8)">]</span> <span style="color:var(--vt-palette-8)">not</span> <span style="color:var(--vt-palette-8)">run</span>26+<span style="color:var(--vt-palette-8)">│</span>27+28+29+30+<span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">❯</span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">Notebook</span><span style="background:var(--vt-palette-6)"> </span> <span style="color:var(--vt-palette-8)">+</span> <span style="color:var(--vt-palette-8)">│</span> untitled <span style="color:var(--vt-palette-8)">modified</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+K</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">search</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+/</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">shortcuts</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">F10</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">menu</span></pre>\ No newline at end of file
D e2e/testdata/screens/notebook-completion-light.html +0 -30
@@ -1,30 +0,0 @@
1-<pre class="screen"> <span style="text-decoration:underline">F</span>ile <span style="text-decoration:underline">E</span>dit <span style="text-decoration:underline">V</span>iew <span style="text-decoration:underline">I</span>nsert F<span style="text-decoration:underline">o</span>rmat <span style="text-decoration:underline">D</span>ata <span style="text-decoration:underline">H</span>elp <span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">NU</span><span style="background:var(--vt-palette-6)"> </span>2-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">A1</span><span style="background:var(--vt-palette-7)"> </span> <span style="font-weight:bold">nu❯</span> $r1 | so3-<span style="color:var(--vt-palette-0)">r1:</span> <span style="color:var(--vt-palette-0)">5</span> <span style="color:var(--vt-palette-0)">rows</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">Esc</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">back</span> <span style="color:var(--vt-palette-0)">to</span> <span style="color:var(--vt-palette-0)">the</span> <span style="color:var(--vt-palette-0)">grid</span>4-<span style="background:var(--vt-palette-7)"> </span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">A</span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-8)">┌─</span> <span style="font-weight:bold">Completions</span> <span style="color:var(--vt-palette-8)">────────────────┐</span><span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">E</span><span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">F</span><span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">G</span><span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">H</span><span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">I</span><span style="background:var(--vt-palette-7)"> </span>5-<span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">1</span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6)">r1</span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6)">l</span><span style="color:var(--vt-palette-8)">│</span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6)">sort-by</span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6)">command</span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-8)">│</span>6-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">2</span><span style="background:var(--vt-palette-7)"> </span> name <span style="color:var(--vt-palette-8)">└─────────────────────</span> <span style="color:var(--vt-palette-0)">1</span> <span style="color:var(--vt-palette-0)">of</span> <span style="color:var(--vt-palette-0)">1</span> <span style="color:var(--vt-palette-8)">─┘</span>7-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">3</span><span style="background:var(--vt-palette-7)"> </span> 012 file 4.2 kB8-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">4</span><span style="background:var(--vt-palette-7)"> </span> CLAUDE.md file 1.0 kB9-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">5</span><span style="background:var(--vt-palette-7)"> </span> README.md file 9.8 kB10-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">6</span><span style="background:var(--vt-palette-7)"> </span> demos dir 3.1 kB11-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">7</span><span style="background:var(--vt-palette-7)"> </span> go.mod file 812 B12-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">8</span><span style="background:var(--vt-palette-7)"> </span>13-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">9</span><span style="background:var(--vt-palette-7)"> </span>14-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">10</span><span style="background:var(--vt-palette-7)"> </span>15-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">11</span><span style="background:var(--vt-palette-7)"> </span>16-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">12</span><span style="background:var(--vt-palette-7)"> </span>17-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">13</span><span style="background:var(--vt-palette-7)"> </span>18-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">14</span><span style="background:var(--vt-palette-7)"> </span>19-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">15</span><span style="background:var(--vt-palette-7)"> </span>20-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">16</span><span style="background:var(--vt-palette-7)"> </span>21-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">17</span><span style="background:var(--vt-palette-7)"> </span>22-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">18</span><span style="background:var(--vt-palette-7)"> </span>23-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">19</span><span style="background:var(--vt-palette-7)"> </span>24-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">20</span><span style="background:var(--vt-palette-7)"> </span>25-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">21</span><span style="background:var(--vt-palette-7)"> </span>26-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">22</span><span style="background:var(--vt-palette-7)"> </span>27-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">23</span><span style="background:var(--vt-palette-7)"> </span>28-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">24</span><span style="background:var(--vt-palette-7)"> </span>29-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">25</span><span style="background:var(--vt-palette-7)"> </span>30-command <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">Tab</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">complete</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">Ctrl+N</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">next</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">Enter</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">run</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">Esc</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">grid</span></pre>\ No newline at end of file
D e2e/testdata/screens/notebook-completion.html +0 -30
@@ -1,30 +0,0 @@
1-<pre class="screen"> <span style="text-decoration:underline">F</span>ile <span style="text-decoration:underline">E</span>dit <span style="text-decoration:underline">V</span>iew <span style="text-decoration:underline">I</span>nsert F<span style="text-decoration:underline">o</span>rmat <span style="text-decoration:underline">D</span>ata <span style="text-decoration:underline">H</span>elp <span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">NU</span><span style="background:var(--vt-palette-6)"> </span>2-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">A1</span><span style="background:var(--vt-palette-8)"> </span> <span style="font-weight:bold">nu❯</span> $r1 | so3-<span style="color:var(--vt-palette-8)">r1:</span> <span style="color:var(--vt-palette-8)">5</span> <span style="color:var(--vt-palette-8)">rows</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Esc</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">back</span> <span style="color:var(--vt-palette-8)">to</span> <span style="color:var(--vt-palette-8)">the</span> <span style="color:var(--vt-palette-8)">grid</span>4-<span style="background:var(--vt-palette-8)"> </span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">A</span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-8)">┌─</span> <span style="font-weight:bold">Completions</span> <span style="color:var(--vt-palette-8)">────────────────┐</span><span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">E</span><span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">F</span><span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">G</span><span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">H</span><span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">I</span><span style="background:var(--vt-palette-8)"> </span>5-<span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">1</span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6)">r1</span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6)">l</span><span style="color:var(--vt-palette-8)">│</span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6)">sort-by</span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6)">command</span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-8)">│</span>6-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">2</span><span style="background:var(--vt-palette-8)"> </span> name <span style="color:var(--vt-palette-8)">└─────────────────────</span> <span style="color:var(--vt-palette-8)">1</span> <span style="color:var(--vt-palette-8)">of</span> <span style="color:var(--vt-palette-8)">1</span> <span style="color:var(--vt-palette-8)">─┘</span>7-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">3</span><span style="background:var(--vt-palette-8)"> </span> 012 file 4.2 kB8-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">4</span><span style="background:var(--vt-palette-8)"> </span> CLAUDE.md file 1.0 kB9-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">5</span><span style="background:var(--vt-palette-8)"> </span> README.md file 9.8 kB10-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">6</span><span style="background:var(--vt-palette-8)"> </span> demos dir 3.1 kB11-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">7</span><span style="background:var(--vt-palette-8)"> </span> go.mod file 812 B12-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">8</span><span style="background:var(--vt-palette-8)"> </span>13-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">9</span><span style="background:var(--vt-palette-8)"> </span>14-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">10</span><span style="background:var(--vt-palette-8)"> </span>15-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">11</span><span style="background:var(--vt-palette-8)"> </span>16-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">12</span><span style="background:var(--vt-palette-8)"> </span>17-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">13</span><span style="background:var(--vt-palette-8)"> </span>18-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">14</span><span style="background:var(--vt-palette-8)"> </span>19-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">15</span><span style="background:var(--vt-palette-8)"> </span>20-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">16</span><span style="background:var(--vt-palette-8)"> </span>21-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">17</span><span style="background:var(--vt-palette-8)"> </span>22-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">18</span><span style="background:var(--vt-palette-8)"> </span>23-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">19</span><span style="background:var(--vt-palette-8)"> </span>24-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">20</span><span style="background:var(--vt-palette-8)"> </span>25-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">21</span><span style="background:var(--vt-palette-8)"> </span>26-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">22</span><span style="background:var(--vt-palette-8)"> </span>27-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">23</span><span style="background:var(--vt-palette-8)"> </span>28-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">24</span><span style="background:var(--vt-palette-8)"> </span>29-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">25</span><span style="background:var(--vt-palette-8)"> </span>30-command <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Tab</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">complete</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+N</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">next</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Enter</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">run</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Esc</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">grid</span></pre>\ No newline at end of file
A e2e/testdata/screens/notebook-error-high-contrast.html +30 -0
@@ -0,0 +1,30 @@
1+<pre class="screen"><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">F</span><span style="color:#ffffff;background:#1f1f1f">ile</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">E</span><span style="color:#ffffff;background:#1f1f1f">dit</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">V</span><span style="color:#ffffff;background:#1f1f1f">iew</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">I</span><span style="color:#ffffff;background:#1f1f1f">nsert</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f">F</span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">o</span><span style="color:#ffffff;background:#1f1f1f">rmat</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">D</span><span style="color:#ffffff;background:#1f1f1f">ata</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">H</span><span style="color:#ffffff;background:#1f1f1f">elp</span><span style="background:#1f1f1f"> </span><span style="background:#40e0ff"> </span><span style="color:#000000;background:#40e0ff;font-weight:bold">NOTEBOOK</span><span style="background:#40e0ff"> </span>2+<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">cell</span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">4</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">a</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">nushell</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">pipeline</span><span style="background:#000000"> </span>3+<span style="color:#ffe040;background:#000000">Failed:</span><span style="background:#000000"> </span><span style="color:#ffe040;background:#000000">Command</span><span style="background:#000000"> </span><span style="color:#ffe040;background:#000000">`lss`</span><span style="background:#000000"> </span><span style="color:#ffe040;background:#000000">not</span><span style="background:#000000"> </span><span style="color:#ffe040;background:#000000">found</span><span style="background:#000000"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">Enter</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">edit</span>4+<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">Notebook</span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">4</span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">cells</span><span style="background:#3a3a3a"> </span>5+<span style="background:#000000"> </span><span style="color:#959595;background:#000000">Markdown</span><span style="background:#000000"> </span>6+<span style="color:#757575;background:#000000">│</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold;text-decoration:underline">Files</span><span style="background:#000000"> </span>7+<span style="color:#757575;background:#000000">│</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">What&#39;s</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">big</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">in</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">the</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">repo,</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">from</span><span style="background:#000000"> </span><span style="color:#40e0ff;background:#000000">ls</span><span style="color:#ffffff;background:#000000">.</span><span style="background:#000000"> </span>8+<span style="background:#000000"> </span>9+<span style="background:#000000"> </span><span style="color:#959595;background:#000000">[1]</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">files</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">✓</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">&lt;1s</span><span style="background:#000000"> </span>10+<span style="color:#757575;background:#000000">│</span><span style="background:#000000"> </span><span style="color:#ff80ff;background:#000000">files</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold">=</span><span style="background:#000000"> </span><span style="color:#40e0ff;background:#000000">ls</span><span style="background:#000000"> </span>11+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold;text-decoration:underline">name</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold;text-decoration:underline">type</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold;text-decoration:underline">size</span><span style="background:#000000"> </span>12+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">012</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">4.2</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>13+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">CLAUDE.md</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">1.0</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>14+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">README.md</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">9.8</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>15+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">demos</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">dir</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">3.1</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>16+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">go.mod</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">812</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">B</span><span style="background:#000000"> </span>17+<span style="background:#000000"> </span>18+<span style="background:#000000"> </span><span style="color:#959595;background:#000000">[2]</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">✓</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">&lt;1s</span><span style="background:#000000"> </span>19+<span style="color:#757575;background:#000000">│</span><span style="background:#000000"> </span><span style="color:#ff80ff;background:#000000">$files</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold">|</span><span style="background:#000000"> </span><span style="color:#40e0ff;background:#000000">where</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">size</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold">&gt;</span><span style="background:#000000"> </span><span style="color:#ffe040;background:#000000">1kb</span><span style="background:#000000"> </span>20+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold;text-decoration:underline">name</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold;text-decoration:underline">type</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold;text-decoration:underline">size</span><span style="background:#000000"> </span>21+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">012</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">4.2</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>22+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">README.md</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">9.8</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>23+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">demos</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">dir</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">3.1</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>24+<span style="background:#000000"> </span>25+<span style="background:#0033cc"> </span><span style="color:#ffffff;background:#0033cc">[3]</span><span style="background:#0033cc"> </span><span style="color:#ffffff;background:#0033cc">failed</span><span style="background:#0033cc"> </span><span style="color:#ffffff;background:#0033cc">&lt;1s</span><span style="background:#0033cc"> </span><span style="background:#000000"> </span>26+<span style="color:#757575;background:#000000">│</span><span style="background:#000000"> </span><span style="color:#40e0ff;background:#000000">lss</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">-a</span><span style="background:#000000"> </span>27+<span style="background:#000000"> </span><span style="color:#ffe040;background:#000000">×</span><span style="background:#000000"> </span><span style="color:#ffe040;background:#000000">Command</span><span style="background:#000000"> </span><span style="color:#ffe040;background:#000000">`lss`</span><span style="background:#000000"> </span><span style="color:#ffe040;background:#000000">not</span><span style="background:#000000"> </span><span style="color:#ffe040;background:#000000">found</span><span style="background:#000000"> </span>28+<span style="background:#000000"> </span><span style="color:#959595;background:#000000">help:</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">commands</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">are</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">listed</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">by</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">`help</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">commands`</span><span style="background:#000000"> </span>29+<span style="background:#000000"> </span>30+<span style="background:#40e0ff"> </span><span style="color:#000000;background:#40e0ff;font-weight:bold">❯</span><span style="background:#40e0ff"> </span><span style="color:#000000;background:#40e0ff;font-weight:bold">Notebook</span><span style="background:#40e0ff"> </span><span style="background:#1f1f1f"> </span><span style="color:#959595;background:#1f1f1f">+</span><span style="background:#1f1f1f"> </span><span style="color:#757575;background:#1f1f1f">│</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f">untitled</span><span style="background:#1f1f1f"> </span><span style="color:#959595;background:#1f1f1f">modified</span><span style="background:#1f1f1f"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">Ctrl+K</span><span style="background:#3a3a3a"> </span><span style="background:#1f1f1f"> </span><span style="color:#959595;background:#1f1f1f">search</span><span style="background:#1f1f1f"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">Ctrl+/</span><span style="background:#3a3a3a"> </span><span style="background:#1f1f1f"> </span><span style="color:#959595;background:#1f1f1f">shortcuts</span><span style="background:#1f1f1f"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">F10</span><span style="background:#3a3a3a"> </span><span style="background:#1f1f1f"> </span><span style="color:#959595;background:#1f1f1f">menu</span></pre>\ No newline at end of file
A e2e/testdata/screens/notebook-error-light.html +30 -0
@@ -0,0 +1,30 @@
1+<pre class="screen"> <span style="text-decoration:underline">F</span>ile <span style="text-decoration:underline">E</span>dit <span style="text-decoration:underline">V</span>iew <span style="text-decoration:underline">I</span>nsert F<span style="text-decoration:underline">o</span>rmat <span style="text-decoration:underline">D</span>ata <span style="text-decoration:underline">H</span>elp <span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">NOTEBOOK</span><span style="background:var(--vt-palette-6)"> </span>2+<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">cell</span><span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">4</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">a</span> <span style="color:var(--vt-palette-0)">nushell</span> <span style="color:var(--vt-palette-0)">pipeline</span>3+<span style="color:var(--vt-palette-3)">Failed:</span> <span style="color:var(--vt-palette-3)">Command</span> <span style="color:var(--vt-palette-3)">`lss`</span> <span style="color:var(--vt-palette-3)">not</span> <span style="color:var(--vt-palette-3)">found</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">Enter</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">edit</span>4+ Notebook 4 cells5+ <span style="color:var(--vt-palette-0)">Markdown</span>6+<span style="color:var(--vt-palette-8)">│</span> <span style="font-weight:bold;text-decoration:underline">Files</span>7+<span style="color:var(--vt-palette-8)">│</span> What&#39;s big in the repo, from <span style="color:var(--vt-palette-4)">ls</span>.8+9+ <span style="color:var(--vt-palette-0)">[1]</span> <span style="color:var(--vt-palette-0)">files</span> <span style="color:var(--vt-palette-0)">✓</span> <span style="color:var(--vt-palette-0)">&lt;1s</span>10+<span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-5)">files</span> <span style="font-weight:bold">=</span> <span style="color:var(--vt-palette-4)">ls</span>11+ <span style="font-weight:bold;text-decoration:underline">name</span> <span style="font-weight:bold;text-decoration:underline">type</span> <span style="font-weight:bold;text-decoration:underline">size</span>12+ 012 file 4.2 kB13+ CLAUDE.md file 1.0 kB14+ README.md file 9.8 kB15+ demos dir 3.1 kB16+ go.mod file 812 B17+18+ <span style="color:var(--vt-palette-0)">[2]</span> <span style="color:var(--vt-palette-0)">✓</span> <span style="color:var(--vt-palette-0)">&lt;1s</span>19+<span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-5)">$files</span> <span style="font-weight:bold">|</span> <span style="color:var(--vt-palette-4)">where</span> size <span style="font-weight:bold">&gt;</span> <span style="color:var(--vt-palette-1)">1kb</span>20+ <span style="font-weight:bold;text-decoration:underline">name</span> <span style="font-weight:bold;text-decoration:underline">type</span> <span style="font-weight:bold;text-decoration:underline">size</span>21+ 012 file 4.2 kB22+ README.md file 9.8 kB23+ demos dir 3.1 kB24+25+<span style="background:var(--vt-palette-4)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-4)">[3]</span><span style="background:var(--vt-palette-4)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-4)">failed</span><span style="background:var(--vt-palette-4)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-4)">&lt;1s</span><span style="background:var(--vt-palette-4)"> </span>26+<span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-4)">lss</span> -a27+ <span style="color:var(--vt-palette-3)">×</span> <span style="color:var(--vt-palette-3)">Command</span> <span style="color:var(--vt-palette-3)">`lss`</span> <span style="color:var(--vt-palette-3)">not</span> <span style="color:var(--vt-palette-3)">found</span>28+ <span style="color:var(--vt-palette-0)">help:</span> <span style="color:var(--vt-palette-0)">commands</span> <span style="color:var(--vt-palette-0)">are</span> <span style="color:var(--vt-palette-0)">listed</span> <span style="color:var(--vt-palette-0)">by</span> <span style="color:var(--vt-palette-0)">`help</span> <span style="color:var(--vt-palette-0)">commands`</span>29+30+<span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">❯</span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">Notebook</span><span style="background:var(--vt-palette-6)"> </span> <span style="color:var(--vt-palette-0)">+</span> <span style="color:var(--vt-palette-8)">│</span> untitled <span style="color:var(--vt-palette-0)">modified</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">Ctrl+K</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">search</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">Ctrl+/</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">shortcuts</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">F10</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">menu</span></pre>\ No newline at end of file
A e2e/testdata/screens/notebook-error.html +30 -0
@@ -0,0 +1,30 @@
1+<pre class="screen"> <span style="text-decoration:underline">F</span>ile <span style="text-decoration:underline">E</span>dit <span style="text-decoration:underline">V</span>iew <span style="text-decoration:underline">I</span>nsert F<span style="text-decoration:underline">o</span>rmat <span style="text-decoration:underline">D</span>ata <span style="text-decoration:underline">H</span>elp <span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">NOTEBOOK</span><span style="background:var(--vt-palette-6)"> </span>2+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">cell</span><span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">4</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">a</span> <span style="color:var(--vt-palette-8)">nushell</span> <span style="color:var(--vt-palette-8)">pipeline</span>3+<span style="color:var(--vt-palette-3)">Failed:</span> <span style="color:var(--vt-palette-3)">Command</span> <span style="color:var(--vt-palette-3)">`lss`</span> <span style="color:var(--vt-palette-3)">not</span> <span style="color:var(--vt-palette-3)">found</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Enter</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">edit</span>4+ Notebook 4 cells5+ <span style="color:var(--vt-palette-8)">Markdown</span>6+<span style="color:var(--vt-palette-8)">│</span> <span style="font-weight:bold;text-decoration:underline">Files</span>7+<span style="color:var(--vt-palette-8)">│</span> What&#39;s big in the repo, from <span style="color:var(--vt-palette-6)">ls</span>.8+9+ <span style="color:var(--vt-palette-8)">[1]</span> <span style="color:var(--vt-palette-8)">files</span> <span style="color:var(--vt-palette-8)">✓</span> <span style="color:var(--vt-palette-8)">&lt;1s</span>10+<span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-5)">files</span> <span style="font-weight:bold">=</span> <span style="color:var(--vt-palette-6)">ls</span>11+ <span style="font-weight:bold;text-decoration:underline">name</span> <span style="font-weight:bold;text-decoration:underline">type</span> <span style="font-weight:bold;text-decoration:underline">size</span>12+ 012 file 4.2 kB13+ CLAUDE.md file 1.0 kB14+ README.md file 9.8 kB15+ demos dir 3.1 kB16+ go.mod file 812 B17+18+ <span style="color:var(--vt-palette-8)">[2]</span> <span style="color:var(--vt-palette-8)">✓</span> <span style="color:var(--vt-palette-8)">&lt;1s</span>19+<span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-5)">$files</span> <span style="font-weight:bold">|</span> <span style="color:var(--vt-palette-6)">where</span> size <span style="font-weight:bold">&gt;</span> <span style="color:var(--vt-palette-3)">1kb</span>20+ <span style="font-weight:bold;text-decoration:underline">name</span> <span style="font-weight:bold;text-decoration:underline">type</span> <span style="font-weight:bold;text-decoration:underline">size</span>21+ 012 file 4.2 kB22+ README.md file 9.8 kB23+ demos dir 3.1 kB24+25+<span style="background:var(--vt-palette-4)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-4)">[3]</span><span style="background:var(--vt-palette-4)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-4)">failed</span><span style="background:var(--vt-palette-4)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-4)">&lt;1s</span><span style="background:var(--vt-palette-4)"> </span>26+<span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-6)">lss</span> -a27+ <span style="color:var(--vt-palette-3)">×</span> <span style="color:var(--vt-palette-3)">Command</span> <span style="color:var(--vt-palette-3)">`lss`</span> <span style="color:var(--vt-palette-3)">not</span> <span style="color:var(--vt-palette-3)">found</span>28+ <span style="color:var(--vt-palette-8)">help:</span> <span style="color:var(--vt-palette-8)">commands</span> <span style="color:var(--vt-palette-8)">are</span> <span style="color:var(--vt-palette-8)">listed</span> <span style="color:var(--vt-palette-8)">by</span> <span style="color:var(--vt-palette-8)">`help</span> <span style="color:var(--vt-palette-8)">commands`</span>29+30+<span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">❯</span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">Notebook</span><span style="background:var(--vt-palette-6)"> </span> <span style="color:var(--vt-palette-8)">+</span> <span style="color:var(--vt-palette-8)">│</span> untitled <span style="color:var(--vt-palette-8)">modified</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+K</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">search</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+/</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">shortcuts</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">F10</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">menu</span></pre>\ No newline at end of file
D e2e/testdata/screens/notebook-failed-high-contrast.html +0 -30
@@ -1,30 +0,0 @@
1-<pre class="screen"><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">F</span><span style="color:#ffffff;background:#1f1f1f">ile</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">E</span><span style="color:#ffffff;background:#1f1f1f">dit</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">V</span><span style="color:#ffffff;background:#1f1f1f">iew</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">I</span><span style="color:#ffffff;background:#1f1f1f">nsert</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f">F</span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">o</span><span style="color:#ffffff;background:#1f1f1f">rmat</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">D</span><span style="color:#ffffff;background:#1f1f1f">ata</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">H</span><span style="color:#ffffff;background:#1f1f1f">elp</span><span style="background:#1f1f1f"> </span><span style="background:#40e0ff"> </span><span style="color:#000000;background:#40e0ff;font-weight:bold">NU</span><span style="background:#40e0ff"> </span>2-<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">A9</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold">nu❯</span><span style="background:#000000"> </span>3-<span style="color:#ffe040;background:#000000">r2</span><span style="background:#000000"> </span><span style="color:#ffe040;background:#000000">failed:</span><span style="background:#000000"> </span><span style="color:#ffe040;background:#000000">Command</span><span style="background:#000000"> </span><span style="color:#ffe040;background:#000000">`lss`</span><span style="background:#000000"> </span><span style="color:#ffe040;background:#000000">not</span><span style="background:#000000"> </span><span style="color:#ffe040;background:#000000">found</span><span style="background:#000000"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">Esc</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">back</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">to</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">the</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">grid</span>4-<span style="background:#3a3a3a"> </span><span style="background:#40e0ff"> </span><span style="color:#000000;background:#40e0ff;font-weight:bold">A</span><span style="background:#40e0ff"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">B</span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">C</span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">D</span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">E</span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">F</span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">G</span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">H</span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">I</span><span style="background:#3a3a3a"> </span>5-<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">1</span><span style="background:#3a3a3a"> </span><span style="background:#000000;text-decoration:underline"> </span><span style="color:#959595;background:#000000;text-decoration:underline">r1</span><span style="background:#000000;text-decoration:underline"> </span><span style="color:#959595;background:#000000;text-decoration:underline">ls</span><span style="background:#000000;text-decoration:underline"> </span><span style="background:#000000"> </span>6-<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">2</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">name</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">type</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">size</span><span style="background:#000000"> </span>7-<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">3</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">012</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">4.2</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>8-<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">4</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">CLAUDE.md</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">1.0</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>9-<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">5</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">README.md</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">9.8</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>10-<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">6</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">demos</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">dir</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">3.1</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>11-<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">7</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">go.mod</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">812</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">B</span><span style="background:#000000"> </span>12-<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">8</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span>13-<span style="background:#40e0ff"> </span><span style="color:#000000;background:#40e0ff;font-weight:bold">9</span><span style="background:#40e0ff"> </span><span style="color:#000000;background:#40e0ff">r2</span><span style="background:#40e0ff"> </span><span style="color:#000000;background:#40e0ff">lss</span><span style="background:#40e0ff"> </span><span style="color:#000000;background:#40e0ff">-</span><span style="color:#959595;background:#000000;text-decoration:underline">a</span><span style="background:#000000;text-decoration:underline"> </span><span style="color:#959595;background:#000000;text-decoration:underline">failed</span><span style="background:#000000"> </span>14-<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">10</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span>15-<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">11</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span>16-<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">12</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span>17-<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">13</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span>18-<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">14</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span>19-<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">15</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span>20-<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">16</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span>21-<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">17</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span>22-<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">18</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span>23-<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">19</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span>24-<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">20</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span>25-<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">21</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span>26-<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">22</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span>27-<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">23</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span>28-<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">24</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span>29-<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">25</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span>30-<span style="background:#40e0ff"> </span><span style="color:#000000;background:#40e0ff;font-weight:bold">Shell</span><span style="background:#40e0ff"> </span><span style="color:#000000;background:#40e0ff;font-weight:bold">1</span><span style="background:#40e0ff"> </span><span style="background:#1f1f1f"> </span><span style="color:#959595;background:#1f1f1f">+</span><span style="background:#1f1f1f"> </span><span style="color:#757575;background:#1f1f1f">│</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f">untitled</span><span style="background:#1f1f1f"> </span><span style="color:#959595;background:#1f1f1f">modified</span><span style="background:#1f1f1f"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">Ctrl+K</span><span style="background:#3a3a3a"> </span><span style="background:#1f1f1f"> </span><span style="color:#959595;background:#1f1f1f">search</span><span style="background:#1f1f1f"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">Ctrl+/</span><span style="background:#3a3a3a"> </span><span style="background:#1f1f1f"> </span><span style="color:#959595;background:#1f1f1f">shortcuts</span><span style="background:#1f1f1f"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">F10</span><span style="background:#3a3a3a"> </span><span style="background:#1f1f1f"> </span><span style="color:#959595;background:#1f1f1f">menu</span></pre>\ No newline at end of file
D e2e/testdata/screens/notebook-failed-light.html +0 -30
@@ -1,30 +0,0 @@
1-<pre class="screen"> <span style="text-decoration:underline">F</span>ile <span style="text-decoration:underline">E</span>dit <span style="text-decoration:underline">V</span>iew <span style="text-decoration:underline">I</span>nsert F<span style="text-decoration:underline">o</span>rmat <span style="text-decoration:underline">D</span>ata <span style="text-decoration:underline">H</span>elp <span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">NU</span><span style="background:var(--vt-palette-6)"> </span>2-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">A9</span><span style="background:var(--vt-palette-7)"> </span> <span style="font-weight:bold">nu❯</span>3-<span style="color:var(--vt-palette-3)">r2</span> <span style="color:var(--vt-palette-3)">failed:</span> <span style="color:var(--vt-palette-3)">Command</span> <span style="color:var(--vt-palette-3)">`lss`</span> <span style="color:var(--vt-palette-3)">not</span> <span style="color:var(--vt-palette-3)">found</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">Esc</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">back</span> <span style="color:var(--vt-palette-0)">to</span> <span style="color:var(--vt-palette-0)">the</span> <span style="color:var(--vt-palette-0)">grid</span>4-<span style="background:var(--vt-palette-7)"> </span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">A</span><span style="background:var(--vt-palette-6)"> </span><span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">B</span><span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">C</span><span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">D</span><span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">E</span><span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">F</span><span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">G</span><span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">H</span><span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">I</span><span style="background:var(--vt-palette-7)"> </span>5-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">1</span><span style="background:var(--vt-palette-7)"> </span><span style="text-decoration:underline"> </span><span style="color:var(--vt-palette-0);text-decoration:underline">r1</span><span style="text-decoration:underline"> </span><span style="color:var(--vt-palette-0);text-decoration:underline">ls</span><span style="text-decoration:underline"> </span>6-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">2</span><span style="background:var(--vt-palette-7)"> </span> name type size7-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">3</span><span style="background:var(--vt-palette-7)"> </span> 012 file 4.2 kB8-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">4</span><span style="background:var(--vt-palette-7)"> </span> CLAUDE.md file 1.0 kB9-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">5</span><span style="background:var(--vt-palette-7)"> </span> README.md file 9.8 kB10-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">6</span><span style="background:var(--vt-palette-7)"> </span> demos dir 3.1 kB11-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">7</span><span style="background:var(--vt-palette-7)"> </span> go.mod file 812 B12-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">8</span><span style="background:var(--vt-palette-7)"> </span>13-<span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">9</span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6)">r2</span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6)">lss</span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6)">-</span><span style="color:var(--vt-palette-0);text-decoration:underline">a</span><span style="text-decoration:underline"> </span><span style="color:var(--vt-palette-0);text-decoration:underline">failed</span>14-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">10</span><span style="background:var(--vt-palette-7)"> </span>15-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">11</span><span style="background:var(--vt-palette-7)"> </span>16-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">12</span><span style="background:var(--vt-palette-7)"> </span>17-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">13</span><span style="background:var(--vt-palette-7)"> </span>18-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">14</span><span style="background:var(--vt-palette-7)"> </span>19-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">15</span><span style="background:var(--vt-palette-7)"> </span>20-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">16</span><span style="background:var(--vt-palette-7)"> </span>21-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">17</span><span style="background:var(--vt-palette-7)"> </span>22-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">18</span><span style="background:var(--vt-palette-7)"> </span>23-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">19</span><span style="background:var(--vt-palette-7)"> </span>24-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">20</span><span style="background:var(--vt-palette-7)"> </span>25-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">21</span><span style="background:var(--vt-palette-7)"> </span>26-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">22</span><span style="background:var(--vt-palette-7)"> </span>27-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">23</span><span style="background:var(--vt-palette-7)"> </span>28-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">24</span><span style="background:var(--vt-palette-7)"> </span>29-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">25</span><span style="background:var(--vt-palette-7)"> </span>30-<span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">Shell</span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">1</span><span style="background:var(--vt-palette-6)"> </span> <span style="color:var(--vt-palette-0)">+</span> <span style="color:var(--vt-palette-8)">│</span> untitled <span style="color:var(--vt-palette-0)">modified</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">Ctrl+K</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">search</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">Ctrl+/</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">shortcuts</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">F10</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">menu</span></pre>\ No newline at end of file
D e2e/testdata/screens/notebook-failed.html +0 -30
@@ -1,30 +0,0 @@
1-<pre class="screen"> <span style="text-decoration:underline">F</span>ile <span style="text-decoration:underline">E</span>dit <span style="text-decoration:underline">V</span>iew <span style="text-decoration:underline">I</span>nsert F<span style="text-decoration:underline">o</span>rmat <span style="text-decoration:underline">D</span>ata <span style="text-decoration:underline">H</span>elp <span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">NU</span><span style="background:var(--vt-palette-6)"> </span>2-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">A9</span><span style="background:var(--vt-palette-8)"> </span> <span style="font-weight:bold">nu❯</span>3-<span style="color:var(--vt-palette-3)">r2</span> <span style="color:var(--vt-palette-3)">failed:</span> <span style="color:var(--vt-palette-3)">Command</span> <span style="color:var(--vt-palette-3)">`lss`</span> <span style="color:var(--vt-palette-3)">not</span> <span style="color:var(--vt-palette-3)">found</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Esc</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">back</span> <span style="color:var(--vt-palette-8)">to</span> <span style="color:var(--vt-palette-8)">the</span> <span style="color:var(--vt-palette-8)">grid</span>4-<span style="background:var(--vt-palette-8)"> </span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">A</span><span style="background:var(--vt-palette-6)"> </span><span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">B</span><span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">C</span><span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">D</span><span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">E</span><span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">F</span><span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">G</span><span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">H</span><span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">I</span><span style="background:var(--vt-palette-8)"> </span>5-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">1</span><span style="background:var(--vt-palette-8)"> </span><span style="text-decoration:underline"> </span><span style="color:var(--vt-palette-8);text-decoration:underline">r1</span><span style="text-decoration:underline"> </span><span style="color:var(--vt-palette-8);text-decoration:underline">ls</span><span style="text-decoration:underline"> </span>6-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">2</span><span style="background:var(--vt-palette-8)"> </span> name type size7-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">3</span><span style="background:var(--vt-palette-8)"> </span> 012 file 4.2 kB8-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">4</span><span style="background:var(--vt-palette-8)"> </span> CLAUDE.md file 1.0 kB9-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">5</span><span style="background:var(--vt-palette-8)"> </span> README.md file 9.8 kB10-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">6</span><span style="background:var(--vt-palette-8)"> </span> demos dir 3.1 kB11-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">7</span><span style="background:var(--vt-palette-8)"> </span> go.mod file 812 B12-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">8</span><span style="background:var(--vt-palette-8)"> </span>13-<span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">9</span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6)">r2</span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6)">lss</span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6)">-</span><span style="color:var(--vt-palette-8);text-decoration:underline">a</span><span style="text-decoration:underline"> </span><span style="color:var(--vt-palette-8);text-decoration:underline">failed</span>14-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">10</span><span style="background:var(--vt-palette-8)"> </span>15-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">11</span><span style="background:var(--vt-palette-8)"> </span>16-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">12</span><span style="background:var(--vt-palette-8)"> </span>17-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">13</span><span style="background:var(--vt-palette-8)"> </span>18-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">14</span><span style="background:var(--vt-palette-8)"> </span>19-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">15</span><span style="background:var(--vt-palette-8)"> </span>20-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">16</span><span style="background:var(--vt-palette-8)"> </span>21-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">17</span><span style="background:var(--vt-palette-8)"> </span>22-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">18</span><span style="background:var(--vt-palette-8)"> </span>23-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">19</span><span style="background:var(--vt-palette-8)"> </span>24-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">20</span><span style="background:var(--vt-palette-8)"> </span>25-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">21</span><span style="background:var(--vt-palette-8)"> </span>26-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">22</span><span style="background:var(--vt-palette-8)"> </span>27-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">23</span><span style="background:var(--vt-palette-8)"> </span>28-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">24</span><span style="background:var(--vt-palette-8)"> </span>29-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">25</span><span style="background:var(--vt-palette-8)"> </span>30-<span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">Shell</span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">1</span><span style="background:var(--vt-palette-6)"> </span> <span style="color:var(--vt-palette-8)">+</span> <span style="color:var(--vt-palette-8)">│</span> untitled <span style="color:var(--vt-palette-8)">modified</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+K</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">search</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+/</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">shortcuts</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">F10</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">menu</span></pre>\ No newline at end of file
A e2e/testdata/screens/notebook-nu-error-high-contrast.html +30 -0
@@ -0,0 +1,30 @@
1+<pre class="screen"><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">F</span><span style="color:#ffffff;background:#1f1f1f">ile</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">E</span><span style="color:#ffffff;background:#1f1f1f">dit</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">V</span><span style="color:#ffffff;background:#1f1f1f">iew</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">I</span><span style="color:#ffffff;background:#1f1f1f">nsert</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f">F</span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">o</span><span style="color:#ffffff;background:#1f1f1f">rmat</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">D</span><span style="color:#ffffff;background:#1f1f1f">ata</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">H</span><span style="color:#ffffff;background:#1f1f1f">elp</span><span style="background:#1f1f1f"> </span><span style="background:#40e0ff"> </span><span style="color:#000000;background:#40e0ff;font-weight:bold">EDIT</span><span style="background:#40e0ff"> </span>2+<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">cell</span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">4</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">a</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">nushell</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">pipeline</span><span style="background:#000000"> </span>3+<span style="color:#ffe040;background:#000000">The</span><span style="background:#000000"> </span><span style="color:#ffe040;background:#000000">`sort-by`</span><span style="background:#000000"> </span><span style="color:#ffe040;background:#000000">command</span><span style="background:#000000"> </span><span style="color:#ffe040;background:#000000">doesn&#39;t</span><span style="background:#000000"> </span><span style="color:#ffe040;background:#000000">have</span><span style="background:#000000"> </span><span style="color:#ffe040;background:#000000">flag</span><span style="background:#000000"> </span><span style="color:#ffe040;background:#000000">`revrse`.</span><span style="background:#000000"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">Shift+Enter</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">run</span><span style="background:#000000"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">Esc</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">done</span>4+<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">Notebook</span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">4</span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">cells</span><span style="background:#3a3a3a"> </span>5+<span style="background:#000000"> </span><span style="color:#959595;background:#000000">Markdown</span><span style="background:#000000"> </span>6+<span style="color:#757575;background:#000000">│</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold;text-decoration:underline">Files</span><span style="background:#000000"> </span>7+<span style="color:#757575;background:#000000">│</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">What&#39;s</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">big</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">in</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">the</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">repo,</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">from</span><span style="background:#000000"> </span><span style="color:#40e0ff;background:#000000">ls</span><span style="color:#ffffff;background:#000000">.</span><span style="background:#000000"> </span>8+<span style="background:#000000"> </span>9+<span style="background:#000000"> </span><span style="color:#959595;background:#000000">[1]</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">files</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">✓</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">&lt;1s</span><span style="background:#000000"> </span>10+<span style="color:#757575;background:#000000">│</span><span style="background:#000000"> </span><span style="color:#ff80ff;background:#000000">files</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold">=</span><span style="background:#000000"> </span><span style="color:#40e0ff;background:#000000">ls</span><span style="background:#000000"> </span>11+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold;text-decoration:underline">name</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold;text-decoration:underline">type</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold;text-decoration:underline">size</span><span style="background:#000000"> </span>12+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">012</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">4.2</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>13+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">CLAUDE.md</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">1.0</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>14+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">README.md</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">9.8</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>15+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">demos</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">dir</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">3.1</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>16+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">go.mod</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">812</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">B</span><span style="background:#000000"> </span>17+<span style="background:#000000"> </span>18+<span style="background:#000000"> </span><span style="color:#959595;background:#000000">[2]</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">✓</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">&lt;1s</span><span style="background:#000000"> </span>19+<span style="color:#757575;background:#000000">│</span><span style="background:#000000"> </span><span style="color:#ff80ff;background:#000000">$files</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold">|</span><span style="background:#000000"> </span><span style="color:#40e0ff;background:#000000">where</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">size</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold">&gt;</span><span style="background:#000000"> </span><span style="color:#ffe040;background:#000000">1kb</span><span style="background:#000000"> </span>20+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold;text-decoration:underline">name</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold;text-decoration:underline">type</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold;text-decoration:underline">size</span><span style="background:#000000"> </span>21+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">012</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">4.2</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>22+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">README.md</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">9.8</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>23+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">demos</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">dir</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">3.1</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>24+<span style="background:#000000"> </span>25+<span style="background:#40e0ff"> </span><span style="color:#000000;background:#40e0ff">[</span><span style="background:#40e0ff"> </span><span style="color:#000000;background:#40e0ff">]</span><span style="background:#40e0ff"> </span><span style="color:#000000;background:#40e0ff">not</span><span style="background:#40e0ff"> </span><span style="color:#000000;background:#40e0ff">run</span><span style="background:#40e0ff"> </span><span style="background:#000000"> </span>26+<span style="color:#757575;background:#000000">│</span><span style="background:#000000"> </span><span style="color:#ff80ff;background:#000000">$files</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold">|</span><span style="background:#000000"> </span><span style="color:#40e0ff;background:#000000">sort-by</span><span style="background:#000000"> </span><span style="color:#40e040;background:#000000">size</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;text-decoration:underline wavy #ff8080">--revrse</span><span style="background:#000000"> </span>27+<span style="background:#000000"> </span>28+<span style="background:#000000"> </span>29+<span style="background:#000000"> </span>30+<span style="background:#40e0ff"> </span><span style="color:#000000;background:#40e0ff;font-weight:bold">❯</span><span style="background:#40e0ff"> </span><span style="color:#000000;background:#40e0ff;font-weight:bold">Notebook</span><span style="background:#40e0ff"> </span><span style="background:#1f1f1f"> </span><span style="color:#959595;background:#1f1f1f">+</span><span style="background:#1f1f1f"> </span><span style="color:#757575;background:#1f1f1f">│</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f">untitled</span><span style="background:#1f1f1f"> </span><span style="color:#959595;background:#1f1f1f">modified</span><span style="background:#1f1f1f"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">Ctrl+K</span><span style="background:#3a3a3a"> </span><span style="background:#1f1f1f"> </span><span style="color:#959595;background:#1f1f1f">search</span><span style="background:#1f1f1f"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">Ctrl+/</span><span style="background:#3a3a3a"> </span><span style="background:#1f1f1f"> </span><span style="color:#959595;background:#1f1f1f">shortcuts</span><span style="background:#1f1f1f"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">F10</span><span style="background:#3a3a3a"> </span><span style="background:#1f1f1f"> </span><span style="color:#959595;background:#1f1f1f">menu</span></pre>\ No newline at end of file
A e2e/testdata/screens/notebook-nu-error-light.html +30 -0
@@ -0,0 +1,30 @@
1+<pre class="screen"> <span style="text-decoration:underline">F</span>ile <span style="text-decoration:underline">E</span>dit <span style="text-decoration:underline">V</span>iew <span style="text-decoration:underline">I</span>nsert F<span style="text-decoration:underline">o</span>rmat <span style="text-decoration:underline">D</span>ata <span style="text-decoration:underline">H</span>elp <span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">EDIT</span><span style="background:var(--vt-palette-6)"> </span>2+<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">cell</span><span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">4</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">a</span> <span style="color:var(--vt-palette-0)">nushell</span> <span style="color:var(--vt-palette-0)">pipeline</span>3+<span style="color:var(--vt-palette-3)">The</span> <span style="color:var(--vt-palette-3)">`sort-by`</span> <span style="color:var(--vt-palette-3)">command</span> <span style="color:var(--vt-palette-3)">doesn&#39;t</span> <span style="color:var(--vt-palette-3)">have</span> <span style="color:var(--vt-palette-3)">flag</span> <span style="color:var(--vt-palette-3)">`revrse`.</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">Shift+Enter</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">run</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">Esc</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">done</span>4+ Notebook 4 cells5+ <span style="color:var(--vt-palette-0)">Markdown</span>6+<span style="color:var(--vt-palette-8)">│</span> <span style="font-weight:bold;text-decoration:underline">Files</span>7+<span style="color:var(--vt-palette-8)">│</span> What&#39;s big in the repo, from <span style="color:var(--vt-palette-4)">ls</span>.8+9+ <span style="color:var(--vt-palette-0)">[1]</span> <span style="color:var(--vt-palette-0)">files</span> <span style="color:var(--vt-palette-0)">✓</span> <span style="color:var(--vt-palette-0)">&lt;1s</span>10+<span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-5)">files</span> <span style="font-weight:bold">=</span> <span style="color:var(--vt-palette-4)">ls</span>11+ <span style="font-weight:bold;text-decoration:underline">name</span> <span style="font-weight:bold;text-decoration:underline">type</span> <span style="font-weight:bold;text-decoration:underline">size</span>12+ 012 file 4.2 kB13+ CLAUDE.md file 1.0 kB14+ README.md file 9.8 kB15+ demos dir 3.1 kB16+ go.mod file 812 B17+18+ <span style="color:var(--vt-palette-0)">[2]</span> <span style="color:var(--vt-palette-0)">✓</span> <span style="color:var(--vt-palette-0)">&lt;1s</span>19+<span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-5)">$files</span> <span style="font-weight:bold">|</span> <span style="color:var(--vt-palette-4)">where</span> size <span style="font-weight:bold">&gt;</span> <span style="color:var(--vt-palette-1)">1kb</span>20+ <span style="font-weight:bold;text-decoration:underline">name</span> <span style="font-weight:bold;text-decoration:underline">type</span> <span style="font-weight:bold;text-decoration:underline">size</span>21+ 012 file 4.2 kB22+ README.md file 9.8 kB23+ demos dir 3.1 kB24+25+<span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6)">[</span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6)">]</span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6)">not</span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6)">run</span><span style="background:var(--vt-palette-6)"> </span>26+<span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-5)">$files</span> <span style="font-weight:bold">|</span> <span style="color:var(--vt-palette-4)">sort-by</span> <span style="color:var(--vt-palette-2)">size</span> <span style="text-decoration:underline wavy var(--vt-palette-1)">--revrse</span>27+28+29+30+<span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">❯</span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">Notebook</span><span style="background:var(--vt-palette-6)"> </span> <span style="color:var(--vt-palette-0)">+</span> <span style="color:var(--vt-palette-8)">│</span> untitled <span style="color:var(--vt-palette-0)">modified</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">Ctrl+K</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">search</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">Ctrl+/</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">shortcuts</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">F10</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">menu</span></pre>\ No newline at end of file
A e2e/testdata/screens/notebook-nu-error.html +30 -0
@@ -0,0 +1,30 @@
1+<pre class="screen"> <span style="text-decoration:underline">F</span>ile <span style="text-decoration:underline">E</span>dit <span style="text-decoration:underline">V</span>iew <span style="text-decoration:underline">I</span>nsert F<span style="text-decoration:underline">o</span>rmat <span style="text-decoration:underline">D</span>ata <span style="text-decoration:underline">H</span>elp <span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">EDIT</span><span style="background:var(--vt-palette-6)"> </span>2+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">cell</span><span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">4</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">a</span> <span style="color:var(--vt-palette-8)">nushell</span> <span style="color:var(--vt-palette-8)">pipeline</span>3+<span style="color:var(--vt-palette-3)">The</span> <span style="color:var(--vt-palette-3)">`sort-by`</span> <span style="color:var(--vt-palette-3)">command</span> <span style="color:var(--vt-palette-3)">doesn&#39;t</span> <span style="color:var(--vt-palette-3)">have</span> <span style="color:var(--vt-palette-3)">flag</span> <span style="color:var(--vt-palette-3)">`revrse`.</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Shift+Enter</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">run</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Esc</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">done</span>4+ Notebook 4 cells5+ <span style="color:var(--vt-palette-8)">Markdown</span>6+<span style="color:var(--vt-palette-8)">│</span> <span style="font-weight:bold;text-decoration:underline">Files</span>7+<span style="color:var(--vt-palette-8)">│</span> What&#39;s big in the repo, from <span style="color:var(--vt-palette-6)">ls</span>.8+9+ <span style="color:var(--vt-palette-8)">[1]</span> <span style="color:var(--vt-palette-8)">files</span> <span style="color:var(--vt-palette-8)">✓</span> <span style="color:var(--vt-palette-8)">&lt;1s</span>10+<span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-5)">files</span> <span style="font-weight:bold">=</span> <span style="color:var(--vt-palette-6)">ls</span>11+ <span style="font-weight:bold;text-decoration:underline">name</span> <span style="font-weight:bold;text-decoration:underline">type</span> <span style="font-weight:bold;text-decoration:underline">size</span>12+ 012 file 4.2 kB13+ CLAUDE.md file 1.0 kB14+ README.md file 9.8 kB15+ demos dir 3.1 kB16+ go.mod file 812 B17+18+ <span style="color:var(--vt-palette-8)">[2]</span> <span style="color:var(--vt-palette-8)">✓</span> <span style="color:var(--vt-palette-8)">&lt;1s</span>19+<span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-5)">$files</span> <span style="font-weight:bold">|</span> <span style="color:var(--vt-palette-6)">where</span> size <span style="font-weight:bold">&gt;</span> <span style="color:var(--vt-palette-3)">1kb</span>20+ <span style="font-weight:bold;text-decoration:underline">name</span> <span style="font-weight:bold;text-decoration:underline">type</span> <span style="font-weight:bold;text-decoration:underline">size</span>21+ 012 file 4.2 kB22+ README.md file 9.8 kB23+ demos dir 3.1 kB24+25+<span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6)">[</span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6)">]</span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6)">not</span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6)">run</span><span style="background:var(--vt-palette-6)"> </span>26+<span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-5)">$files</span> <span style="font-weight:bold">|</span> <span style="color:var(--vt-palette-6)">sort-by</span> <span style="color:var(--vt-palette-2)">size</span> <span style="text-decoration:underline wavy var(--vt-palette-1)">--revrse</span>27+28+29+30+<span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">❯</span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">Notebook</span><span style="background:var(--vt-palette-6)"> </span> <span style="color:var(--vt-palette-8)">+</span> <span style="color:var(--vt-palette-8)">│</span> untitled <span style="color:var(--vt-palette-8)">modified</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+K</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">search</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+/</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">shortcuts</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">F10</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">menu</span></pre>\ No newline at end of file
A e2e/testdata/screens/notebook-nu-highlight-high-contrast.html +30 -0
@@ -0,0 +1,30 @@
1+<pre class="screen"><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">F</span><span style="color:#ffffff;background:#1f1f1f">ile</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">E</span><span style="color:#ffffff;background:#1f1f1f">dit</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">V</span><span style="color:#ffffff;background:#1f1f1f">iew</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">I</span><span style="color:#ffffff;background:#1f1f1f">nsert</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f">F</span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">o</span><span style="color:#ffffff;background:#1f1f1f">rmat</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">D</span><span style="color:#ffffff;background:#1f1f1f">ata</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">H</span><span style="color:#ffffff;background:#1f1f1f">elp</span><span style="background:#1f1f1f"> </span><span style="background:#40e0ff"> </span><span style="color:#000000;background:#40e0ff;font-weight:bold">EDIT</span><span style="background:#40e0ff"> </span>2+<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">cell</span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">4</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">a</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">nushell</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">pipeline</span><span style="background:#000000"> </span>3+<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">Shift+Enter</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">run</span><span style="background:#000000"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">Ctrl+Enter</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">run</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">here</span><span style="background:#000000"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">Tab</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">complete</span><span style="background:#000000"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">Esc</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">done</span>4+<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">Notebook</span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">4</span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">cells</span><span style="background:#3a3a3a"> </span>5+<span style="background:#000000"> </span><span style="color:#959595;background:#000000">Markdown</span><span style="background:#000000"> </span>6+<span style="color:#757575;background:#000000">│</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold;text-decoration:underline">Files</span><span style="background:#000000"> </span>7+<span style="color:#757575;background:#000000">│</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">What&#39;s</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">big</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">in</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">the</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">repo,</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">from</span><span style="background:#000000"> </span><span style="color:#40e0ff;background:#000000">ls</span><span style="color:#ffffff;background:#000000">.</span><span style="background:#000000"> </span>8+<span style="background:#000000"> </span>9+<span style="background:#000000"> </span><span style="color:#959595;background:#000000">[1]</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">files</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">✓</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">&lt;1s</span><span style="background:#000000"> </span>10+<span style="color:#757575;background:#000000">│</span><span style="background:#000000"> </span><span style="color:#ff80ff;background:#000000">files</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold">=</span><span style="background:#000000"> </span><span style="color:#40e0ff;background:#000000">ls</span><span style="background:#000000"> </span>11+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold;text-decoration:underline">name</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold;text-decoration:underline">type</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold;text-decoration:underline">size</span><span style="background:#000000"> </span>12+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">012</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">4.2</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>13+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">CLAUDE.md</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">1.0</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>14+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">README.md</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">9.8</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>15+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">demos</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">dir</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">3.1</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>16+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">go.mod</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">812</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">B</span><span style="background:#000000"> </span>17+<span style="background:#000000"> </span>18+<span style="background:#000000"> </span><span style="color:#959595;background:#000000">[2]</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">✓</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">&lt;1s</span><span style="background:#000000"> </span>19+<span style="color:#757575;background:#000000">│</span><span style="background:#000000"> </span><span style="color:#ff80ff;background:#000000">$files</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold">|</span><span style="background:#000000"> </span><span style="color:#40e0ff;background:#000000">where</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">size</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold">&gt;</span><span style="background:#000000"> </span><span style="color:#ffe040;background:#000000">1kb</span><span style="background:#000000"> </span>20+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold;text-decoration:underline">name</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold;text-decoration:underline">type</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold;text-decoration:underline">size</span><span style="background:#000000"> </span>21+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">012</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">4.2</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>22+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">README.md</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">9.8</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>23+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">demos</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">dir</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">3.1</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>24+<span style="background:#000000"> </span>25+<span style="background:#40e0ff"> </span><span style="color:#000000;background:#40e0ff">[</span><span style="background:#40e0ff"> </span><span style="color:#000000;background:#40e0ff">]</span><span style="background:#40e0ff"> </span><span style="color:#000000;background:#40e0ff">not</span><span style="background:#40e0ff"> </span><span style="color:#000000;background:#40e0ff">run</span><span style="background:#40e0ff"> </span><span style="background:#000000"> </span>26+<span style="color:#757575;background:#000000">│</span><span style="background:#000000"> </span><span style="color:#ff80ff;background:#000000">big</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold">=</span><span style="background:#000000"> </span><span style="color:#ff80ff;background:#000000">$files</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold">|</span><span style="background:#000000"> </span><span style="color:#40e0ff;background:#000000">where</span><span style="background:#000000"> </span><span style="color:#ff80ff;background:#000000">size</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold">&gt;</span><span style="background:#000000"> </span><span style="color:#ffe040;background:#000000">1kb</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold">|</span><span style="background:#000000"> </span><span style="color:#40e0ff;background:#000000">sort-by</span><span style="background:#000000"> </span><span style="color:#40e040;background:#000000">size</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">--reverse</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000;font-style:italic">#</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000;font-style:italic">largest</span><span style="background:#000000"> </span>27+<span style="background:#000000"> </span>28+<span style="background:#000000"> </span>29+<span style="background:#000000"> </span>30+<span style="background:#40e0ff"> </span><span style="color:#000000;background:#40e0ff;font-weight:bold">❯</span><span style="background:#40e0ff"> </span><span style="color:#000000;background:#40e0ff;font-weight:bold">Notebook</span><span style="background:#40e0ff"> </span><span style="background:#1f1f1f"> </span><span style="color:#959595;background:#1f1f1f">+</span><span style="background:#1f1f1f"> </span><span style="color:#757575;background:#1f1f1f">│</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f">untitled</span><span style="background:#1f1f1f"> </span><span style="color:#959595;background:#1f1f1f">modified</span><span style="background:#1f1f1f"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">Ctrl+K</span><span style="background:#3a3a3a"> </span><span style="background:#1f1f1f"> </span><span style="color:#959595;background:#1f1f1f">search</span><span style="background:#1f1f1f"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">Ctrl+/</span><span style="background:#3a3a3a"> </span><span style="background:#1f1f1f"> </span><span style="color:#959595;background:#1f1f1f">shortcuts</span><span style="background:#1f1f1f"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">F10</span><span style="background:#3a3a3a"> </span><span style="background:#1f1f1f"> </span><span style="color:#959595;background:#1f1f1f">menu</span></pre>\ No newline at end of file
A e2e/testdata/screens/notebook-nu-highlight-light.html +30 -0
@@ -0,0 +1,30 @@
1+<pre class="screen"> <span style="text-decoration:underline">F</span>ile <span style="text-decoration:underline">E</span>dit <span style="text-decoration:underline">V</span>iew <span style="text-decoration:underline">I</span>nsert F<span style="text-decoration:underline">o</span>rmat <span style="text-decoration:underline">D</span>ata <span style="text-decoration:underline">H</span>elp <span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">EDIT</span><span style="background:var(--vt-palette-6)"> </span>2+<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">cell</span><span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">4</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">a</span> <span style="color:var(--vt-palette-0)">nushell</span> <span style="color:var(--vt-palette-0)">pipeline</span>3+<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">Shift+Enter</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">run</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">Ctrl+Enter</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">run</span> <span style="color:var(--vt-palette-0)">here</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">Tab</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">complete</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">Esc</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">done</span>4+ Notebook 4 cells5+ <span style="color:var(--vt-palette-0)">Markdown</span>6+<span style="color:var(--vt-palette-8)">│</span> <span style="font-weight:bold;text-decoration:underline">Files</span>7+<span style="color:var(--vt-palette-8)">│</span> What&#39;s big in the repo, from <span style="color:var(--vt-palette-4)">ls</span>.8+9+ <span style="color:var(--vt-palette-0)">[1]</span> <span style="color:var(--vt-palette-0)">files</span> <span style="color:var(--vt-palette-0)">✓</span> <span style="color:var(--vt-palette-0)">&lt;1s</span>10+<span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-5)">files</span> <span style="font-weight:bold">=</span> <span style="color:var(--vt-palette-4)">ls</span>11+ <span style="font-weight:bold;text-decoration:underline">name</span> <span style="font-weight:bold;text-decoration:underline">type</span> <span style="font-weight:bold;text-decoration:underline">size</span>12+ 012 file 4.2 kB13+ CLAUDE.md file 1.0 kB14+ README.md file 9.8 kB15+ demos dir 3.1 kB16+ go.mod file 812 B17+18+ <span style="color:var(--vt-palette-0)">[2]</span> <span style="color:var(--vt-palette-0)">✓</span> <span style="color:var(--vt-palette-0)">&lt;1s</span>19+<span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-5)">$files</span> <span style="font-weight:bold">|</span> <span style="color:var(--vt-palette-4)">where</span> size <span style="font-weight:bold">&gt;</span> <span style="color:var(--vt-palette-1)">1kb</span>20+ <span style="font-weight:bold;text-decoration:underline">name</span> <span style="font-weight:bold;text-decoration:underline">type</span> <span style="font-weight:bold;text-decoration:underline">size</span>21+ 012 file 4.2 kB22+ README.md file 9.8 kB23+ demos dir 3.1 kB24+25+<span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6)">[</span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6)">]</span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6)">not</span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6)">run</span><span style="background:var(--vt-palette-6)"> </span>26+<span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-5)">big</span> <span style="font-weight:bold">=</span> <span style="color:var(--vt-palette-5)">$files</span> <span style="font-weight:bold">|</span> <span style="color:var(--vt-palette-4)">where</span> <span style="color:var(--vt-palette-5)">size</span> <span style="font-weight:bold">&gt;</span> <span style="color:var(--vt-palette-1)">1kb</span> <span style="font-weight:bold">|</span> <span style="color:var(--vt-palette-4)">sort-by</span> <span style="color:var(--vt-palette-2)">size</span> --reverse <span style="color:var(--vt-palette-0);font-style:italic">#</span> <span style="color:var(--vt-palette-0);font-style:italic">largest</span>27+28+29+30+<span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">❯</span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">Notebook</span><span style="background:var(--vt-palette-6)"> </span> <span style="color:var(--vt-palette-0)">+</span> <span style="color:var(--vt-palette-8)">│</span> untitled <span style="color:var(--vt-palette-0)">modified</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">Ctrl+K</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">search</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">Ctrl+/</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">shortcuts</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">F10</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">menu</span></pre>\ No newline at end of file
A e2e/testdata/screens/notebook-nu-highlight.html +30 -0
@@ -0,0 +1,30 @@
1+<pre class="screen"> <span style="text-decoration:underline">F</span>ile <span style="text-decoration:underline">E</span>dit <span style="text-decoration:underline">V</span>iew <span style="text-decoration:underline">I</span>nsert F<span style="text-decoration:underline">o</span>rmat <span style="text-decoration:underline">D</span>ata <span style="text-decoration:underline">H</span>elp <span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">EDIT</span><span style="background:var(--vt-palette-6)"> </span>2+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">cell</span><span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">4</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">a</span> <span style="color:var(--vt-palette-8)">nushell</span> <span style="color:var(--vt-palette-8)">pipeline</span>3+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Shift+Enter</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">run</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+Enter</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">run</span> <span style="color:var(--vt-palette-8)">here</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Tab</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">complete</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Esc</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">done</span>4+ Notebook 4 cells5+ <span style="color:var(--vt-palette-8)">Markdown</span>6+<span style="color:var(--vt-palette-8)">│</span> <span style="font-weight:bold;text-decoration:underline">Files</span>7+<span style="color:var(--vt-palette-8)">│</span> What&#39;s big in the repo, from <span style="color:var(--vt-palette-6)">ls</span>.8+9+ <span style="color:var(--vt-palette-8)">[1]</span> <span style="color:var(--vt-palette-8)">files</span> <span style="color:var(--vt-palette-8)">✓</span> <span style="color:var(--vt-palette-8)">&lt;1s</span>10+<span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-5)">files</span> <span style="font-weight:bold">=</span> <span style="color:var(--vt-palette-6)">ls</span>11+ <span style="font-weight:bold;text-decoration:underline">name</span> <span style="font-weight:bold;text-decoration:underline">type</span> <span style="font-weight:bold;text-decoration:underline">size</span>12+ 012 file 4.2 kB13+ CLAUDE.md file 1.0 kB14+ README.md file 9.8 kB15+ demos dir 3.1 kB16+ go.mod file 812 B17+18+ <span style="color:var(--vt-palette-8)">[2]</span> <span style="color:var(--vt-palette-8)">✓</span> <span style="color:var(--vt-palette-8)">&lt;1s</span>19+<span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-5)">$files</span> <span style="font-weight:bold">|</span> <span style="color:var(--vt-palette-6)">where</span> size <span style="font-weight:bold">&gt;</span> <span style="color:var(--vt-palette-3)">1kb</span>20+ <span style="font-weight:bold;text-decoration:underline">name</span> <span style="font-weight:bold;text-decoration:underline">type</span> <span style="font-weight:bold;text-decoration:underline">size</span>21+ 012 file 4.2 kB22+ README.md file 9.8 kB23+ demos dir 3.1 kB24+25+<span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6)">[</span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6)">]</span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6)">not</span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6)">run</span><span style="background:var(--vt-palette-6)"> </span>26+<span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-5)">big</span> <span style="font-weight:bold">=</span> <span style="color:var(--vt-palette-5)">$files</span> <span style="font-weight:bold">|</span> <span style="color:var(--vt-palette-6)">where</span> <span style="color:var(--vt-palette-5)">size</span> <span style="font-weight:bold">&gt;</span> <span style="color:var(--vt-palette-3)">1kb</span> <span style="font-weight:bold">|</span> <span style="color:var(--vt-palette-6)">sort-by</span> <span style="color:var(--vt-palette-2)">size</span> --reverse <span style="color:var(--vt-palette-8);font-style:italic">#</span> <span style="color:var(--vt-palette-8);font-style:italic">largest</span>27+28+29+30+<span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">❯</span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">Notebook</span><span style="background:var(--vt-palette-6)"> </span> <span style="color:var(--vt-palette-8)">+</span> <span style="color:var(--vt-palette-8)">│</span> untitled <span style="color:var(--vt-palette-8)">modified</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+K</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">search</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+/</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">shortcuts</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">F10</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">menu</span></pre>\ No newline at end of file
A e2e/testdata/screens/notebook-output-high-contrast.html +30 -0
@@ -0,0 +1,30 @@
1+<pre class="screen"><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">F</span><span style="color:#ffffff;background:#1f1f1f">ile</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">E</span><span style="color:#ffffff;background:#1f1f1f">dit</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">V</span><span style="color:#ffffff;background:#1f1f1f">iew</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">I</span><span style="color:#ffffff;background:#1f1f1f">nsert</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f">F</span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">o</span><span style="color:#ffffff;background:#1f1f1f">rmat</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">D</span><span style="color:#ffffff;background:#1f1f1f">ata</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">H</span><span style="color:#ffffff;background:#1f1f1f">elp</span><span style="background:#1f1f1f"> </span><span style="background:#40e0ff"> </span><span style="color:#000000;background:#40e0ff;font-weight:bold">OUTPUT</span><span style="background:#40e0ff"> </span>2+<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">files</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">a</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">table:</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">5</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">rows,</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">3</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">columns</span><span style="background:#000000"> </span>3+<span style="color:#959595;background:#000000">files</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">[1]</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">5</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">rows</span><span style="background:#000000"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">s</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">sort</span><span style="background:#000000"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">S</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">descending</span><span style="background:#000000"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">/</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">filter</span><span style="background:#000000"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">Esc</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">back</span>4+<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">files</span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">[1],</span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">full-screen</span><span style="background:#3a3a3a"> </span>5+<span style="background:#3a3a3a"> </span><span style="color:#000000;background:#40e0ff;font-weight:bold">name</span><span style="background:#40e0ff"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">type</span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">size</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span>6+<span style="background:#40e0ff"> </span><span style="color:#000000;background:#40e0ff;font-weight:bold">1</span><span style="background:#000000"> </span><span style="color:#000000;background:#40e0ff">012</span><span style="background:#40e0ff"> </span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">4.2</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>7+<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">2</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">CLAUDE.md</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">1.0</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>8+<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">3</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">README.md</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">9.8</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>9+<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">4</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">demos</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">dir</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">3.1</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>10+<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">5</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">go.mod</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">812</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">B</span><span style="background:#000000"> </span>11+<span style="background:#000000"> </span>12+<span style="background:#000000"> </span>13+<span style="background:#000000"> </span>14+<span style="background:#000000"> </span>15+<span style="background:#000000"> </span>16+<span style="background:#000000"> </span>17+<span style="background:#000000"> </span>18+<span style="background:#000000"> </span>19+<span style="background:#000000"> </span>20+<span style="background:#000000"> </span>21+<span style="background:#000000"> </span>22+<span style="background:#000000"> </span>23+<span style="background:#000000"> </span>24+<span style="background:#000000"> </span>25+<span style="background:#000000"> </span>26+<span style="background:#000000"> </span>27+<span style="background:#000000"> </span>28+<span style="background:#000000"> </span>29+<span style="background:#000000"> </span>30+<span style="background:#40e0ff"> </span><span style="color:#000000;background:#40e0ff;font-weight:bold">❯</span><span style="background:#40e0ff"> </span><span style="color:#000000;background:#40e0ff;font-weight:bold">Notebook</span><span style="background:#40e0ff"> </span><span style="background:#1f1f1f"> </span><span style="color:#959595;background:#1f1f1f">+</span><span style="background:#1f1f1f"> </span><span style="color:#757575;background:#1f1f1f">│</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f">untitled</span><span style="background:#1f1f1f"> </span><span style="color:#959595;background:#1f1f1f">modified</span><span style="background:#1f1f1f"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">Ctrl+K</span><span style="background:#3a3a3a"> </span><span style="background:#1f1f1f"> </span><span style="color:#959595;background:#1f1f1f">search</span><span style="background:#1f1f1f"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">Ctrl+/</span><span style="background:#3a3a3a"> </span><span style="background:#1f1f1f"> </span><span style="color:#959595;background:#1f1f1f">shortcuts</span><span style="background:#1f1f1f"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">F10</span><span style="background:#3a3a3a"> </span><span style="background:#1f1f1f"> </span><span style="color:#959595;background:#1f1f1f">menu</span></pre>\ No newline at end of file
A e2e/testdata/screens/notebook-output-light.html +30 -0
@@ -0,0 +1,30 @@
1+<pre class="screen"> <span style="text-decoration:underline">F</span>ile <span style="text-decoration:underline">E</span>dit <span style="text-decoration:underline">V</span>iew <span style="text-decoration:underline">I</span>nsert F<span style="text-decoration:underline">o</span>rmat <span style="text-decoration:underline">D</span>ata <span style="text-decoration:underline">H</span>elp <span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">OUTPUT</span><span style="background:var(--vt-palette-6)"> </span>2+<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">files</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">a</span> <span style="color:var(--vt-palette-0)">table:</span> <span style="color:var(--vt-palette-0)">5</span> <span style="color:var(--vt-palette-0)">rows,</span> <span style="color:var(--vt-palette-0)">3</span> <span style="color:var(--vt-palette-0)">columns</span>3+<span style="color:var(--vt-palette-0)">files</span> <span style="color:var(--vt-palette-0)">[1]</span> <span style="color:var(--vt-palette-0)">5</span> <span style="color:var(--vt-palette-0)">rows</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">s</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">sort</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">S</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">descending</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">/</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">filter</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">Esc</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">back</span>4+ files [1], full-screen5+<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">name</span><span style="background:var(--vt-palette-6)"> </span><span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">type</span><span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">size</span><span style="background:var(--vt-palette-7)"> </span>6+<span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">1</span> <span style="color:var(--vt-palette-0);background:var(--vt-palette-6)">012</span><span style="background:var(--vt-palette-6)"> </span> file 4.2 kB7+<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">2</span> CLAUDE.md file 1.0 kB8+<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">3</span> README.md file 9.8 kB9+<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">4</span> demos dir 3.1 kB10+<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">5</span> go.mod file 812 B11+12+13+14+15+16+17+18+19+20+21+22+23+24+25+26+27+28+29+30+<span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">❯</span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">Notebook</span><span style="background:var(--vt-palette-6)"> </span> <span style="color:var(--vt-palette-0)">+</span> <span style="color:var(--vt-palette-8)">│</span> untitled <span style="color:var(--vt-palette-0)">modified</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">Ctrl+K</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">search</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">Ctrl+/</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">shortcuts</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">F10</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">menu</span></pre>\ No newline at end of file
A e2e/testdata/screens/notebook-output.html +30 -0
@@ -0,0 +1,30 @@
1+<pre class="screen"> <span style="text-decoration:underline">F</span>ile <span style="text-decoration:underline">E</span>dit <span style="text-decoration:underline">V</span>iew <span style="text-decoration:underline">I</span>nsert F<span style="text-decoration:underline">o</span>rmat <span style="text-decoration:underline">D</span>ata <span style="text-decoration:underline">H</span>elp <span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">OUTPUT</span><span style="background:var(--vt-palette-6)"> </span>2+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">files</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">a</span> <span style="color:var(--vt-palette-8)">table:</span> <span style="color:var(--vt-palette-8)">5</span> <span style="color:var(--vt-palette-8)">rows,</span> <span style="color:var(--vt-palette-8)">3</span> <span style="color:var(--vt-palette-8)">columns</span>3+<span style="color:var(--vt-palette-8)">files</span> <span style="color:var(--vt-palette-8)">[1]</span> <span style="color:var(--vt-palette-8)">5</span> <span style="color:var(--vt-palette-8)">rows</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">s</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">sort</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">S</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">descending</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">/</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">filter</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Esc</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">back</span>4+ files [1], full-screen5+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">name</span><span style="background:var(--vt-palette-6)"> </span><span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">type</span><span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">size</span><span style="background:var(--vt-palette-8)"> </span>6+<span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">1</span> <span style="color:var(--vt-palette-0);background:var(--vt-palette-6)">012</span><span style="background:var(--vt-palette-6)"> </span> file 4.2 kB7+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">2</span> CLAUDE.md file 1.0 kB8+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">3</span> README.md file 9.8 kB9+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">4</span> demos dir 3.1 kB10+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">5</span> go.mod file 812 B11+12+13+14+15+16+17+18+19+20+21+22+23+24+25+26+27+28+29+30+<span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">❯</span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">Notebook</span><span style="background:var(--vt-palette-6)"> </span> <span style="color:var(--vt-palette-8)">+</span> <span style="color:var(--vt-palette-8)">│</span> untitled <span style="color:var(--vt-palette-8)">modified</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+K</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">search</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+/</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">shortcuts</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">F10</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">menu</span></pre>\ No newline at end of file
D e2e/testdata/screens/notebook-regions-high-contrast.html +0 -30
@@ -1,30 +0,0 @@
1-<pre class="screen"><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">F</span><span style="color:#ffffff;background:#1f1f1f">ile</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">E</span><span style="color:#ffffff;background:#1f1f1f">dit</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">V</span><span style="color:#ffffff;background:#1f1f1f">iew</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">I</span><span style="color:#ffffff;background:#1f1f1f">nsert</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f">F</span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">o</span><span style="color:#ffffff;background:#1f1f1f">rmat</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">D</span><span style="color:#ffffff;background:#1f1f1f">ata</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">H</span><span style="color:#ffffff;background:#1f1f1f">elp</span><span style="background:#1f1f1f"> </span><span style="background:#40e0ff"> </span><span style="color:#000000;background:#40e0ff;font-weight:bold">READY</span><span style="background:#40e0ff"> </span>2-<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">C11</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">nu❯</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">$r1</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">|</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">where</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">size</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">&gt;</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">1kb</span><span style="background:#000000"> </span>3-<span style="color:#959595;background:#000000">Region</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">big</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">of</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">$r1</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">|</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">where</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">size</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">&gt;</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">1kb</span><span style="background:#000000"> </span>4-<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">A</span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">B</span><span style="background:#3a3a3a"> </span><span style="background:#40e0ff"> </span><span style="color:#000000;background:#40e0ff;font-weight:bold">C</span><span style="background:#40e0ff"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">D</span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">E</span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">F</span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">G</span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">H</span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">I</span><span style="background:#3a3a3a"> </span>5-<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">1</span><span style="background:#3a3a3a"> </span><span style="background:#000000;text-decoration:underline"> </span><span style="color:#959595;background:#000000;text-decoration:underline">r1</span><span style="background:#000000;text-decoration:underline"> </span><span style="color:#959595;background:#000000;text-decoration:underline">ls</span><span style="background:#000000;text-decoration:underline"> </span><span style="background:#000000"> </span>6-<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">2</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">name</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">type</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">size</span><span style="background:#000000"> </span>7-<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">3</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">012</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">4.2</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>8-<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">4</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">CLAUDE.md</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">1.0</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>9-<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">5</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">README.md</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">9.8</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>10-<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">6</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">demos</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">dir</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">3.1</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>11-<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">7</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">go.mod</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">812</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">B</span><span style="background:#000000"> </span>12-<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">8</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span>13-<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">9</span><span style="background:#3a3a3a"> </span><span style="background:#000000;text-decoration:underline"> </span><span style="color:#959595;background:#000000;text-decoration:underline">big</span><span style="background:#000000;text-decoration:underline"> </span><span style="color:#959595;background:#000000;text-decoration:underline">$r1</span><span style="background:#000000;text-decoration:underline"> </span><span style="color:#959595;background:#000000;text-decoration:underline">|</span><span style="background:#000000;text-decoration:underline"> </span><span style="color:#959595;background:#000000;text-decoration:underline">where</span><span style="background:#000000;text-decoration:underline"> </span><span style="color:#959595;background:#000000;text-decoration:underline">size</span><span style="background:#000000;text-decoration:underline"> </span><span style="color:#959595;background:#000000;text-decoration:underline">&gt;</span><span style="background:#000000;text-decoration:underline"> </span><span style="color:#959595;background:#000000;text-decoration:underline">1kb</span><span style="background:#000000;text-decoration:underline"> </span><span style="background:#000000"> </span>14-<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">10</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">name</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">type</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">size</span><span style="background:#000000"> </span>15-<span style="background:#40e0ff"> </span><span style="color:#000000;background:#40e0ff;font-weight:bold">11</span><span style="background:#40e0ff"> </span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">012</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="background:#40e0ff"> </span><span style="color:#000000;background:#40e0ff">4.2</span><span style="background:#40e0ff"> </span><span style="color:#000000;background:#40e0ff">kB</span><span style="background:#40e0ff"> </span><span style="background:#000000"> </span>16-<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">12</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">README.md</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">9.8</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>17-<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">13</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">demos</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">dir</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">3.1</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>18-<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">14</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span>19-<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">15</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span>20-<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">16</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span>21-<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">17</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span>22-<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">18</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span>23-<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">19</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span>24-<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">20</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span>25-<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">21</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span>26-<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">22</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span>27-<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">23</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span>28-<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">24</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span>29-<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">25</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span>30-<span style="background:#40e0ff"> </span><span style="color:#000000;background:#40e0ff;font-weight:bold">Shell</span><span style="background:#40e0ff"> </span><span style="color:#000000;background:#40e0ff;font-weight:bold">1</span><span style="background:#40e0ff"> </span><span style="background:#1f1f1f"> </span><span style="color:#959595;background:#1f1f1f">+</span><span style="background:#1f1f1f"> </span><span style="color:#757575;background:#1f1f1f">│</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f">untitled</span><span style="background:#1f1f1f"> </span><span style="color:#959595;background:#1f1f1f">modified</span><span style="background:#1f1f1f"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">Ctrl+K</span><span style="background:#3a3a3a"> </span><span style="background:#1f1f1f"> </span><span style="color:#959595;background:#1f1f1f">search</span><span style="background:#1f1f1f"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">Ctrl+/</span><span style="background:#3a3a3a"> </span><span style="background:#1f1f1f"> </span><span style="color:#959595;background:#1f1f1f">shortcuts</span><span style="background:#1f1f1f"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">F10</span><span style="background:#3a3a3a"> </span><span style="background:#1f1f1f"> </span><span style="color:#959595;background:#1f1f1f">menu</span></pre>\ No newline at end of file
D e2e/testdata/screens/notebook-regions-light.html +0 -30
@@ -1,30 +0,0 @@
1-<pre class="screen"> <span style="text-decoration:underline">F</span>ile <span style="text-decoration:underline">E</span>dit <span style="text-decoration:underline">V</span>iew <span style="text-decoration:underline">I</span>nsert F<span style="text-decoration:underline">o</span>rmat <span style="text-decoration:underline">D</span>ata <span style="text-decoration:underline">H</span>elp <span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">READY</span><span style="background:var(--vt-palette-6)"> </span>2-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">C11</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">nu❯</span> <span style="color:var(--vt-palette-0)">$r1</span> <span style="color:var(--vt-palette-0)">|</span> <span style="color:var(--vt-palette-0)">where</span> <span style="color:var(--vt-palette-0)">size</span> <span style="color:var(--vt-palette-0)">&gt;</span> <span style="color:var(--vt-palette-0)">1kb</span>3-<span style="color:var(--vt-palette-0)">Region</span> <span style="color:var(--vt-palette-0)">big</span> <span style="color:var(--vt-palette-0)">of</span> <span style="color:var(--vt-palette-0)">$r1</span> <span style="color:var(--vt-palette-0)">|</span> <span style="color:var(--vt-palette-0)">where</span> <span style="color:var(--vt-palette-0)">size</span> <span style="color:var(--vt-palette-0)">&gt;</span> <span style="color:var(--vt-palette-0)">1kb</span>4-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">A</span><span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">B</span><span style="background:var(--vt-palette-7)"> </span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">C</span><span style="background:var(--vt-palette-6)"> </span><span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">D</span><span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">E</span><span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">F</span><span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">G</span><span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">H</span><span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">I</span><span style="background:var(--vt-palette-7)"> </span>5-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">1</span><span style="background:var(--vt-palette-7)"> </span><span style="text-decoration:underline"> </span><span style="color:var(--vt-palette-0);text-decoration:underline">r1</span><span style="text-decoration:underline"> </span><span style="color:var(--vt-palette-0);text-decoration:underline">ls</span><span style="text-decoration:underline"> </span>6-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">2</span><span style="background:var(--vt-palette-7)"> </span> name type size7-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">3</span><span style="background:var(--vt-palette-7)"> </span> 012 file 4.2 kB8-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">4</span><span style="background:var(--vt-palette-7)"> </span> CLAUDE.md file 1.0 kB9-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">5</span><span style="background:var(--vt-palette-7)"> </span> README.md file 9.8 kB10-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">6</span><span style="background:var(--vt-palette-7)"> </span> demos dir 3.1 kB11-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">7</span><span style="background:var(--vt-palette-7)"> </span> go.mod file 812 B12-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">8</span><span style="background:var(--vt-palette-7)"> </span>13-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">9</span><span style="background:var(--vt-palette-7)"> </span><span style="text-decoration:underline"> </span><span style="color:var(--vt-palette-0);text-decoration:underline">big</span><span style="text-decoration:underline"> </span><span style="color:var(--vt-palette-0);text-decoration:underline">$r1</span><span style="text-decoration:underline"> </span><span style="color:var(--vt-palette-0);text-decoration:underline">|</span><span style="text-decoration:underline"> </span><span style="color:var(--vt-palette-0);text-decoration:underline">where</span><span style="text-decoration:underline"> </span><span style="color:var(--vt-palette-0);text-decoration:underline">size</span><span style="text-decoration:underline"> </span><span style="color:var(--vt-palette-0);text-decoration:underline">&gt;</span><span style="text-decoration:underline"> </span><span style="color:var(--vt-palette-0);text-decoration:underline">1kb</span><span style="text-decoration:underline"> </span>14-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">10</span><span style="background:var(--vt-palette-7)"> </span> name type size15-<span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">11</span><span style="background:var(--vt-palette-6)"> </span> 012 file <span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6)">4.2</span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6)">kB</span><span style="background:var(--vt-palette-6)"> </span>16-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">12</span><span style="background:var(--vt-palette-7)"> </span> README.md file 9.8 kB17-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">13</span><span style="background:var(--vt-palette-7)"> </span> demos dir 3.1 kB18-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">14</span><span style="background:var(--vt-palette-7)"> </span>19-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">15</span><span style="background:var(--vt-palette-7)"> </span>20-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">16</span><span style="background:var(--vt-palette-7)"> </span>21-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">17</span><span style="background:var(--vt-palette-7)"> </span>22-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">18</span><span style="background:var(--vt-palette-7)"> </span>23-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">19</span><span style="background:var(--vt-palette-7)"> </span>24-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">20</span><span style="background:var(--vt-palette-7)"> </span>25-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">21</span><span style="background:var(--vt-palette-7)"> </span>26-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">22</span><span style="background:var(--vt-palette-7)"> </span>27-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">23</span><span style="background:var(--vt-palette-7)"> </span>28-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">24</span><span style="background:var(--vt-palette-7)"> </span>29-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">25</span><span style="background:var(--vt-palette-7)"> </span>30-<span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">Shell</span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">1</span><span style="background:var(--vt-palette-6)"> </span> <span style="color:var(--vt-palette-0)">+</span> <span style="color:var(--vt-palette-8)">│</span> untitled <span style="color:var(--vt-palette-0)">modified</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">Ctrl+K</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">search</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">Ctrl+/</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">shortcuts</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">F10</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">menu</span></pre>\ No newline at end of file
D e2e/testdata/screens/notebook-regions.html +0 -30
@@ -1,30 +0,0 @@
1-<pre class="screen"> <span style="text-decoration:underline">F</span>ile <span style="text-decoration:underline">E</span>dit <span style="text-decoration:underline">V</span>iew <span style="text-decoration:underline">I</span>nsert F<span style="text-decoration:underline">o</span>rmat <span style="text-decoration:underline">D</span>ata <span style="text-decoration:underline">H</span>elp <span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">READY</span><span style="background:var(--vt-palette-6)"> </span>2-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">C11</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">nu❯</span> <span style="color:var(--vt-palette-8)">$r1</span> <span style="color:var(--vt-palette-8)">|</span> <span style="color:var(--vt-palette-8)">where</span> <span style="color:var(--vt-palette-8)">size</span> <span style="color:var(--vt-palette-8)">&gt;</span> <span style="color:var(--vt-palette-8)">1kb</span>3-<span style="color:var(--vt-palette-8)">Region</span> <span style="color:var(--vt-palette-8)">big</span> <span style="color:var(--vt-palette-8)">of</span> <span style="color:var(--vt-palette-8)">$r1</span> <span style="color:var(--vt-palette-8)">|</span> <span style="color:var(--vt-palette-8)">where</span> <span style="color:var(--vt-palette-8)">size</span> <span style="color:var(--vt-palette-8)">&gt;</span> <span style="color:var(--vt-palette-8)">1kb</span>4-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">A</span><span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">B</span><span style="background:var(--vt-palette-8)"> </span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">C</span><span style="background:var(--vt-palette-6)"> </span><span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">D</span><span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">E</span><span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">F</span><span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">G</span><span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">H</span><span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">I</span><span style="background:var(--vt-palette-8)"> </span>5-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">1</span><span style="background:var(--vt-palette-8)"> </span><span style="text-decoration:underline"> </span><span style="color:var(--vt-palette-8);text-decoration:underline">r1</span><span style="text-decoration:underline"> </span><span style="color:var(--vt-palette-8);text-decoration:underline">ls</span><span style="text-decoration:underline"> </span>6-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">2</span><span style="background:var(--vt-palette-8)"> </span> name type size7-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">3</span><span style="background:var(--vt-palette-8)"> </span> 012 file 4.2 kB8-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">4</span><span style="background:var(--vt-palette-8)"> </span> CLAUDE.md file 1.0 kB9-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">5</span><span style="background:var(--vt-palette-8)"> </span> README.md file 9.8 kB10-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">6</span><span style="background:var(--vt-palette-8)"> </span> demos dir 3.1 kB11-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">7</span><span style="background:var(--vt-palette-8)"> </span> go.mod file 812 B12-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">8</span><span style="background:var(--vt-palette-8)"> </span>13-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">9</span><span style="background:var(--vt-palette-8)"> </span><span style="text-decoration:underline"> </span><span style="color:var(--vt-palette-8);text-decoration:underline">big</span><span style="text-decoration:underline"> </span><span style="color:var(--vt-palette-8);text-decoration:underline">$r1</span><span style="text-decoration:underline"> </span><span style="color:var(--vt-palette-8);text-decoration:underline">|</span><span style="text-decoration:underline"> </span><span style="color:var(--vt-palette-8);text-decoration:underline">where</span><span style="text-decoration:underline"> </span><span style="color:var(--vt-palette-8);text-decoration:underline">size</span><span style="text-decoration:underline"> </span><span style="color:var(--vt-palette-8);text-decoration:underline">&gt;</span><span style="text-decoration:underline"> </span><span style="color:var(--vt-palette-8);text-decoration:underline">1kb</span><span style="text-decoration:underline"> </span>14-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">10</span><span style="background:var(--vt-palette-8)"> </span> name type size15-<span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">11</span><span style="background:var(--vt-palette-6)"> </span> 012 file <span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6)">4.2</span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6)">kB</span><span style="background:var(--vt-palette-6)"> </span>16-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">12</span><span style="background:var(--vt-palette-8)"> </span> README.md file 9.8 kB17-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">13</span><span style="background:var(--vt-palette-8)"> </span> demos dir 3.1 kB18-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">14</span><span style="background:var(--vt-palette-8)"> </span>19-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">15</span><span style="background:var(--vt-palette-8)"> </span>20-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">16</span><span style="background:var(--vt-palette-8)"> </span>21-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">17</span><span style="background:var(--vt-palette-8)"> </span>22-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">18</span><span style="background:var(--vt-palette-8)"> </span>23-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">19</span><span style="background:var(--vt-palette-8)"> </span>24-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">20</span><span style="background:var(--vt-palette-8)"> </span>25-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">21</span><span style="background:var(--vt-palette-8)"> </span>26-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">22</span><span style="background:var(--vt-palette-8)"> </span>27-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">23</span><span style="background:var(--vt-palette-8)"> </span>28-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">24</span><span style="background:var(--vt-palette-8)"> </span>29-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">25</span><span style="background:var(--vt-palette-8)"> </span>30-<span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">Shell</span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">1</span><span style="background:var(--vt-palette-6)"> </span> <span style="color:var(--vt-palette-8)">+</span> <span style="color:var(--vt-palette-8)">│</span> untitled <span style="color:var(--vt-palette-8)">modified</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+K</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">search</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+/</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">shortcuts</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">F10</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">menu</span></pre>\ No newline at end of file
A e2e/testdata/screens/notebook-running-high-contrast.html +30 -0
@@ -0,0 +1,30 @@
1+<pre class="screen"><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">F</span><span style="color:#ffffff;background:#1f1f1f">ile</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">E</span><span style="color:#ffffff;background:#1f1f1f">dit</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">V</span><span style="color:#ffffff;background:#1f1f1f">iew</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">I</span><span style="color:#ffffff;background:#1f1f1f">nsert</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f">F</span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">o</span><span style="color:#ffffff;background:#1f1f1f">rmat</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">D</span><span style="color:#ffffff;background:#1f1f1f">ata</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">H</span><span style="color:#ffffff;background:#1f1f1f">elp</span><span style="background:#1f1f1f"> </span><span style="background:#40e0ff"> </span><span style="color:#000000;background:#40e0ff;font-weight:bold">NOTEBOOK</span><span style="background:#40e0ff"> </span>2+<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">cell</span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">5</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">a</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">nushell</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">pipeline</span><span style="background:#000000"> </span>3+<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">Enter</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">edit</span><span style="background:#000000"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">Shift+Enter</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">run</span><span style="background:#000000"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">a</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">add</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">above</span><span style="background:#000000"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">b</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">below</span><span style="background:#000000"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">dd</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">delete</span><span style="background:#000000"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">G</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">send</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">to</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">a</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">sheet</span><span style="background:#000000"> </span>4+<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">Notebook</span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">5</span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">cells</span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">running</span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">1,</span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">1</span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">waiting</span><span style="background:#3a3a3a"> </span>5+<span style="color:#757575;background:#000000">│</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold;text-decoration:underline">Files</span><span style="background:#000000"> </span>6+<span style="color:#757575;background:#000000">│</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">What&#39;s</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">big</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">in</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">the</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">repo,</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">from</span><span style="background:#000000"> </span><span style="color:#40e0ff;background:#000000">ls</span><span style="color:#ffffff;background:#000000">.</span><span style="background:#000000"> </span>7+<span style="background:#000000"> </span>8+<span style="background:#000000"> </span><span style="color:#959595;background:#000000">[3]</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">files</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">✓</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">&lt;1s</span><span style="background:#000000"> </span>9+<span style="color:#757575;background:#000000">│</span><span style="background:#000000"> </span><span style="color:#ff80ff;background:#000000">files</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold">=</span><span style="background:#000000"> </span><span style="color:#40e0ff;background:#000000">ls</span><span style="background:#000000"> </span>10+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold;text-decoration:underline">name</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold;text-decoration:underline">type</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold;text-decoration:underline">size</span><span style="background:#000000"> </span>11+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">012</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">4.2</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>12+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">CLAUDE.md</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">1.0</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>13+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">README.md</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">9.8</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>14+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">demos</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">dir</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">3.1</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>15+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">go.mod</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">812</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">B</span><span style="background:#000000"> </span>16+<span style="background:#000000"> </span>17+<span style="background:#000000"> </span><span style="color:#959595;background:#000000">[4]</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">✓</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">&lt;1s</span><span style="background:#000000"> </span>18+<span style="color:#757575;background:#000000">│</span><span style="background:#000000"> </span><span style="color:#ff80ff;background:#000000">$files</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold">|</span><span style="background:#000000"> </span><span style="color:#40e0ff;background:#000000">where</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">size</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold">&gt;</span><span style="background:#000000"> </span><span style="color:#ffe040;background:#000000">1kb</span><span style="background:#000000"> </span>19+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold;text-decoration:underline">name</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold;text-decoration:underline">type</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold;text-decoration:underline">size</span><span style="background:#000000"> </span>20+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">012</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">4.2</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>21+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">README.md</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">9.8</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>22+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">demos</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">dir</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">3.1</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>23+<span style="background:#000000"> </span>24+<span style="background:#000000"> </span><span style="color:#959595;background:#000000">[*]</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">running</span><span style="background:#000000"> </span>25+<span style="color:#757575;background:#000000">│</span><span style="background:#000000"> </span><span style="color:#40e0ff;background:#000000">sleep</span><span style="background:#000000"> </span><span style="color:#ffe040;background:#000000">10min</span><span style="background:#000000"> </span>26+<span style="background:#000000"> </span>27+<span style="background:#0033cc"> </span><span style="color:#ffffff;background:#0033cc">[</span><span style="background:#0033cc"> </span><span style="color:#ffffff;background:#0033cc">]</span><span style="background:#0033cc"> </span><span style="color:#ffffff;background:#0033cc">waiting</span><span style="background:#0033cc"> </span><span style="background:#000000"> </span>28+<span style="color:#757575;background:#000000">│</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">{name:</span><span style="background:#000000"> </span><span style="color:#ffe040;background:#000000">1</span><span style="color:#ffffff;background:#000000">}</span><span style="background:#000000"> </span>29+<span style="background:#000000"> </span>30+<span style="background:#40e0ff"> </span><span style="color:#000000;background:#40e0ff;font-weight:bold">❯</span><span style="background:#40e0ff"> </span><span style="color:#000000;background:#40e0ff;font-weight:bold">Notebook</span><span style="background:#40e0ff"> </span><span style="background:#1f1f1f"> </span><span style="color:#959595;background:#1f1f1f">+</span><span style="background:#1f1f1f"> </span><span style="color:#757575;background:#1f1f1f">│</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f">untitled</span><span style="background:#1f1f1f"> </span><span style="color:#959595;background:#1f1f1f">modified</span><span style="background:#1f1f1f"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">Ctrl+K</span><span style="background:#3a3a3a"> </span><span style="background:#1f1f1f"> </span><span style="color:#959595;background:#1f1f1f">search</span><span style="background:#1f1f1f"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">Ctrl+/</span><span style="background:#3a3a3a"> </span><span style="background:#1f1f1f"> </span><span style="color:#959595;background:#1f1f1f">shortcuts</span><span style="background:#1f1f1f"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">F10</span><span style="background:#3a3a3a"> </span><span style="background:#1f1f1f"> </span><span style="color:#959595;background:#1f1f1f">menu</span></pre>\ No newline at end of file
A e2e/testdata/screens/notebook-running-light.html +30 -0
@@ -0,0 +1,30 @@
1+<pre class="screen"> <span style="text-decoration:underline">F</span>ile <span style="text-decoration:underline">E</span>dit <span style="text-decoration:underline">V</span>iew <span style="text-decoration:underline">I</span>nsert F<span style="text-decoration:underline">o</span>rmat <span style="text-decoration:underline">D</span>ata <span style="text-decoration:underline">H</span>elp <span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">NOTEBOOK</span><span style="background:var(--vt-palette-6)"> </span>2+<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">cell</span><span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">5</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">a</span> <span style="color:var(--vt-palette-0)">nushell</span> <span style="color:var(--vt-palette-0)">pipeline</span>3+<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">Enter</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">edit</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">Shift+Enter</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">run</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">a</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">add</span> <span style="color:var(--vt-palette-0)">above</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">b</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">below</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">dd</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">delete</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">G</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">send</span> <span style="color:var(--vt-palette-0)">to</span> <span style="color:var(--vt-palette-0)">a</span> <span style="color:var(--vt-palette-0)">sheet</span>4+ Notebook 5 cells running 1, 1 waiting5+<span style="color:var(--vt-palette-8)">│</span> <span style="font-weight:bold;text-decoration:underline">Files</span>6+<span style="color:var(--vt-palette-8)">│</span> What&#39;s big in the repo, from <span style="color:var(--vt-palette-4)">ls</span>.7+8+ <span style="color:var(--vt-palette-0)">[3]</span> <span style="color:var(--vt-palette-0)">files</span> <span style="color:var(--vt-palette-0)">✓</span> <span style="color:var(--vt-palette-0)">&lt;1s</span>9+<span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-5)">files</span> <span style="font-weight:bold">=</span> <span style="color:var(--vt-palette-4)">ls</span>10+ <span style="font-weight:bold;text-decoration:underline">name</span> <span style="font-weight:bold;text-decoration:underline">type</span> <span style="font-weight:bold;text-decoration:underline">size</span>11+ 012 file 4.2 kB12+ CLAUDE.md file 1.0 kB13+ README.md file 9.8 kB14+ demos dir 3.1 kB15+ go.mod file 812 B16+17+ <span style="color:var(--vt-palette-0)">[4]</span> <span style="color:var(--vt-palette-0)">✓</span> <span style="color:var(--vt-palette-0)">&lt;1s</span>18+<span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-5)">$files</span> <span style="font-weight:bold">|</span> <span style="color:var(--vt-palette-4)">where</span> size <span style="font-weight:bold">&gt;</span> <span style="color:var(--vt-palette-1)">1kb</span>19+ <span style="font-weight:bold;text-decoration:underline">name</span> <span style="font-weight:bold;text-decoration:underline">type</span> <span style="font-weight:bold;text-decoration:underline">size</span>20+ 012 file 4.2 kB21+ README.md file 9.8 kB22+ demos dir 3.1 kB23+24+ <span style="color:var(--vt-palette-0)">[*]</span> <span style="color:var(--vt-palette-0)">running</span>25+<span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-4)">sleep</span> <span style="color:var(--vt-palette-1)">10min</span>26+27+<span style="background:var(--vt-palette-4)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-4)">[</span><span style="background:var(--vt-palette-4)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-4)">]</span><span style="background:var(--vt-palette-4)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-4)">waiting</span><span style="background:var(--vt-palette-4)"> </span>28+<span style="color:var(--vt-palette-8)">│</span> {name: <span style="color:var(--vt-palette-1)">1</span>}29+30+<span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">❯</span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">Notebook</span><span style="background:var(--vt-palette-6)"> </span> <span style="color:var(--vt-palette-0)">+</span> <span style="color:var(--vt-palette-8)">│</span> untitled <span style="color:var(--vt-palette-0)">modified</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">Ctrl+K</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">search</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">Ctrl+/</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">shortcuts</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">F10</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">menu</span></pre>\ No newline at end of file
A e2e/testdata/screens/notebook-running.html +30 -0
@@ -0,0 +1,30 @@
1+<pre class="screen"> <span style="text-decoration:underline">F</span>ile <span style="text-decoration:underline">E</span>dit <span style="text-decoration:underline">V</span>iew <span style="text-decoration:underline">I</span>nsert F<span style="text-decoration:underline">o</span>rmat <span style="text-decoration:underline">D</span>ata <span style="text-decoration:underline">H</span>elp <span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">NOTEBOOK</span><span style="background:var(--vt-palette-6)"> </span>2+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">cell</span><span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">5</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">a</span> <span style="color:var(--vt-palette-8)">nushell</span> <span style="color:var(--vt-palette-8)">pipeline</span>3+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Enter</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">edit</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Shift+Enter</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">run</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">a</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">add</span> <span style="color:var(--vt-palette-8)">above</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">b</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">below</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">dd</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">delete</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">G</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">send</span> <span style="color:var(--vt-palette-8)">to</span> <span style="color:var(--vt-palette-8)">a</span> <span style="color:var(--vt-palette-8)">sheet</span>4+ Notebook 5 cells running 1, 1 waiting5+<span style="color:var(--vt-palette-8)">│</span> <span style="font-weight:bold;text-decoration:underline">Files</span>6+<span style="color:var(--vt-palette-8)">│</span> What&#39;s big in the repo, from <span style="color:var(--vt-palette-6)">ls</span>.7+8+ <span style="color:var(--vt-palette-8)">[3]</span> <span style="color:var(--vt-palette-8)">files</span> <span style="color:var(--vt-palette-8)">✓</span> <span style="color:var(--vt-palette-8)">&lt;1s</span>9+<span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-5)">files</span> <span style="font-weight:bold">=</span> <span style="color:var(--vt-palette-6)">ls</span>10+ <span style="font-weight:bold;text-decoration:underline">name</span> <span style="font-weight:bold;text-decoration:underline">type</span> <span style="font-weight:bold;text-decoration:underline">size</span>11+ 012 file 4.2 kB12+ CLAUDE.md file 1.0 kB13+ README.md file 9.8 kB14+ demos dir 3.1 kB15+ go.mod file 812 B16+17+ <span style="color:var(--vt-palette-8)">[4]</span> <span style="color:var(--vt-palette-8)">✓</span> <span style="color:var(--vt-palette-8)">&lt;1s</span>18+<span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-5)">$files</span> <span style="font-weight:bold">|</span> <span style="color:var(--vt-palette-6)">where</span> size <span style="font-weight:bold">&gt;</span> <span style="color:var(--vt-palette-3)">1kb</span>19+ <span style="font-weight:bold;text-decoration:underline">name</span> <span style="font-weight:bold;text-decoration:underline">type</span> <span style="font-weight:bold;text-decoration:underline">size</span>20+ 012 file 4.2 kB21+ README.md file 9.8 kB22+ demos dir 3.1 kB23+24+ <span style="color:var(--vt-palette-8)">[*]</span> <span style="color:var(--vt-palette-8)">running</span>25+<span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-6)">sleep</span> <span style="color:var(--vt-palette-3)">10min</span>26+27+<span style="background:var(--vt-palette-4)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-4)">[</span><span style="background:var(--vt-palette-4)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-4)">]</span><span style="background:var(--vt-palette-4)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-4)">waiting</span><span style="background:var(--vt-palette-4)"> </span>28+<span style="color:var(--vt-palette-8)">│</span> {name: <span style="color:var(--vt-palette-3)">1</span>}29+30+<span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">❯</span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">Notebook</span><span style="background:var(--vt-palette-6)"> </span> <span style="color:var(--vt-palette-8)">+</span> <span style="color:var(--vt-palette-8)">│</span> untitled <span style="color:var(--vt-palette-8)">modified</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+K</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">search</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+/</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">shortcuts</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">F10</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">menu</span></pre>\ No newline at end of file
R e2e/testdata/screens/notebook-completion-high-contrast.html → e2e/testdata/screens/notebook-sent-high-contrast.html +12 -12
@@ -1,14 +1,14 @@
1-<pre class="screen"><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">F</span><span style="color:#ffffff;background:#1f1f1f">ile</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">E</span><span style="color:#ffffff;background:#1f1f1f">dit</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">V</span><span style="color:#ffffff;background:#1f1f1f">iew</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">I</span><span style="color:#ffffff;background:#1f1f1f">nsert</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f">F</span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">o</span><span style="color:#ffffff;background:#1f1f1f">rmat</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">D</span><span style="color:#ffffff;background:#1f1f1f">ata</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">H</span><span style="color:#ffffff;background:#1f1f1f">elp</span><span style="background:#1f1f1f"> </span><span style="background:#40e0ff"> </span><span style="color:#000000;background:#40e0ff;font-weight:bold">NU</span><span style="background:#40e0ff"> </span>2-<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">A1</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold">nu❯</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">$r1</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">|</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">so</span><span style="background:#000000"> </span>3-<span style="color:#959595;background:#000000">r1:</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">5</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">rows</span><span style="background:#000000"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">Esc</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">back</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">to</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">the</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">grid</span>4-<span style="background:#3a3a3a"> </span><span style="background:#40e0ff"> </span><span style="color:#000000;background:#40e0ff;font-weight:bold">A</span><span style="background:#40e0ff"> </span><span style="color:#757575;background:#000000">┌─</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold">Completions</span><span style="background:#000000"> </span><span style="color:#757575;background:#000000">────────────────┐</span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">E</span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">F</span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">G</span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">H</span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">I</span><span style="background:#3a3a3a"> </span>5-<span style="background:#40e0ff"> </span><span style="color:#000000;background:#40e0ff;font-weight:bold">1</span><span style="background:#40e0ff"> </span><span style="color:#000000;background:#40e0ff">r1</span><span style="background:#40e0ff"> </span><span style="color:#000000;background:#40e0ff">l</span><span style="color:#757575;background:#000000">│</span><span style="background:#40e0ff"> </span><span style="color:#000000;background:#40e0ff">sort-by</span><span style="background:#40e0ff"> </span><span style="color:#000000;background:#40e0ff">command</span><span style="background:#40e0ff"> </span><span style="color:#757575;background:#000000">│</span><span style="background:#000000"> </span>6-<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">2</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">name</span><span style="background:#000000"> </span><span style="color:#757575;background:#000000">└─────────────────────</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">1</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">of</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">1</span><span style="background:#000000"> </span><span style="color:#757575;background:#000000">─┘</span><span style="background:#000000"> </span>7-<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">3</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">012</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">4.2</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>8-<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">4</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">CLAUDE.md</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">1.0</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>9-<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">5</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">README.md</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">9.8</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>10-<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">6</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">demos</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">dir</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">3.1</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>11-<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">7</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">go.mod</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">812</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">B</span><span style="background:#000000"> </span>1+<pre class="screen"><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">F</span><span style="color:#ffffff;background:#1f1f1f">ile</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">E</span><span style="color:#ffffff;background:#1f1f1f">dit</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">V</span><span style="color:#ffffff;background:#1f1f1f">iew</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">I</span><span style="color:#ffffff;background:#1f1f1f">nsert</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f">F</span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">o</span><span style="color:#ffffff;background:#1f1f1f">rmat</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">D</span><span style="color:#ffffff;background:#1f1f1f">ata</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">H</span><span style="color:#ffffff;background:#1f1f1f">elp</span><span style="background:#1f1f1f"> </span><span style="background:#40e0ff"> </span><span style="color:#000000;background:#40e0ff;font-weight:bold">READY</span><span style="background:#40e0ff"> </span>2+<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">A2</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">012</span><span style="background:#000000"> </span>3+<span style="color:#959595;background:#000000">Output</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">of</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">files</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">from</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">Notebook,</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">nu.files</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">in</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">formulas</span><span style="background:#000000"> </span>4+<span style="background:#3a3a3a"> </span><span style="background:#40e0ff"> </span><span style="color:#000000;background:#40e0ff;font-weight:bold">A</span><span style="background:#40e0ff"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">B</span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">C</span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">D</span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">E</span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">F</span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">G</span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">H</span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">I</span><span style="background:#3a3a3a"> </span>5+<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">1</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#40e0ff;background:#000000;font-style:italic">name</span><span style="background:#000000"> </span><span style="color:#40e0ff;background:#000000;font-style:italic">type</span><span style="background:#000000"> </span><span style="color:#40e0ff;background:#000000;font-style:italic">size</span><span style="background:#000000"> </span>6+<span style="background:#40e0ff"> </span><span style="color:#000000;background:#40e0ff;font-weight:bold">2</span><span style="background:#40e0ff"> </span><span style="color:#000000;background:#40e0ff">012</span><span style="background:#40e0ff"> </span><span style="background:#000000"> </span><span style="color:#40e0ff;background:#000000;font-style:italic">file</span><span style="background:#000000"> </span><span style="color:#40e0ff;background:#000000;font-style:italic">4.2</span><span style="background:#000000"> </span><span style="color:#40e0ff;background:#000000;font-style:italic">kB</span><span style="background:#000000"> </span>7+<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">3</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#40e0ff;background:#000000;font-style:italic">CLAUDE.md</span><span style="background:#000000"> </span><span style="color:#40e0ff;background:#000000;font-style:italic">file</span><span style="background:#000000"> </span><span style="color:#40e0ff;background:#000000;font-style:italic">1.0</span><span style="background:#000000"> </span><span style="color:#40e0ff;background:#000000;font-style:italic">kB</span><span style="background:#000000"> </span>8+<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">4</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#40e0ff;background:#000000;font-style:italic">README.md</span><span style="background:#000000"> </span><span style="color:#40e0ff;background:#000000;font-style:italic">file</span><span style="background:#000000"> </span><span style="color:#40e0ff;background:#000000;font-style:italic">9.8</span><span style="background:#000000"> </span><span style="color:#40e0ff;background:#000000;font-style:italic">kB</span><span style="background:#000000"> </span>9+<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">5</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#40e0ff;background:#000000;font-style:italic">demos</span><span style="background:#000000"> </span><span style="color:#40e0ff;background:#000000;font-style:italic">dir</span><span style="background:#000000"> </span><span style="color:#40e0ff;background:#000000;font-style:italic">3.1</span><span style="background:#000000"> </span><span style="color:#40e0ff;background:#000000;font-style:italic">kB</span><span style="background:#000000"> </span>10+<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">6</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#40e0ff;background:#000000;font-style:italic">go.mod</span><span style="background:#000000"> </span><span style="color:#40e0ff;background:#000000;font-style:italic">file</span><span style="background:#000000"> </span><span style="color:#40e0ff;background:#000000;font-style:italic">812</span><span style="background:#000000"> </span><span style="color:#40e0ff;background:#000000;font-style:italic">B</span><span style="background:#000000"> </span>11+<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">7</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span>1212 <span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">8</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span>1313 <span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">9</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span>1414 <span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">10</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span>
@@ -27,4 +27,4 @@
2727 <span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">23</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span>2828 <span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">24</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span>2929 <span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">25</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span>30-<span style="color:#ffffff;background:#1f1f1f">command</span><span style="background:#1f1f1f"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">Tab</span><span style="background:#3a3a3a"> </span><span style="background:#1f1f1f"> </span><span style="color:#959595;background:#1f1f1f">complete</span><span style="background:#1f1f1f"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">Ctrl+N</span><span style="background:#3a3a3a"> </span><span style="background:#1f1f1f"> </span><span style="color:#959595;background:#1f1f1f">next</span><span style="background:#1f1f1f"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">Enter</span><span style="background:#3a3a3a"> </span><span style="background:#1f1f1f"> </span><span style="color:#959595;background:#1f1f1f">run</span><span style="background:#1f1f1f"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">Esc</span><span style="background:#3a3a3a"> </span><span style="background:#1f1f1f"> </span><span style="color:#959595;background:#1f1f1f">grid</span></pre>\ No newline at end of file30+<span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f">❯</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f">Notebook</span><span style="background:#1f1f1f"> </span><span style="background:#40e0ff"> </span><span style="color:#000000;background:#40e0ff;font-weight:bold">files</span><span style="background:#40e0ff"> </span><span style="background:#1f1f1f"> </span><span style="color:#959595;background:#1f1f1f">+</span><span style="background:#1f1f1f"> </span><span style="color:#757575;background:#1f1f1f">│</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f">untitled</span><span style="background:#1f1f1f"> </span><span style="color:#959595;background:#1f1f1f">modified</span><span style="background:#1f1f1f"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">Ctrl+K</span><span style="background:#3a3a3a"> </span><span style="background:#1f1f1f"> </span><span style="color:#959595;background:#1f1f1f">search</span><span style="background:#1f1f1f"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">Ctrl+/</span><span style="background:#3a3a3a"> </span><span style="background:#1f1f1f"> </span><span style="color:#959595;background:#1f1f1f">shortcuts</span><span style="background:#1f1f1f"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">F10</span><span style="background:#3a3a3a"> </span><span style="background:#1f1f1f"> </span><span style="color:#959595;background:#1f1f1f">menu</span></pre>\ No newline at end of file
A e2e/testdata/screens/notebook-sent-light.html +30 -0
@@ -0,0 +1,30 @@
1+<pre class="screen"> <span style="text-decoration:underline">F</span>ile <span style="text-decoration:underline">E</span>dit <span style="text-decoration:underline">V</span>iew <span style="text-decoration:underline">I</span>nsert F<span style="text-decoration:underline">o</span>rmat <span style="text-decoration:underline">D</span>ata <span style="text-decoration:underline">H</span>elp <span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">READY</span><span style="background:var(--vt-palette-6)"> </span>2+<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">A2</span><span style="background:var(--vt-palette-7)"> </span> 0123+<span style="color:var(--vt-palette-0)">Output</span> <span style="color:var(--vt-palette-0)">of</span> <span style="color:var(--vt-palette-0)">files</span> <span style="color:var(--vt-palette-0)">from</span> <span style="color:var(--vt-palette-0)">Notebook,</span> <span style="color:var(--vt-palette-0)">nu.files</span> <span style="color:var(--vt-palette-0)">in</span> <span style="color:var(--vt-palette-0)">formulas</span>4+<span style="background:var(--vt-palette-7)"> </span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">A</span><span style="background:var(--vt-palette-6)"> </span><span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">B</span><span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">C</span><span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">D</span><span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">E</span><span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">F</span><span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">G</span><span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">H</span><span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">I</span><span style="background:var(--vt-palette-7)"> </span>5+<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">1</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-4);font-style:italic">name</span> <span style="color:var(--vt-palette-4);font-style:italic">type</span> <span style="color:var(--vt-palette-4);font-style:italic">size</span>6+<span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">2</span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6)">012</span><span style="background:var(--vt-palette-6)"> </span> <span style="color:var(--vt-palette-4);font-style:italic">file</span> <span style="color:var(--vt-palette-4);font-style:italic">4.2</span> <span style="color:var(--vt-palette-4);font-style:italic">kB</span>7+<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">3</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-4);font-style:italic">CLAUDE.md</span> <span style="color:var(--vt-palette-4);font-style:italic">file</span> <span style="color:var(--vt-palette-4);font-style:italic">1.0</span> <span style="color:var(--vt-palette-4);font-style:italic">kB</span>8+<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">4</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-4);font-style:italic">README.md</span> <span style="color:var(--vt-palette-4);font-style:italic">file</span> <span style="color:var(--vt-palette-4);font-style:italic">9.8</span> <span style="color:var(--vt-palette-4);font-style:italic">kB</span>9+<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">5</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-4);font-style:italic">demos</span> <span style="color:var(--vt-palette-4);font-style:italic">dir</span> <span style="color:var(--vt-palette-4);font-style:italic">3.1</span> <span style="color:var(--vt-palette-4);font-style:italic">kB</span>10+<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">6</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-4);font-style:italic">go.mod</span> <span style="color:var(--vt-palette-4);font-style:italic">file</span> <span style="color:var(--vt-palette-4);font-style:italic">812</span> <span style="color:var(--vt-palette-4);font-style:italic">B</span>11+<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">7</span><span style="background:var(--vt-palette-7)"> </span>12+<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">8</span><span style="background:var(--vt-palette-7)"> </span>13+<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">9</span><span style="background:var(--vt-palette-7)"> </span>14+<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">10</span><span style="background:var(--vt-palette-7)"> </span>15+<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">11</span><span style="background:var(--vt-palette-7)"> </span>16+<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">12</span><span style="background:var(--vt-palette-7)"> </span>17+<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">13</span><span style="background:var(--vt-palette-7)"> </span>18+<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">14</span><span style="background:var(--vt-palette-7)"> </span>19+<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">15</span><span style="background:var(--vt-palette-7)"> </span>20+<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">16</span><span style="background:var(--vt-palette-7)"> </span>21+<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">17</span><span style="background:var(--vt-palette-7)"> </span>22+<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">18</span><span style="background:var(--vt-palette-7)"> </span>23+<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">19</span><span style="background:var(--vt-palette-7)"> </span>24+<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">20</span><span style="background:var(--vt-palette-7)"> </span>25+<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">21</span><span style="background:var(--vt-palette-7)"> </span>26+<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">22</span><span style="background:var(--vt-palette-7)"> </span>27+<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">23</span><span style="background:var(--vt-palette-7)"> </span>28+<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">24</span><span style="background:var(--vt-palette-7)"> </span>29+<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">25</span><span style="background:var(--vt-palette-7)"> </span>30+ ❯ Notebook <span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">files</span><span style="background:var(--vt-palette-6)"> </span> <span style="color:var(--vt-palette-0)">+</span> <span style="color:var(--vt-palette-8)">│</span> untitled <span style="color:var(--vt-palette-0)">modified</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">Ctrl+K</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">search</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">Ctrl+/</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">shortcuts</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">F10</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">menu</span></pre>\ No newline at end of file
A e2e/testdata/screens/notebook-sent.html +30 -0
@@ -0,0 +1,30 @@
1+<pre class="screen"> <span style="text-decoration:underline">F</span>ile <span style="text-decoration:underline">E</span>dit <span style="text-decoration:underline">V</span>iew <span style="text-decoration:underline">I</span>nsert F<span style="text-decoration:underline">o</span>rmat <span style="text-decoration:underline">D</span>ata <span style="text-decoration:underline">H</span>elp <span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">READY</span><span style="background:var(--vt-palette-6)"> </span>2+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">A2</span><span style="background:var(--vt-palette-8)"> </span> 0123+<span style="color:var(--vt-palette-8)">Output</span> <span style="color:var(--vt-palette-8)">of</span> <span style="color:var(--vt-palette-8)">files</span> <span style="color:var(--vt-palette-8)">from</span> <span style="color:var(--vt-palette-8)">Notebook,</span> <span style="color:var(--vt-palette-8)">nu.files</span> <span style="color:var(--vt-palette-8)">in</span> <span style="color:var(--vt-palette-8)">formulas</span>4+<span style="background:var(--vt-palette-8)"> </span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">A</span><span style="background:var(--vt-palette-6)"> </span><span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">B</span><span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">C</span><span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">D</span><span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">E</span><span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">F</span><span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">G</span><span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">H</span><span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">I</span><span style="background:var(--vt-palette-8)"> </span>5+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">1</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-6);font-style:italic">name</span> <span style="color:var(--vt-palette-6);font-style:italic">type</span> <span style="color:var(--vt-palette-6);font-style:italic">size</span>6+<span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">2</span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6)">012</span><span style="background:var(--vt-palette-6)"> </span> <span style="color:var(--vt-palette-6);font-style:italic">file</span> <span style="color:var(--vt-palette-6);font-style:italic">4.2</span> <span style="color:var(--vt-palette-6);font-style:italic">kB</span>7+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">3</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-6);font-style:italic">CLAUDE.md</span> <span style="color:var(--vt-palette-6);font-style:italic">file</span> <span style="color:var(--vt-palette-6);font-style:italic">1.0</span> <span style="color:var(--vt-palette-6);font-style:italic">kB</span>8+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">4</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-6);font-style:italic">README.md</span> <span style="color:var(--vt-palette-6);font-style:italic">file</span> <span style="color:var(--vt-palette-6);font-style:italic">9.8</span> <span style="color:var(--vt-palette-6);font-style:italic">kB</span>9+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">5</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-6);font-style:italic">demos</span> <span style="color:var(--vt-palette-6);font-style:italic">dir</span> <span style="color:var(--vt-palette-6);font-style:italic">3.1</span> <span style="color:var(--vt-palette-6);font-style:italic">kB</span>10+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">6</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-6);font-style:italic">go.mod</span> <span style="color:var(--vt-palette-6);font-style:italic">file</span> <span style="color:var(--vt-palette-6);font-style:italic">812</span> <span style="color:var(--vt-palette-6);font-style:italic">B</span>11+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">7</span><span style="background:var(--vt-palette-8)"> </span>12+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">8</span><span style="background:var(--vt-palette-8)"> </span>13+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">9</span><span style="background:var(--vt-palette-8)"> </span>14+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">10</span><span style="background:var(--vt-palette-8)"> </span>15+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">11</span><span style="background:var(--vt-palette-8)"> </span>16+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">12</span><span style="background:var(--vt-palette-8)"> </span>17+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">13</span><span style="background:var(--vt-palette-8)"> </span>18+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">14</span><span style="background:var(--vt-palette-8)"> </span>19+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">15</span><span style="background:var(--vt-palette-8)"> </span>20+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">16</span><span style="background:var(--vt-palette-8)"> </span>21+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">17</span><span style="background:var(--vt-palette-8)"> </span>22+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">18</span><span style="background:var(--vt-palette-8)"> </span>23+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">19</span><span style="background:var(--vt-palette-8)"> </span>24+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">20</span><span style="background:var(--vt-palette-8)"> </span>25+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">21</span><span style="background:var(--vt-palette-8)"> </span>26+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">22</span><span style="background:var(--vt-palette-8)"> </span>27+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">23</span><span style="background:var(--vt-palette-8)"> </span>28+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">24</span><span style="background:var(--vt-palette-8)"> </span>29+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">25</span><span style="background:var(--vt-palette-8)"> </span>30+ ❯ Notebook <span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">files</span><span style="background:var(--vt-palette-6)"> </span> <span style="color:var(--vt-palette-8)">+</span> <span style="color:var(--vt-palette-8)">│</span> untitled <span style="color:var(--vt-palette-8)">modified</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+K</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">search</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+/</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">shortcuts</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">F10</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">menu</span></pre>\ No newline at end of file
A e2e/testdata/screens/notebook-stale-high-contrast.html +30 -0
@@ -0,0 +1,30 @@
1+<pre class="screen"><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">F</span><span style="color:#ffffff;background:#1f1f1f">ile</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">E</span><span style="color:#ffffff;background:#1f1f1f">dit</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">V</span><span style="color:#ffffff;background:#1f1f1f">iew</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">I</span><span style="color:#ffffff;background:#1f1f1f">nsert</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f">F</span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">o</span><span style="color:#ffffff;background:#1f1f1f">rmat</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">D</span><span style="color:#ffffff;background:#1f1f1f">ata</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">H</span><span style="color:#ffffff;background:#1f1f1f">elp</span><span style="background:#1f1f1f"> </span><span style="background:#40e0ff"> </span><span style="color:#000000;background:#40e0ff;font-weight:bold">NOTEBOOK</span><span style="background:#40e0ff"> </span>2+<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">files</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">a</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">nushell</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">pipeline;</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">read</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">as</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">$files,</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">nu.files</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">in</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">formulas</span><span style="background:#000000"> </span>3+<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">Enter</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">edit</span><span style="background:#000000"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">Shift+Enter</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">run</span><span style="background:#000000"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">a</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">add</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">above</span><span style="background:#000000"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">b</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">below</span><span style="background:#000000"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">dd</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">delete</span><span style="background:#000000"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">G</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">send</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">to</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">a</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">sheet</span><span style="background:#000000"> </span>4+<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">Notebook</span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">4</span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">cells</span><span style="background:#3a3a3a"> </span>5+<span style="background:#000000"> </span><span style="color:#959595;background:#000000">Markdown</span><span style="background:#000000"> </span>6+<span style="color:#757575;background:#000000">│</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold;text-decoration:underline">Files</span><span style="background:#000000"> </span>7+<span style="color:#757575;background:#000000">│</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">What&#39;s</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">big</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">in</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">the</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">repo,</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">from</span><span style="background:#000000"> </span><span style="color:#40e0ff;background:#000000">ls</span><span style="color:#ffffff;background:#000000">.</span><span style="background:#000000"> </span>8+<span style="background:#000000"> </span>9+<span style="background:#0033cc"> </span><span style="color:#ffffff;background:#0033cc">[1]</span><span style="background:#0033cc"> </span><span style="color:#ffffff;background:#0033cc">files</span><span style="background:#0033cc"> </span><span style="color:#ffffff;background:#0033cc">stale</span><span style="background:#0033cc"> </span><span style="color:#ffffff;background:#0033cc">&lt;1s</span><span style="background:#0033cc"> </span><span style="background:#000000"> </span>10+<span style="color:#757575;background:#000000">│</span><span style="background:#000000"> </span><span style="color:#ff80ff;background:#000000">files</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold">=</span><span style="background:#000000"> </span><span style="color:#40e0ff;background:#000000">ls</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold">|</span><span style="background:#000000"> </span><span style="color:#40e0ff;background:#000000">first</span><span style="background:#000000"> </span><span style="color:#ffe040;background:#000000">4</span><span style="background:#000000"> </span>11+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold;text-decoration:underline">name</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold;text-decoration:underline">type</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold;text-decoration:underline">size</span><span style="background:#000000"> </span>12+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">012</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">4.2</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>13+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">CLAUDE.md</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">1.0</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>14+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">README.md</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">9.8</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>15+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">demos</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">dir</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">3.1</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>16+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">go.mod</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">812</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">B</span><span style="background:#000000"> </span>17+<span style="background:#000000"> </span>18+<span style="background:#000000"> </span><span style="color:#959595;background:#000000">[2]</span><span style="background:#000000"> </span><span style="color:#ffe040;background:#000000;font-style:italic">stale</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">&lt;1s</span><span style="background:#000000"> </span>19+<span style="color:#757575;background:#000000">│</span><span style="background:#000000"> </span><span style="color:#ff80ff;background:#000000">$files</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold">|</span><span style="background:#000000"> </span><span style="color:#40e0ff;background:#000000">where</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">size</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold">&gt;</span><span style="background:#000000"> </span><span style="color:#ffe040;background:#000000">1kb</span><span style="background:#000000"> </span>20+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold;text-decoration:underline">name</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold;text-decoration:underline">type</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold;text-decoration:underline">size</span><span style="background:#000000"> </span>21+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">012</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">4.2</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>22+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">README.md</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">9.8</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>23+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">demos</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">dir</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">3.1</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>24+<span style="background:#000000"> </span>25+<span style="background:#000000"> </span><span style="color:#959595;background:#000000">[</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">]</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">not</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">run</span><span style="background:#000000"> </span>26+<span style="color:#757575;background:#000000">│</span><span style="background:#000000"> </span>27+<span style="background:#000000"> </span>28+<span style="background:#000000"> </span>29+<span style="background:#000000"> </span>30+<span style="background:#40e0ff"> </span><span style="color:#000000;background:#40e0ff;font-weight:bold">❯</span><span style="background:#40e0ff"> </span><span style="color:#000000;background:#40e0ff;font-weight:bold">Notebook</span><span style="background:#40e0ff"> </span><span style="background:#1f1f1f"> </span><span style="color:#959595;background:#1f1f1f">+</span><span style="background:#1f1f1f"> </span><span style="color:#757575;background:#1f1f1f">│</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f">untitled</span><span style="background:#1f1f1f"> </span><span style="color:#959595;background:#1f1f1f">modified</span><span style="background:#1f1f1f"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">Ctrl+K</span><span style="background:#3a3a3a"> </span><span style="background:#1f1f1f"> </span><span style="color:#959595;background:#1f1f1f">search</span><span style="background:#1f1f1f"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">Ctrl+/</span><span style="background:#3a3a3a"> </span><span style="background:#1f1f1f"> </span><span style="color:#959595;background:#1f1f1f">shortcuts</span><span style="background:#1f1f1f"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">F10</span><span style="background:#3a3a3a"> </span><span style="background:#1f1f1f"> </span><span style="color:#959595;background:#1f1f1f">menu</span></pre>\ No newline at end of file
A e2e/testdata/screens/notebook-stale-light.html +30 -0
@@ -0,0 +1,30 @@
1+<pre class="screen"> <span style="text-decoration:underline">F</span>ile <span style="text-decoration:underline">E</span>dit <span style="text-decoration:underline">V</span>iew <span style="text-decoration:underline">I</span>nsert F<span style="text-decoration:underline">o</span>rmat <span style="text-decoration:underline">D</span>ata <span style="text-decoration:underline">H</span>elp <span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">NOTEBOOK</span><span style="background:var(--vt-palette-6)"> </span>2+<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">files</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">a</span> <span style="color:var(--vt-palette-0)">nushell</span> <span style="color:var(--vt-palette-0)">pipeline;</span> <span style="color:var(--vt-palette-0)">read</span> <span style="color:var(--vt-palette-0)">as</span> <span style="color:var(--vt-palette-0)">$files,</span> <span style="color:var(--vt-palette-0)">nu.files</span> <span style="color:var(--vt-palette-0)">in</span> <span style="color:var(--vt-palette-0)">formulas</span>3+<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">Enter</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">edit</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">Shift+Enter</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">run</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">a</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">add</span> <span style="color:var(--vt-palette-0)">above</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">b</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">below</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">dd</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">delete</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">G</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">send</span> <span style="color:var(--vt-palette-0)">to</span> <span style="color:var(--vt-palette-0)">a</span> <span style="color:var(--vt-palette-0)">sheet</span>4+ Notebook 4 cells5+ <span style="color:var(--vt-palette-0)">Markdown</span>6+<span style="color:var(--vt-palette-8)">│</span> <span style="font-weight:bold;text-decoration:underline">Files</span>7+<span style="color:var(--vt-palette-8)">│</span> What&#39;s big in the repo, from <span style="color:var(--vt-palette-4)">ls</span>.8+9+<span style="background:var(--vt-palette-4)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-4)">[1]</span><span style="background:var(--vt-palette-4)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-4)">files</span><span style="background:var(--vt-palette-4)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-4)">stale</span><span style="background:var(--vt-palette-4)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-4)">&lt;1s</span><span style="background:var(--vt-palette-4)"> </span>10+<span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-5)">files</span> <span style="font-weight:bold">=</span> <span style="color:var(--vt-palette-4)">ls</span> <span style="font-weight:bold">|</span> <span style="color:var(--vt-palette-4)">first</span> <span style="color:var(--vt-palette-1)">4</span>11+ <span style="font-weight:bold;text-decoration:underline">name</span> <span style="font-weight:bold;text-decoration:underline">type</span> <span style="font-weight:bold;text-decoration:underline">size</span>12+ 012 file 4.2 kB13+ CLAUDE.md file 1.0 kB14+ README.md file 9.8 kB15+ demos dir 3.1 kB16+ go.mod file 812 B17+18+ <span style="color:var(--vt-palette-0)">[2]</span> <span style="color:var(--vt-palette-5);font-style:italic">stale</span> <span style="color:var(--vt-palette-0)">&lt;1s</span>19+<span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-5)">$files</span> <span style="font-weight:bold">|</span> <span style="color:var(--vt-palette-4)">where</span> size <span style="font-weight:bold">&gt;</span> <span style="color:var(--vt-palette-1)">1kb</span>20+ <span style="font-weight:bold;text-decoration:underline">name</span> <span style="font-weight:bold;text-decoration:underline">type</span> <span style="font-weight:bold;text-decoration:underline">size</span>21+ 012 file 4.2 kB22+ README.md file 9.8 kB23+ demos dir 3.1 kB24+25+ <span style="color:var(--vt-palette-0)">[</span> <span style="color:var(--vt-palette-0)">]</span> <span style="color:var(--vt-palette-0)">not</span> <span style="color:var(--vt-palette-0)">run</span>26+<span style="color:var(--vt-palette-8)">│</span>27+28+29+30+<span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">❯</span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">Notebook</span><span style="background:var(--vt-palette-6)"> </span> <span style="color:var(--vt-palette-0)">+</span> <span style="color:var(--vt-palette-8)">│</span> untitled <span style="color:var(--vt-palette-0)">modified</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">Ctrl+K</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">search</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">Ctrl+/</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">shortcuts</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">F10</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">menu</span></pre>\ No newline at end of file
A e2e/testdata/screens/notebook-stale.html +30 -0
@@ -0,0 +1,30 @@
1+<pre class="screen"> <span style="text-decoration:underline">F</span>ile <span style="text-decoration:underline">E</span>dit <span style="text-decoration:underline">V</span>iew <span style="text-decoration:underline">I</span>nsert F<span style="text-decoration:underline">o</span>rmat <span style="text-decoration:underline">D</span>ata <span style="text-decoration:underline">H</span>elp <span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">NOTEBOOK</span><span style="background:var(--vt-palette-6)"> </span>2+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">files</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">a</span> <span style="color:var(--vt-palette-8)">nushell</span> <span style="color:var(--vt-palette-8)">pipeline;</span> <span style="color:var(--vt-palette-8)">read</span> <span style="color:var(--vt-palette-8)">as</span> <span style="color:var(--vt-palette-8)">$files,</span> <span style="color:var(--vt-palette-8)">nu.files</span> <span style="color:var(--vt-palette-8)">in</span> <span style="color:var(--vt-palette-8)">formulas</span>3+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Enter</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">edit</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Shift+Enter</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">run</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">a</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">add</span> <span style="color:var(--vt-palette-8)">above</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">b</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">below</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">dd</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">delete</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">G</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">send</span> <span style="color:var(--vt-palette-8)">to</span> <span style="color:var(--vt-palette-8)">a</span> <span style="color:var(--vt-palette-8)">sheet</span>4+ Notebook 4 cells5+ <span style="color:var(--vt-palette-8)">Markdown</span>6+<span style="color:var(--vt-palette-8)">│</span> <span style="font-weight:bold;text-decoration:underline">Files</span>7+<span style="color:var(--vt-palette-8)">│</span> What&#39;s big in the repo, from <span style="color:var(--vt-palette-6)">ls</span>.8+9+<span style="background:var(--vt-palette-4)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-4)">[1]</span><span style="background:var(--vt-palette-4)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-4)">files</span><span style="background:var(--vt-palette-4)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-4)">stale</span><span style="background:var(--vt-palette-4)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-4)">&lt;1s</span><span style="background:var(--vt-palette-4)"> </span>10+<span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-5)">files</span> <span style="font-weight:bold">=</span> <span style="color:var(--vt-palette-6)">ls</span> <span style="font-weight:bold">|</span> <span style="color:var(--vt-palette-6)">first</span> <span style="color:var(--vt-palette-3)">4</span>11+ <span style="font-weight:bold;text-decoration:underline">name</span> <span style="font-weight:bold;text-decoration:underline">type</span> <span style="font-weight:bold;text-decoration:underline">size</span>12+ 012 file 4.2 kB13+ CLAUDE.md file 1.0 kB14+ README.md file 9.8 kB15+ demos dir 3.1 kB16+ go.mod file 812 B17+18+ <span style="color:var(--vt-palette-8)">[2]</span> <span style="color:var(--vt-palette-3);font-style:italic">stale</span> <span style="color:var(--vt-palette-8)">&lt;1s</span>19+<span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-5)">$files</span> <span style="font-weight:bold">|</span> <span style="color:var(--vt-palette-6)">where</span> size <span style="font-weight:bold">&gt;</span> <span style="color:var(--vt-palette-3)">1kb</span>20+ <span style="font-weight:bold;text-decoration:underline">name</span> <span style="font-weight:bold;text-decoration:underline">type</span> <span style="font-weight:bold;text-decoration:underline">size</span>21+ 012 file 4.2 kB22+ README.md file 9.8 kB23+ demos dir 3.1 kB24+25+ <span style="color:var(--vt-palette-8)">[</span> <span style="color:var(--vt-palette-8)">]</span> <span style="color:var(--vt-palette-8)">not</span> <span style="color:var(--vt-palette-8)">run</span>26+<span style="color:var(--vt-palette-8)">│</span>27+28+29+30+<span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">❯</span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">Notebook</span><span style="background:var(--vt-palette-6)"> </span> <span style="color:var(--vt-palette-8)">+</span> <span style="color:var(--vt-palette-8)">│</span> untitled <span style="color:var(--vt-palette-8)">modified</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+K</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">search</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+/</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">shortcuts</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">F10</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">menu</span></pre>\ No newline at end of file
A e2e/testdata/screens/notebook-wrap-high-contrast.html +24 -0
@@ -0,0 +1,24 @@
1+<pre class="screen"><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">F</span><span style="color:#ffffff;background:#1f1f1f">ile</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">E</span><span style="color:#ffffff;background:#1f1f1f">dit</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">V</span><span style="color:#ffffff;background:#1f1f1f">iew</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">I</span><span style="color:#ffffff;background:#1f1f1f">nsert</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f">F</span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">o</span><span style="color:#ffffff;background:#1f1f1f">rmat</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">D</span><span style="color:#ffffff;background:#1f1f1f">ata</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">H</span><span style="color:#ffffff;background:#1f1f1f">elp</span><span style="background:#1f1f1f"> </span><span style="background:#40e0ff"> </span><span style="color:#000000;background:#40e0ff;font-weight:bold">EDIT</span><span style="background:#40e0ff"> </span>2+<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">cell</span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">4</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">a</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">nushell</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">pipeline</span><span style="background:#000000"> </span>3+<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">Shift+Enter</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">run</span><span style="background:#000000"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">Ctrl+Enter</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">run</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">here</span><span style="background:#000000"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">Tab</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">complete</span><span style="background:#000000"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">Esc</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">done</span>4+<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">Notebook</span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">4</span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">cells</span><span style="background:#3a3a3a"> </span>5+<span style="background:#000000"> </span><span style="color:#959595;background:#000000">[1]</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">files</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">✓</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">&lt;1s</span><span style="background:#000000"> </span>6+<span style="color:#757575;background:#000000">│</span><span style="background:#000000"> </span><span style="color:#ff80ff;background:#000000">files</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold">=</span><span style="background:#000000"> </span><span style="color:#40e0ff;background:#000000">ls</span><span style="background:#000000"> </span>7+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold;text-decoration:underline">name</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold;text-decoration:underline">type</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold;text-decoration:underline">size</span><span style="background:#000000"> </span>8+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">012</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">4.2</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>9+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">CLAUDE.md</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">1.0</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>10+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">README.md</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">9.8</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>11+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">demos</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">dir</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">3.1</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>12+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">go.mod</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">812</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">B</span><span style="background:#000000"> </span>13+<span style="background:#000000"> </span>14+<span style="background:#000000"> </span><span style="color:#959595;background:#000000">[2]</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">✓</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">&lt;1s</span><span style="background:#000000"> </span>15+<span style="color:#757575;background:#000000">│</span><span style="background:#000000"> </span><span style="color:#ff80ff;background:#000000">$files</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold">|</span><span style="background:#000000"> </span><span style="color:#40e0ff;background:#000000">where</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">size</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold">&gt;</span><span style="background:#000000"> </span><span style="color:#ffe040;background:#000000">1kb</span><span style="background:#000000"> </span>16+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold;text-decoration:underline">name</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold;text-decoration:underline">type</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold;text-decoration:underline">size</span><span style="background:#000000"> </span>17+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">012</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">4.2</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>18+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">README.md</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">9.8</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>19+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">demos</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">dir</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">3.1</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>20+<span style="background:#000000"> </span>21+<span style="background:#40e0ff"> </span><span style="color:#000000;background:#40e0ff">[</span><span style="background:#40e0ff"> </span><span style="color:#000000;background:#40e0ff">]</span><span style="background:#40e0ff"> </span><span style="color:#000000;background:#40e0ff">not</span><span style="background:#40e0ff"> </span><span style="color:#000000;background:#40e0ff">run</span><span style="background:#40e0ff"> </span><span style="background:#000000"> </span>22+<span style="color:#757575;background:#000000">│</span><span style="background:#000000"> </span><span style="color:#ff80ff;background:#000000">$files</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold">|</span><span style="background:#000000"> </span><span style="color:#40e0ff;background:#000000">where</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">size</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold">&gt;</span><span style="background:#000000"> </span><span style="color:#ffe040;background:#000000">1kb</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold">|</span><span style="background:#000000"> </span><span style="color:#40e0ff;background:#000000">where</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">type</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold">==</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold">|</span><span style="background:#000000"> </span><span style="color:#40e0ff;background:#000000">sort-by</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">size</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">--reverse</span><span style="background:#000000"> </span>23+<span style="color:#757575;background:#000000">│</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold">|</span><span style="background:#000000"> </span><span style="color:#40e0ff;background:#000000">select</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">name</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">size</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold">|</span><span style="background:#000000"> </span><span style="color:#40e0ff;background:#000000">first</span><span style="background:#000000"> </span><span style="color:#ffe040;background:#000000">10</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold">|</span><span style="background:#000000"> </span><span style="color:#40e0ff;background:#000000">rename</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">bytes</span><span style="background:#000000"> </span>24+<span style="background:#40e0ff"> </span><span style="color:#000000;background:#40e0ff;font-weight:bold">❯</span><span style="background:#40e0ff"> </span><span style="color:#000000;background:#40e0ff;font-weight:bold">Notebook</span><span style="background:#40e0ff"> </span><span style="background:#1f1f1f"> </span><span style="color:#959595;background:#1f1f1f">+</span><span style="background:#1f1f1f"> </span><span style="color:#757575;background:#1f1f1f">│</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f">untitled</span><span style="background:#1f1f1f"> </span><span style="color:#959595;background:#1f1f1f">modified</span><span style="background:#1f1f1f"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">Ctrl+K</span><span style="background:#3a3a3a"> </span><span style="background:#1f1f1f"> </span><span style="color:#959595;background:#1f1f1f">search</span><span style="background:#1f1f1f"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">Ctrl+/</span><span style="background:#3a3a3a"> </span><span style="background:#1f1f1f"> </span><span style="color:#959595;background:#1f1f1f">shortcuts</span></pre>\ No newline at end of file
A e2e/testdata/screens/notebook-wrap-light.html +24 -0
@@ -0,0 +1,24 @@
1+<pre class="screen"> <span style="text-decoration:underline">F</span>ile <span style="text-decoration:underline">E</span>dit <span style="text-decoration:underline">V</span>iew <span style="text-decoration:underline">I</span>nsert F<span style="text-decoration:underline">o</span>rmat <span style="text-decoration:underline">D</span>ata <span style="text-decoration:underline">H</span>elp <span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">EDIT</span><span style="background:var(--vt-palette-6)"> </span>2+<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">cell</span><span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">4</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">a</span> <span style="color:var(--vt-palette-0)">nushell</span> <span style="color:var(--vt-palette-0)">pipeline</span>3+<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">Shift+Enter</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">run</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">Ctrl+Enter</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">run</span> <span style="color:var(--vt-palette-0)">here</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">Tab</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">complete</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">Esc</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">done</span>4+ Notebook 4 cells5+ <span style="color:var(--vt-palette-0)">[1]</span> <span style="color:var(--vt-palette-0)">files</span> <span style="color:var(--vt-palette-0)">✓</span> <span style="color:var(--vt-palette-0)">&lt;1s</span>6+<span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-5)">files</span> <span style="font-weight:bold">=</span> <span style="color:var(--vt-palette-4)">ls</span>7+ <span style="font-weight:bold;text-decoration:underline">name</span> <span style="font-weight:bold;text-decoration:underline">type</span> <span style="font-weight:bold;text-decoration:underline">size</span>8+ 012 file 4.2 kB9+ CLAUDE.md file 1.0 kB10+ README.md file 9.8 kB11+ demos dir 3.1 kB12+ go.mod file 812 B13+14+ <span style="color:var(--vt-palette-0)">[2]</span> <span style="color:var(--vt-palette-0)">✓</span> <span style="color:var(--vt-palette-0)">&lt;1s</span>15+<span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-5)">$files</span> <span style="font-weight:bold">|</span> <span style="color:var(--vt-palette-4)">where</span> size <span style="font-weight:bold">&gt;</span> <span style="color:var(--vt-palette-1)">1kb</span>16+ <span style="font-weight:bold;text-decoration:underline">name</span> <span style="font-weight:bold;text-decoration:underline">type</span> <span style="font-weight:bold;text-decoration:underline">size</span>17+ 012 file 4.2 kB18+ README.md file 9.8 kB19+ demos dir 3.1 kB20+21+<span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6)">[</span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6)">]</span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6)">not</span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6)">run</span><span style="background:var(--vt-palette-6)"> </span>22+<span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-5)">$files</span> <span style="font-weight:bold">|</span> <span style="color:var(--vt-palette-4)">where</span> size <span style="font-weight:bold">&gt;</span> <span style="color:var(--vt-palette-1)">1kb</span> <span style="font-weight:bold">|</span> <span style="color:var(--vt-palette-4)">where</span> type <span style="font-weight:bold">==</span> file <span style="font-weight:bold">|</span> <span style="color:var(--vt-palette-4)">sort-by</span> size --reverse23+<span style="color:var(--vt-palette-8)">│</span> <span style="font-weight:bold">|</span> <span style="color:var(--vt-palette-4)">select</span> name size <span style="font-weight:bold">|</span> <span style="color:var(--vt-palette-4)">first</span> <span style="color:var(--vt-palette-1)">10</span> <span style="font-weight:bold">|</span> <span style="color:var(--vt-palette-4)">rename</span> file bytes24+<span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">❯</span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">Notebook</span><span style="background:var(--vt-palette-6)"> </span> <span style="color:var(--vt-palette-0)">+</span> <span style="color:var(--vt-palette-8)">│</span> untitled <span style="color:var(--vt-palette-0)">modified</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">Ctrl+K</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">search</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">Ctrl+/</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">shortcuts</span></pre>\ No newline at end of file
A e2e/testdata/screens/notebook-wrap-narrow-high-contrast.html +24 -0
@@ -0,0 +1,24 @@
1+<pre class="screen"><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">F</span><span style="color:#ffffff;background:#1f1f1f">ile</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">E</span><span style="color:#ffffff;background:#1f1f1f">dit</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">V</span><span style="color:#ffffff;background:#1f1f1f">iew</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">I</span><span style="color:#ffffff;background:#1f1f1f">nsert</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f">F</span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">o</span><span style="color:#ffffff;background:#1f1f1f">rmat</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">D</span><span style="color:#ffffff;background:#1f1f1f">ata</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f;text-decoration:underline">H</span><span style="color:#ffffff;background:#1f1f1f">elp</span><span style="background:#1f1f1f"> </span><span style="background:#40e0ff"> </span><span style="color:#000000;background:#40e0ff;font-weight:bold">NOTEBOOK</span><span style="background:#40e0ff"> </span>2+<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">cell</span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">4</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">reads</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">$files</span><span style="background:#000000"> </span>3+<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">Enter</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">edit</span><span style="background:#000000"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">Shift+Enter</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">run</span><span style="background:#000000"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">a</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">add</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">above</span><span style="background:#000000"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">b</span><span style="background:#3a3a3a"> </span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">below</span><span style="background:#000000"> </span><span style="background:#3a3a3a"> </span>4+<span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">Notebook</span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">4</span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">cells</span><span style="background:#3a3a3a"> </span>5+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold;text-decoration:underline">name</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold;text-decoration:underline">type</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold;text-decoration:underline">size</span><span style="background:#000000"> </span>6+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">012</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">4.2</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>7+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">CLAUDE.md</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">1.0</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>8+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">README.md</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">9.8</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>9+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">demos</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">dir</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">3.1</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>10+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">go.mod</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">812</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">B</span><span style="background:#000000"> </span>11+<span style="background:#000000"> </span>12+<span style="background:#000000"> </span><span style="color:#959595;background:#000000">[2]</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">✓</span><span style="background:#000000"> </span><span style="color:#959595;background:#000000">&lt;1s</span><span style="background:#000000"> </span>13+<span style="color:#757575;background:#000000">│</span><span style="background:#000000"> </span><span style="color:#ff80ff;background:#000000">$files</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold">|</span><span style="background:#000000"> </span><span style="color:#40e0ff;background:#000000">where</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">size</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold">&gt;</span><span style="background:#000000"> </span><span style="color:#ffe040;background:#000000">1kb</span><span style="background:#000000"> </span>14+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold;text-decoration:underline">name</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold;text-decoration:underline">type</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold;text-decoration:underline">size</span><span style="background:#000000"> </span>15+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">012</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">4.2</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>16+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">README.md</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">9.8</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>17+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">demos</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">dir</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">3.1</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">kB</span><span style="background:#000000"> </span>18+<span style="background:#000000"> </span>19+<span style="background:#0033cc"> </span><span style="color:#ffffff;background:#0033cc">[3]</span><span style="background:#0033cc"> </span><span style="color:#ffffff;background:#0033cc">✓</span><span style="background:#0033cc"> </span><span style="color:#ffffff;background:#0033cc">&lt;1s</span><span style="background:#0033cc"> </span><span style="background:#000000"> </span>20+<span style="color:#757575;background:#000000">│</span><span style="background:#000000"> </span><span style="color:#ff80ff;background:#000000">$files</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold">|</span><span style="background:#000000"> </span><span style="color:#40e0ff;background:#000000">where</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">size</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold">&gt;</span><span style="background:#000000"> </span><span style="color:#ffe040;background:#000000">1kb</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold">|</span><span style="background:#000000"> </span><span style="color:#40e0ff;background:#000000">where</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">type</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold">==</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span>21+<span style="color:#757575;background:#000000">│</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold">|</span><span style="background:#000000"> </span><span style="color:#40e0ff;background:#000000">sort-by</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">size</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">--reverse</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold">|</span><span style="background:#000000"> </span><span style="color:#40e0ff;background:#000000">select</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">name</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">size</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold">|</span><span style="background:#000000"> </span><span style="color:#40e0ff;background:#000000">first</span><span style="background:#000000"> </span><span style="color:#ffe040;background:#000000">10</span><span style="background:#000000"> </span>22+<span style="color:#757575;background:#000000">│</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold">|</span><span style="background:#000000"> </span><span style="color:#40e0ff;background:#000000">rename</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">file</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000">bytes</span><span style="background:#000000"> </span>23+<span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold;text-decoration:underline">name</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold;text-decoration:underline">type</span><span style="background:#000000"> </span><span style="color:#ffffff;background:#000000;font-weight:bold;text-decoration:underline">size</span><span style="background:#000000"> </span>24+<span style="background:#40e0ff"> </span><span style="color:#000000;background:#40e0ff;font-weight:bold">❯</span><span style="background:#40e0ff"> </span><span style="color:#000000;background:#40e0ff;font-weight:bold">Notebook</span><span style="background:#40e0ff"> </span><span style="background:#1f1f1f"> </span><span style="color:#959595;background:#1f1f1f">+</span><span style="background:#1f1f1f"> </span><span style="color:#757575;background:#1f1f1f">│</span><span style="background:#1f1f1f"> </span><span style="color:#ffffff;background:#1f1f1f">untitled</span><span style="background:#1f1f1f"> </span><span style="color:#959595;background:#1f1f1f">modified</span><span style="background:#1f1f1f"> </span><span style="background:#3a3a3a"> </span><span style="color:#ffffff;background:#3a3a3a">Ctrl+K</span><span style="background:#3a3a3a"> </span><span style="background:#1f1f1f"> </span><span style="color:#959595;background:#1f1f1f">search</span></pre>\ No newline at end of file
A e2e/testdata/screens/notebook-wrap-narrow-light.html +24 -0
@@ -0,0 +1,24 @@
1+<pre class="screen"> <span style="text-decoration:underline">F</span>ile <span style="text-decoration:underline">E</span>dit <span style="text-decoration:underline">V</span>iew <span style="text-decoration:underline">I</span>nsert F<span style="text-decoration:underline">o</span>rmat <span style="text-decoration:underline">D</span>ata <span style="text-decoration:underline">H</span>elp <span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">NOTEBOOK</span><span style="background:var(--vt-palette-6)"> </span>2+<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">cell</span><span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">4</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">reads</span> <span style="color:var(--vt-palette-0)">$files</span>3+<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">Enter</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">edit</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">Shift+Enter</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">run</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">a</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">add</span> <span style="color:var(--vt-palette-0)">above</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">b</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">below</span> <span style="background:var(--vt-palette-7)"> </span>4+ Notebook 4 cells5+ <span style="font-weight:bold;text-decoration:underline">name</span> <span style="font-weight:bold;text-decoration:underline">type</span> <span style="font-weight:bold;text-decoration:underline">size</span>6+ 012 file 4.2 kB7+ CLAUDE.md file 1.0 kB8+ README.md file 9.8 kB9+ demos dir 3.1 kB10+ go.mod file 812 B11+12+ <span style="color:var(--vt-palette-0)">[2]</span> <span style="color:var(--vt-palette-0)">✓</span> <span style="color:var(--vt-palette-0)">&lt;1s</span>13+<span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-5)">$files</span> <span style="font-weight:bold">|</span> <span style="color:var(--vt-palette-4)">where</span> size <span style="font-weight:bold">&gt;</span> <span style="color:var(--vt-palette-1)">1kb</span>14+ <span style="font-weight:bold;text-decoration:underline">name</span> <span style="font-weight:bold;text-decoration:underline">type</span> <span style="font-weight:bold;text-decoration:underline">size</span>15+ 012 file 4.2 kB16+ README.md file 9.8 kB17+ demos dir 3.1 kB18+19+<span style="background:var(--vt-palette-4)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-4)">[3]</span><span style="background:var(--vt-palette-4)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-4)">✓</span><span style="background:var(--vt-palette-4)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-4)">&lt;1s</span><span style="background:var(--vt-palette-4)"> </span>20+<span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-5)">$files</span> <span style="font-weight:bold">|</span> <span style="color:var(--vt-palette-4)">where</span> size <span style="font-weight:bold">&gt;</span> <span style="color:var(--vt-palette-1)">1kb</span> <span style="font-weight:bold">|</span> <span style="color:var(--vt-palette-4)">where</span> type <span style="font-weight:bold">==</span> file21+<span style="color:var(--vt-palette-8)">│</span> <span style="font-weight:bold">|</span> <span style="color:var(--vt-palette-4)">sort-by</span> size --reverse <span style="font-weight:bold">|</span> <span style="color:var(--vt-palette-4)">select</span> name size <span style="font-weight:bold">|</span> <span style="color:var(--vt-palette-4)">first</span> <span style="color:var(--vt-palette-1)">10</span>22+<span style="color:var(--vt-palette-8)">│</span> <span style="font-weight:bold">|</span> <span style="color:var(--vt-palette-4)">rename</span> file bytes23+ <span style="font-weight:bold;text-decoration:underline">name</span> <span style="font-weight:bold;text-decoration:underline">type</span> <span style="font-weight:bold;text-decoration:underline">size</span>24+<span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">❯</span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">Notebook</span><span style="background:var(--vt-palette-6)"> </span> <span style="color:var(--vt-palette-0)">+</span> <span style="color:var(--vt-palette-8)">│</span> untitled <span style="color:var(--vt-palette-0)">modified</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">Ctrl+K</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-0)">search</span></pre>\ No newline at end of file
A e2e/testdata/screens/notebook-wrap-narrow.html +24 -0
@@ -0,0 +1,24 @@
1+<pre class="screen"> <span style="text-decoration:underline">F</span>ile <span style="text-decoration:underline">E</span>dit <span style="text-decoration:underline">V</span>iew <span style="text-decoration:underline">I</span>nsert F<span style="text-decoration:underline">o</span>rmat <span style="text-decoration:underline">D</span>ata <span style="text-decoration:underline">H</span>elp <span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">NOTEBOOK</span><span style="background:var(--vt-palette-6)"> </span>2+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">cell</span><span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">4</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">reads</span> <span style="color:var(--vt-palette-8)">$files</span>3+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Enter</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">edit</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Shift+Enter</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">run</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">a</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">add</span> <span style="color:var(--vt-palette-8)">above</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">b</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">below</span> <span style="background:var(--vt-palette-8)"> </span>4+ Notebook 4 cells5+ <span style="font-weight:bold;text-decoration:underline">name</span> <span style="font-weight:bold;text-decoration:underline">type</span> <span style="font-weight:bold;text-decoration:underline">size</span>6+ 012 file 4.2 kB7+ CLAUDE.md file 1.0 kB8+ README.md file 9.8 kB9+ demos dir 3.1 kB10+ go.mod file 812 B11+12+ <span style="color:var(--vt-palette-8)">[2]</span> <span style="color:var(--vt-palette-8)">✓</span> <span style="color:var(--vt-palette-8)">&lt;1s</span>13+<span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-5)">$files</span> <span style="font-weight:bold">|</span> <span style="color:var(--vt-palette-6)">where</span> size <span style="font-weight:bold">&gt;</span> <span style="color:var(--vt-palette-3)">1kb</span>14+ <span style="font-weight:bold;text-decoration:underline">name</span> <span style="font-weight:bold;text-decoration:underline">type</span> <span style="font-weight:bold;text-decoration:underline">size</span>15+ 012 file 4.2 kB16+ README.md file 9.8 kB17+ demos dir 3.1 kB18+19+<span style="background:var(--vt-palette-4)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-4)">[3]</span><span style="background:var(--vt-palette-4)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-4)">✓</span><span style="background:var(--vt-palette-4)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-4)">&lt;1s</span><span style="background:var(--vt-palette-4)"> </span>20+<span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-5)">$files</span> <span style="font-weight:bold">|</span> <span style="color:var(--vt-palette-6)">where</span> size <span style="font-weight:bold">&gt;</span> <span style="color:var(--vt-palette-3)">1kb</span> <span style="font-weight:bold">|</span> <span style="color:var(--vt-palette-6)">where</span> type <span style="font-weight:bold">==</span> file21+<span style="color:var(--vt-palette-8)">│</span> <span style="font-weight:bold">|</span> <span style="color:var(--vt-palette-6)">sort-by</span> size --reverse <span style="font-weight:bold">|</span> <span style="color:var(--vt-palette-6)">select</span> name size <span style="font-weight:bold">|</span> <span style="color:var(--vt-palette-6)">first</span> <span style="color:var(--vt-palette-3)">10</span>22+<span style="color:var(--vt-palette-8)">│</span> <span style="font-weight:bold">|</span> <span style="color:var(--vt-palette-6)">rename</span> file bytes23+ <span style="font-weight:bold;text-decoration:underline">name</span> <span style="font-weight:bold;text-decoration:underline">type</span> <span style="font-weight:bold;text-decoration:underline">size</span>24+<span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">❯</span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">Notebook</span><span style="background:var(--vt-palette-6)"> </span> <span style="color:var(--vt-palette-8)">+</span> <span style="color:var(--vt-palette-8)">│</span> untitled <span style="color:var(--vt-palette-8)">modified</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+K</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">search</span></pre>\ No newline at end of file
A e2e/testdata/screens/notebook-wrap.html +24 -0
@@ -0,0 +1,24 @@
1+<pre class="screen"> <span style="text-decoration:underline">F</span>ile <span style="text-decoration:underline">E</span>dit <span style="text-decoration:underline">V</span>iew <span style="text-decoration:underline">I</span>nsert F<span style="text-decoration:underline">o</span>rmat <span style="text-decoration:underline">D</span>ata <span style="text-decoration:underline">H</span>elp <span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">EDIT</span><span style="background:var(--vt-palette-6)"> </span>2+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">cell</span><span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">4</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">a</span> <span style="color:var(--vt-palette-8)">nushell</span> <span style="color:var(--vt-palette-8)">pipeline</span>3+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Shift+Enter</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">run</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+Enter</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">run</span> <span style="color:var(--vt-palette-8)">here</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Tab</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">complete</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Esc</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">done</span>4+ Notebook 4 cells5+ <span style="color:var(--vt-palette-8)">[1]</span> <span style="color:var(--vt-palette-8)">files</span> <span style="color:var(--vt-palette-8)">✓</span> <span style="color:var(--vt-palette-8)">&lt;1s</span>6+<span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-5)">files</span> <span style="font-weight:bold">=</span> <span style="color:var(--vt-palette-6)">ls</span>7+ <span style="font-weight:bold;text-decoration:underline">name</span> <span style="font-weight:bold;text-decoration:underline">type</span> <span style="font-weight:bold;text-decoration:underline">size</span>8+ 012 file 4.2 kB9+ CLAUDE.md file 1.0 kB10+ README.md file 9.8 kB11+ demos dir 3.1 kB12+ go.mod file 812 B13+14+ <span style="color:var(--vt-palette-8)">[2]</span> <span style="color:var(--vt-palette-8)">✓</span> <span style="color:var(--vt-palette-8)">&lt;1s</span>15+<span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-5)">$files</span> <span style="font-weight:bold">|</span> <span style="color:var(--vt-palette-6)">where</span> size <span style="font-weight:bold">&gt;</span> <span style="color:var(--vt-palette-3)">1kb</span>16+ <span style="font-weight:bold;text-decoration:underline">name</span> <span style="font-weight:bold;text-decoration:underline">type</span> <span style="font-weight:bold;text-decoration:underline">size</span>17+ 012 file 4.2 kB18+ README.md file 9.8 kB19+ demos dir 3.1 kB20+21+<span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6)">[</span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6)">]</span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6)">not</span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6)">run</span><span style="background:var(--vt-palette-6)"> </span>22+<span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-5)">$files</span> <span style="font-weight:bold">|</span> <span style="color:var(--vt-palette-6)">where</span> size <span style="font-weight:bold">&gt;</span> <span style="color:var(--vt-palette-3)">1kb</span> <span style="font-weight:bold">|</span> <span style="color:var(--vt-palette-6)">where</span> type <span style="font-weight:bold">==</span> file <span style="font-weight:bold">|</span> <span style="color:var(--vt-palette-6)">sort-by</span> size --reverse23+<span style="color:var(--vt-palette-8)">│</span> <span style="font-weight:bold">|</span> <span style="color:var(--vt-palette-6)">select</span> name size <span style="font-weight:bold">|</span> <span style="color:var(--vt-palette-6)">first</span> <span style="color:var(--vt-palette-3)">10</span> <span style="font-weight:bold">|</span> <span style="color:var(--vt-palette-6)">rename</span> file bytes24+<span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">❯</span><span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">Notebook</span><span style="background:var(--vt-palette-6)"> </span> <span style="color:var(--vt-palette-8)">+</span> <span style="color:var(--vt-palette-8)">│</span> untitled <span style="color:var(--vt-palette-8)">modified</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+K</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">search</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+/</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">shortcuts</span></pre>\ No newline at end of file
M e2e/testdata/screens/palette-narrow.html +2 -2
@@ -10,7 +10,7 @@
1010 <span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-3);font-weight:bold">S</span>ort range Z to <span style="color:var(--vt-palette-3);font-weight:bold">A</span> <span style="color:var(--vt-palette-8)">│</span>1111 <span style="color:var(--vt-palette-8)">│</span> Sort <span style="color:var(--vt-palette-3);font-weight:bold">s</span>heet <span style="color:var(--vt-palette-3);font-weight:bold">A</span> to Z <span style="color:var(--vt-palette-8)">│</span>1212 <span style="color:var(--vt-palette-8)">│</span> Sort <span style="color:var(--vt-palette-3);font-weight:bold">s</span>heet Z to <span style="color:var(--vt-palette-3);font-weight:bold">A</span> <span style="color:var(--vt-palette-8)">│</span>13+<span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-8)">Show</span> <span style="color:var(--vt-palette-8)">all</span> <span style="color:var(--vt-palette-8)">of</span> <span style="color:var(--vt-palette-8)">the</span> <span style="color:var(--vt-palette-8)">output</span> <span style="color:var(--vt-palette-8)">o</span> <span style="color:var(--vt-palette-8)">│</span>1314 <span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-8)">Ask</span> <span style="color:var(--vt-palette-8)">JEV</span> <span style="color:var(--vt-palette-8)">again</span> <span style="color:var(--vt-palette-8)">│</span>14-<span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-3);font-weight:bold">S</span>ort r<span style="color:var(--vt-palette-3);font-weight:bold">a</span>nge <span style="color:var(--vt-palette-8)">│</span>15-<span style="color:var(--vt-palette-8)">└──────────────────────────────────────────────</span> <span style="color:var(--vt-palette-8)">49</span> <span style="color:var(--vt-palette-8)">of</span> <span style="color:var(--vt-palette-8)">187</span> <span style="color:var(--vt-palette-8)">─┘</span>15+<span style="color:var(--vt-palette-8)">└──────────────────────────────────────────────</span> <span style="color:var(--vt-palette-8)">53</span> <span style="color:var(--vt-palette-8)">of</span> <span style="color:var(--vt-palette-8)">208</span> <span style="color:var(--vt-palette-8)">─┘</span>1616 Save the sheet u… <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Up/Down</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">move</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Enter</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">run</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Esc</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">close</span></pre>\ No newline at end of file
M e2e/testdata/screens/palette-search-light.html +1 -1
@@ -14,7 +14,7 @@
1414 <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">10</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-8)">│</span> Reset <span style="color:var(--vt-palette-4);font-weight:bold">col</span>umn width <span style="color:var(--vt-palette-0)">Format</span> <span style="color:var(--vt-palette-8)">│</span>1515 <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">11</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-8)">│</span> Insert <span style="color:var(--vt-palette-4);font-weight:bold">col</span>umns right <span style="color:var(--vt-palette-0)">Insert</span> <span style="color:var(--vt-palette-8)">│</span>1616 <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">12</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-8)">│</span> Delete rows or <span style="color:var(--vt-palette-4);font-weight:bold">col</span>umns <span style="color:var(--vt-palette-0)">Edit</span> <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">Ctrl+Alt+-</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-8)">│</span>17-<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">13</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-8)">└────────────────────────────────────────────────────────────────</span> <span style="color:var(--vt-palette-0)">32</span> <span style="color:var(--vt-palette-0)">of</span> <span style="color:var(--vt-palette-0)">187</span> <span style="color:var(--vt-palette-8)">─┘</span>17+<span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">13</span><span style="background:var(--vt-palette-7)"> </span> <span style="color:var(--vt-palette-8)">└────────────────────────────────────────────────────────────────</span> <span style="color:var(--vt-palette-0)">39</span> <span style="color:var(--vt-palette-0)">of</span> <span style="color:var(--vt-palette-0)">208</span> <span style="color:var(--vt-palette-8)">─┘</span>1818 <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">14</span><span style="background:var(--vt-palette-7)"> </span>1919 <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">15</span><span style="background:var(--vt-palette-7)"> </span>2020 <span style="background:var(--vt-palette-7)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-7)">16</span><span style="background:var(--vt-palette-7)"> </span>
M e2e/testdata/screens/palette-search.html +1 -1
@@ -14,7 +14,7 @@
1414 <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">10</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Reset <span style="color:var(--vt-palette-3);font-weight:bold">col</span>umn width <span style="color:var(--vt-palette-8)">Format</span> <span style="color:var(--vt-palette-8)">│</span>1515 <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">11</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Insert <span style="color:var(--vt-palette-3);font-weight:bold">col</span>umns right <span style="color:var(--vt-palette-8)">Insert</span> <span style="color:var(--vt-palette-8)">│</span>1616 <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">12</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Delete rows or <span style="color:var(--vt-palette-3);font-weight:bold">col</span>umns <span style="color:var(--vt-palette-8)">Edit</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+Alt+-</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>17-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">13</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">└────────────────────────────────────────────────────────────────</span> <span style="color:var(--vt-palette-8)">32</span> <span style="color:var(--vt-palette-8)">of</span> <span style="color:var(--vt-palette-8)">187</span> <span style="color:var(--vt-palette-8)">─┘</span>17+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">13</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">└────────────────────────────────────────────────────────────────</span> <span style="color:var(--vt-palette-8)">39</span> <span style="color:var(--vt-palette-8)">of</span> <span style="color:var(--vt-palette-8)">208</span> <span style="color:var(--vt-palette-8)">─┘</span>1818 <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">14</span><span style="background:var(--vt-palette-8)"> </span>1919 <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">15</span><span style="background:var(--vt-palette-8)"> </span>2020 <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">16</span><span style="background:var(--vt-palette-8)"> </span>
M e2e/testdata/screens/palette-wide.html +6 -6
@@ -6,15 +6,15 @@
66 <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">2</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-3);font-weight:bold">Sel</span>ect rows <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Shift+Space</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>77 <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">3</span><span style="background:var(--vt-palette-8)"> </span> Rent 1450 <span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-8)">Select</span> <span style="color:var(--vt-palette-8)">chart</span> <span style="color:var(--vt-palette-8)">│</span>88 <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">4</span><span style="background:var(--vt-palette-8)"> </span> Groceries 612.4 <span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-3);font-weight:bold">Sel</span>ect columns <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+Space</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>9-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">5</span><span style="background:var(--vt-palette-8)"> </span> Transit 96 <span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-8)">Move</span> <span style="color:var(--vt-palette-8)">sheet</span> <span style="color:var(--vt-palette-8)">left</span> <span style="color:var(--vt-palette-8)">Edit</span> <span style="color:var(--vt-palette-8)">›</span> <span style="color:var(--vt-palette-8)">Sheet</span> <span style="color:var(--vt-palette-8)">│</span>10-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">6</span><span style="background:var(--vt-palette-8)"> </span> Savings r <span style="color:var(--vt-palette-1);text-decoration:underline wavy var(--vt-palette-1)">#DIV/0!</span> <span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-3);font-weight:bold">S</span>h<span style="color:var(--vt-palette-3);font-weight:bold">el</span>l <span style="color:var(--vt-palette-8)">Data</span> <span style="color:var(--vt-palette-8)">│</span>9+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">5</span><span style="background:var(--vt-palette-8)"> </span> Transit 96 <span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-8)">Run</span> <span style="color:var(--vt-palette-8)">cell</span> <span style="color:var(--vt-palette-8)">and</span> <span style="color:var(--vt-palette-8)">select</span> <span style="color:var(--vt-palette-8)">next</span> <span style="color:var(--vt-palette-8)">Data</span> <span style="color:var(--vt-palette-8)">›</span> <span style="color:var(--vt-palette-8)">Shell</span> <span style="color:var(--vt-palette-8)">Shift+Enter</span> <span style="color:var(--vt-palette-8)">│</span>10+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">6</span><span style="background:var(--vt-palette-8)"> </span> Savings r <span style="color:var(--vt-palette-1);text-decoration:underline wavy var(--vt-palette-1)">#DIV/0!</span> <span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-8)">Move</span> <span style="color:var(--vt-palette-8)">sheet</span> <span style="color:var(--vt-palette-8)">left</span> <span style="color:var(--vt-palette-8)">Edit</span> <span style="color:var(--vt-palette-8)">›</span> <span style="color:var(--vt-palette-8)">Sheet</span> <span style="color:var(--vt-palette-8)">│</span>1111 <span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">7</span><span style="background:var(--vt-palette-6)"> </span> Total <span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6)">2158.4</span><span style="background:var(--vt-palette-6)"> </span> <span style="color:var(--vt-palette-8)">│</span> De<span style="color:var(--vt-palette-3);font-weight:bold">sel</span>ect <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Esc</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>1212 <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">8</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> In<span style="color:var(--vt-palette-3);font-weight:bold">se</span>rt columns <span style="color:var(--vt-palette-3);font-weight:bold">l</span>eft <span style="color:var(--vt-palette-8)">Insert</span> <span style="color:var(--vt-palette-8)">│</span>13-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">9</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-8)">Stop</span> <span style="color:var(--vt-palette-8)">shell</span> <span style="color:var(--vt-palette-8)">command</span> <span style="color:var(--vt-palette-8)">Data</span> <span style="color:var(--vt-palette-8)">›</span> <span style="color:var(--vt-palette-8)">Shell</span> <span style="color:var(--vt-palette-8)">regio…</span> <span style="color:var(--vt-palette-8)">│</span>14-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">10</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-3);font-weight:bold">S</span>av<span style="color:var(--vt-palette-3);font-weight:bold">e</span> <span style="color:var(--vt-palette-8)">Fi</span><span style="color:var(--vt-palette-3);font-weight:bold">l</span><span style="color:var(--vt-palette-8)">e</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+S</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>15-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">11</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-3);font-weight:bold">S</span>av<span style="color:var(--vt-palette-3);font-weight:bold">e</span> as <span style="color:var(--vt-palette-8)">Fi</span><span style="color:var(--vt-palette-3);font-weight:bold">l</span><span style="color:var(--vt-palette-8)">e</span> <span style="color:var(--vt-palette-8)">│</span>13+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">9</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-3);font-weight:bold">S</span>av<span style="color:var(--vt-palette-3);font-weight:bold">e</span> <span style="color:var(--vt-palette-8)">Fi</span><span style="color:var(--vt-palette-3);font-weight:bold">l</span><span style="color:var(--vt-palette-8)">e</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+S</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>14+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">10</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-3);font-weight:bold">S</span>av<span style="color:var(--vt-palette-3);font-weight:bold">e</span> as <span style="color:var(--vt-palette-8)">Fi</span><span style="color:var(--vt-palette-3);font-weight:bold">l</span><span style="color:var(--vt-palette-8)">e</span> <span style="color:var(--vt-palette-8)">│</span>15+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">11</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-8)">Paste</span> <span style="color:var(--vt-palette-8)">cell</span> <span style="color:var(--vt-palette-8)">Data</span> <span style="color:var(--vt-palette-8)">›</span> <span style="color:var(--vt-palette-8)">Shell</span> <span style="color:var(--vt-palette-8)">v</span> <span style="color:var(--vt-palette-8)">│</span>1616 <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">12</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> In<span style="color:var(--vt-palette-3);font-weight:bold">se</span>rt rows be<span style="color:var(--vt-palette-3);font-weight:bold">l</span>ow <span style="color:var(--vt-palette-8)">Insert</span> <span style="color:var(--vt-palette-8)">│</span>17-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">13</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">└────────────────────────────────────────────────────────────────</span> <span style="color:var(--vt-palette-8)">32</span> <span style="color:var(--vt-palette-8)">of</span> <span style="color:var(--vt-palette-8)">187</span> <span style="color:var(--vt-palette-8)">─┘</span>17+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">13</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">└────────────────────────────────────────────────────────────────</span> <span style="color:var(--vt-palette-8)">50</span> <span style="color:var(--vt-palette-8)">of</span> <span style="color:var(--vt-palette-8)">208</span> <span style="color:var(--vt-palette-8)">─┘</span>1818 <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">14</span><span style="background:var(--vt-palette-8)"> </span>1919 <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">15</span><span style="background:var(--vt-palette-8)"> </span>2020 <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">16</span><span style="background:var(--vt-palette-8)"> </span>
M e2e/testdata/screens/palette.html +1 -1
@@ -14,7 +14,7 @@
1414 <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">10</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Download as JSON <span style="color:var(--vt-palette-8)">File</span> <span style="color:var(--vt-palette-8)">›</span> <span style="color:var(--vt-palette-8)">Download</span> <span style="color:var(--vt-palette-8)">│</span>1515 <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">11</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Download as NUON <span style="color:var(--vt-palette-8)">File</span> <span style="color:var(--vt-palette-8)">›</span> <span style="color:var(--vt-palette-8)">Download</span> <span style="color:var(--vt-palette-8)">│</span>1616 <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">12</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> Decimal arithmetic <span style="color:var(--vt-palette-8)">File</span> <span style="color:var(--vt-palette-8)">›</span> <span style="color:var(--vt-palette-8)">Settings</span> <span style="color:var(--vt-palette-8)">│</span>17-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">13</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">└───────────────────────────────────────────────────────────────</span> <span style="color:var(--vt-palette-8)">187</span> <span style="color:var(--vt-palette-8)">of</span> <span style="color:var(--vt-palette-8)">187</span> <span style="color:var(--vt-palette-8)">─┘</span>17+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">13</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">└───────────────────────────────────────────────────────────────</span> <span style="color:var(--vt-palette-8)">208</span> <span style="color:var(--vt-palette-8)">of</span> <span style="color:var(--vt-palette-8)">208</span> <span style="color:var(--vt-palette-8)">─┘</span>1818 <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">14</span><span style="background:var(--vt-palette-8)"> </span>1919 <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">15</span><span style="background:var(--vt-palette-8)"> </span>2020 <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">16</span><span style="background:var(--vt-palette-8)"> </span>
M internal/config/registry.go +13 -7
@@ -119,14 +119,20 @@ var Options = []Option{
119119 "The grid itself is 1,048,576 rows by 16,384 columns (A to XFD) whatever this is."},120120 121121 {Name: "shell", Kind: Enum, Group: GroupShell, Default: "ask", Values: []string{"off", "ask", "on"}, Env: []string{"O12_SHELL"}, Live: true,122- Desc: "Whether notebook sheets run nushell commands (docs/nushell/notebooks.md). `ask` runs what you type " +123- "and asks once before running the commands of a file made on another computer; `on` never asks; " +124- "`off` runs none. Opening a file never runs its commands."},122+ Desc: "Whether notebooks run their code cells (docs/nushell/notebooks.md). `ask` runs what you write " +123+ "and asks once before running the cells of a file made on another computer; `on` never asks; " +124+ "`off` runs none. Opening a file never runs its cells."},125125 {Name: "nu-timeout", Kind: Duration, Group: GroupShell, Default: "30s", Env: []string{"O12_NU_TIMEOUT"}, Live: true,126- Desc: "Stop a notebook's command that runs longer than this; 0 lets it run until Esc stops it."},126+ Desc: "Stop a notebook cell that runs longer than this; 0 lets it run until Stop (i i) stops it."},127127 {Name: "nu-config", Kind: Bool, Group: GroupShell, Default: "false", Env: []string{"O12_NU_CONFIG"}, Live: true,128- Desc: "Run notebook commands with your nushell config files (config.nu, env.nu) rather than " +128+ Desc: "Run notebook cells with your nushell config files (config.nu, env.nu) rather than " +129129 "`nu --no-config-file`, for your own commands and aliases."},130+ {Name: "nu-save-cell-kb", Kind: Int, Group: GroupShell, Default: "1024", Env: []string{"O12_NU_SAVE_CELL_KB"}, Live: true,131+ Desc: "The largest output of one cell a saved file keeps, in kilobytes of NUON; a larger one is left out " +132+ "and shows `not saved; run to see` when the file is opened."},133+ {Name: "nu-save-notebook-kb", Kind: Int, Group: GroupShell, Default: "8192", Env: []string{"O12_NU_SAVE_NOTEBOOK_KB"}, Live: true,134+ Desc: "How much of all its cells' outputs a saved file keeps, in kilobytes of NUON: the outputs that fit, " +135+ "in the notebook's order."},130136 131137 {Name: "jev-api-key-command", Kind: Command, Group: GroupJEV,132138 Desc: "A command that prints the TypeSafe API key, used when TYPESAFE_API_KEY isn't set and the " +
@@ -165,8 +171,8 @@ var Options = []Option{
165171 {Name: "serve-max-sessions", Kind: Int, Group: GroupServe, Default: "8",166172 Desc: "How many 012 serve sessions may run at once; more are turned away."},167173 {Name: "serve-shell", Kind: Bool, Group: GroupServe, Default: "false",168- Desc: "Let 012 serve sessions run notebooks' nushell commands, as the user 012 serve runs as, " +169- "following the shell option. Off, served notebooks show their regions but run nothing."},174+ Desc: "Let 012 serve sessions run notebooks' code cells, as the user 012 serve runs as, " +175+ "following the shell option. Off, served notebooks show their cells and saved outputs but run nothing."},170176 171177 {Name: "config-file", Kind: Path, Group: GroupFiles, Repeat: true,172178 Desc: "Read another config file after this one, relative to this file's directory. " +
A internal/diff/cells.go +285 -0
@@ -0,0 +1,285 @@
1+package diff2+3+import (4+ "encoding/json"5+ "maps"6+ "slices"7+ "strconv"8+ "strings"9+10+ "github.com/FelineStateMachine/012/internal/fileio"11+ "github.com/FelineStateMachine/012/internal/formula"12+ "github.com/FelineStateMachine/012/internal/functions"13+ "github.com/FelineStateMachine/012/internal/nuon"14+ "github.com/FelineStateMachine/012/internal/sheet"15+)16+17+// cellParts are the parts of a cell that change separately: what was18+// typed, its note, and its formatting (every other field of the cell19+// in the file), which merging keeps apart too.20+type cellParts struct {21+ input, note string22+ format json.RawMessage // a compact object, or nil for none23+}24+25+// splitCell reads a cell as the file stores it: its input alone, or an26+// object with the input, the note and the formatting.27+func splitCell(raw json.RawMessage) cellParts {28+ var p cellParts29+ if raw == nil || json.Unmarshal(raw, &p.input) == nil {30+ return p31+ }32+ var fields map[string]json.RawMessage33+ if json.Unmarshal(raw, &fields) != nil {34+ return p35+ }36+ json.Unmarshal(fields["input"], &p.input)37+ json.Unmarshal(fields["note"], &p.note)38+ delete(fields, "input")39+ delete(fields, "note")40+ if len(fields) > 0 {41+ p.format, _ = json.Marshal(fields) // keys sorted42+ }43+ return p44+}45+46+// joinCell is the inverse of splitCell; nil for a cell with nothing.47+func joinCell(p cellParts) json.RawMessage {48+ if p.format == nil && p.note == "" {49+ if p.input == "" {50+ return nil51+ }52+ raw, _ := json.Marshal(p.input)53+ return raw54+ }55+ fields := map[string]json.RawMessage{}56+ json.Unmarshal(p.format, &fields)57+ if p.input != "" {58+ fields["input"], _ = json.Marshal(p.input)59+ }60+ if p.note != "" {61+ fields["note"], _ = json.Marshal(p.note)62+ }63+ raw, _ := json.Marshal(fields)64+ return raw65+}66+67+// formatText describes a cell's formatting in words from its fields:68+// currency, decimals 2, bold.69+func formatText(raw json.RawMessage) string {70+ if raw == nil {71+ return ""72+ }73+ var fields map[string]json.RawMessage74+ if json.Unmarshal(raw, &fields) != nil {75+ return string(raw)76+ }77+ var parts []string78+ for _, k := range slices.Sorted(maps.Keys(fields)) {79+ v := string(fields[k])80+ var s string81+ switch {82+ case v == "true":83+ parts = append(parts, k)84+ case k == "format" && json.Unmarshal(fields[k], &s) == nil:85+ parts = append([]string{s}, parts...)86+ case json.Unmarshal(fields[k], &s) == nil:87+ parts = append(parts, k+" "+s)88+ default:89+ parts = append(parts, k+" "+v)90+ }91+ }92+ return strings.Join(parts, ", ")93+}94+95+// sheetDiff compares a sheet's cells, regions and other fields; name is96+// what the changes call the sheet.97+func sheetDiff(a, b side, name string) []Change {98+ out := cellChanges(a, b, name)99+ out = append(out, notebookChanges(name, field(a.raw, "notebookCells"), field(b.raw, "notebookCells"))...)100+ out = append(out, listChanges(KindRegion, name, field(a.raw, "regions"), field(b.raw, "regions"))...)101+ keys := map[string]bool{}102+ for _, s := range []*rawSheet{a.raw, b.raw} {103+ for k := range s.fields {104+ keys[k] = true105+ }106+ }107+ for _, k := range slices.Sorted(maps.Keys(keys)) {108+ if k != "regions" && k != "notebookCells" {109+ out = append(out, fieldChanges(KindLayout, name, k, a.raw.fields[k], b.raw.fields[k])...)110+ }111+ }112+ return out113+}114+115+func field(s *rawSheet, k string) json.RawMessage { return s.fields[k] }116+117+// cellChanges compares each cell either side has contents, formatting118+// or a value in, row by row.119+func cellChanges(a, b side, name string) []Change {120+ var out []Change121+ for _, at := range cellAddrs(a, b) {122+ out = append(out, cellChange(a, b, name, at)...)123+ }124+ return out125+}126+127+// cellAddrs are the cells either side has something in, in row order.128+func cellAddrs(a, b side) []sheet.Addr {129+ addrs := map[sheet.Addr]bool{}130+ for _, sd := range []side{a, b} {131+ for k := range sd.raw.cells {132+ if at, ok := sheet.ParseAddr(k); ok {133+ addrs[at] = true134+ }135+ }136+ if sd.s != nil {137+ for _, at := range sd.s.Addrs() {138+ addrs[at] = true139+ }140+ }141+ }142+ return slices.SortedFunc(maps.Keys(addrs), byRow)143+}144+145+func byRow(x, y sheet.Addr) int {146+ if x.Row != y.Row {147+ return x.Row - y.Row148+ }149+ return x.Col - y.Col150+}151+152+// cellChange compares one cell's input, value, format and note.153+func cellChange(a, b side, name string, at sheet.Addr) []Change {154+ key := at.String()155+ pa, pb := splitCell(a.raw.cells[key]), splitCell(b.raw.cells[key])156+ item := func(field, old, new string) Change { return textChange(KindCell, name, key, field, old, new) }157+ var out []Change158+ if pa.input != pb.input {159+ out = append(out, item("input", pa.input, pb.input))160+ }161+ if showsValue(pa, pb) && !a.vol[at] && !b.vol[at] {162+ if c, ok := valueChange(a.s, b.s, at); ok {163+ c.Sheet, c.Item = name, key164+ out = append(out, c)165+ }166+ }167+ if !equal(pa.format, pb.format) {168+ c := item("format", formatText(pa.format), formatText(pb.format))169+ c.Old, c.New = recordValue(pa.format), recordValue(pb.format)170+ out = append(out, c)171+ }172+ if pa.note != pb.note {173+ out = append(out, item("note", pa.note, pb.note))174+ }175+ return out176+}177+178+// showsValue reports whether a cell's value says more than its input:179+// it's a formula on either side, or it has no input on either (a value180+// an array spilled or a region's table).181+func showsValue(a, b cellParts) bool {182+ return sheet.IsFormulaEntry(a.input) || sheet.IsFormulaEntry(b.input) || a.input == "" && b.input == ""183+}184+185+// valueChange compares the values the two sheets compute at a.186+func valueChange(a, b *sheet.Sheet, at sheet.Addr) (Change, bool) {187+ oldV, oldT := cellValue(a, at)188+ newV, newT := cellValue(b, at)189+ if oldT == newT && nuon.Equal(oldV, newV) {190+ return Change{}, false191+ }192+ return Change{Kind: KindCell, Field: "value", Old: oldV, New: newV, oldText: oldT, newText: newT}, true193+}194+195+// cellValue is a cell's value typed by its format, and as it shows.196+func cellValue(s *sheet.Sheet, at sheet.Addr) (nuon.Value, string) {197+ if s == nil || s.Value(at).Kind == sheet.Empty {198+ return nuon.NullValue(), ""199+ }200+ return fileio.CellValue(fileio.SnapCell{Value: s.Value(at), Format: s.DisplayFormat(at)}), s.LocalText(at)201+}202+203+// recordValue is a JSON object as a NUON record, or null.204+func recordValue(raw json.RawMessage) nuon.Value {205+ if raw == nil {206+ return nuon.NullValue()207+ }208+ v, err := nuon.Parse(raw)209+ if err != nil {210+ return nuon.StringValue(string(raw))211+ }212+ return v213+}214+215+// volatiles are the cells of w whose values change on every216+// recalculation, which comparing would report as changed each time:217+// formulas calling a volatile function (NOW, RAND), what reads them,218+// directly or not, on any sheet, and the cells their arrays spill into.219+func volatiles(w *sheet.Workbook) map[*sheet.Sheet]map[sheet.Addr]bool {220+ out := map[*sheet.Sheet]map[sheet.Addr]bool{}221+ if w == nil {222+ return out223+ }224+ var queue []sheet.Target225+ mark := func(s *sheet.Sheet, at sheet.Addr) {226+ if out[s] == nil {227+ out[s] = map[sheet.Addr]bool{}228+ }229+ if !out[s][at] {230+ out[s][at] = true231+ queue = append(queue, sheet.Target{Sheet: s, Range: sheet.Rect{From: at, To: at}})232+ }233+ }234+ for _, s := range w.Sheets() {235+ for _, at := range s.Addrs() {236+ if c := s.Cell(at); c != nil && c.IsFormula() && callsVolatile(c.Input) {237+ mark(s, at)238+ }239+ }240+ }241+ for len(queue) > 0 {242+ t := queue[0]243+ queue = queue[1:]244+ if area, ok := t.Sheet.SpillArea(t.Range.From); ok {245+ for row := area.From.Row; row <= area.To.Row; row++ {246+ for col := area.From.Col; col <= area.To.Col; col++ {247+ mark(t.Sheet, sheet.Addr{Col: col, Row: row})248+ }249+ }250+ }251+ for _, d := range t.Sheet.Dependents(t.Range.From) {252+ mark(d.Sheet, d.Range.From)253+ }254+ }255+ return out256+}257+258+// callsVolatile reports whether a formula calls a volatile function.259+func callsVolatile(input string) bool {260+ n, err := sheet.Parse(input)261+ if err != nil {262+ return false263+ }264+ found := false265+ var walk func(formula.Node)266+ walk = func(n formula.Node) {267+ if call, ok := n.(formula.Call); ok {268+ if f := functions.Of(call); f != nil && f.Volatile {269+ found = true270+ }271+ }272+ formula.EachChild(n, walk)273+ }274+ walk(n)275+ return found276+}277+278+// quoted writes text as the text output shows a side: as it is, or279+// quoted when spaces at its ends or line breaks would hide.280+func quoted(s string) string {281+ if s != strings.TrimSpace(s) || strings.ContainsAny(s, "\n\t") {282+ return strconv.Quote(s)283+ }284+ return s285+}
A internal/diff/diff.go +203 -0
@@ -0,0 +1,203 @@
1+// Package diff compares and merges .012 workbooks cell by cell, for2+// 012 diff and 012 merge-driver (see docs/files/git.md). Compare lists3+// what changed between two workbooks: sheets added, removed, renamed or4+// moved, each cell's input, value, format and note, notebook cells,5+// regions, named ranges, macros and the other fields of sheets and6+// workbooks. Merge7+// combines two workbooks changed from a common one, and conflicts only8+// where both changed the same part of the same cell or field.9+//10+// Both read files as their JSON fields (raw.go), so nothing a file holds11+// goes unseen, and use the engine only to compute formulas' values,12+// which the file doesn't store. Nothing here runs a notebook cell or13+// asks JEV: values come from what the formulas compute alone.14+package diff15+16+import (17+ "bytes"18+ "fmt"19+20+ "github.com/FelineStateMachine/012/internal/nuon"21+ "github.com/FelineStateMachine/012/internal/sheet"22+)23+24+// Book is a workbook read for comparing: its file's fields and the25+// workbook they make.26+type Book struct {27+ raw *rawBook28+ wb *sheet.Workbook // nil for a workbook without sheets29+ vol map[*sheet.Sheet]map[sheet.Addr]bool30+}31+32+// Read reads a workbook file's contents. Empty contents are a workbook33+// without sheets, which is what git hands over for a file that doesn't34+// exist on one side.35+func Read(data []byte) (*Book, error) {36+ raw, err := parseRaw(data)37+ if err != nil {38+ return nil, err39+ }40+ b := &Book{raw: raw}41+ if len(raw.sheets) > 0 {42+ if b.wb, err = sheet.ReadBook(bytes.NewReader(data)); err != nil {43+ return nil, err44+ }45+ b.vol = volatiles(b.wb)46+ }47+ return b, nil48+}49+50+// Kinds of Change.51+const (52+ KindSheet = "sheet" // a sheet added, removed, renamed or moved53+ KindCell = "cell" // a cell's input, value, format or note54+ KindRegion = "region" // a notebook region or linked file55+ KindLayout = "layout" // another field of a sheet: widths, rules, charts56+ KindName = "name" // a named range57+ KindMacro = "macro" // a macro58+ KindWorkbook = "workbook" // a field of the workbook: locale, arithmetic59+)60+61+// Change is one difference. Old or New is null where the thing isn't on62+// that side.63+type Change struct {64+ Kind string65+ Sheet string // the sheet, by its name in the second workbook where it has one66+ Item string // the cell, region, name, macro or field that changed67+ // Field is what about it changed: for a cell input, value, format or68+ // note; for a sheet added, removed, renamed or moved; for a region,69+ // name or macro added, removed or its field's name; for a layout70+ // field, its key when it has keys (a column's width), else "".71+ Field string72+ Old, New nuon.Value73+ // oldText and newText are Old and New as the text output shows them.74+ oldText, newText string75+}76+77+// Where names the change's place as the text output does: Q3!B7,78+// sheet Q3, Q3 region r1, Q3 widths, name Sales.79+func (c Change) Where() string {80+ switch c.Kind {81+ case KindCell:82+ return sheet.QuoteSheet(c.Sheet) + "!" + c.Item83+ case KindSheet:84+ return "sheet " + c.Sheet85+ case KindRegion:86+ return c.Sheet + " region " + c.Item87+ case KindLayout, KindNotebook:88+ return c.Sheet + " " + c.Item89+ }90+ return c.Kind + " " + c.Item91+}92+93+// Compare lists what changed from a to b: sheets first, then each94+// sheet's cells in row order, a notebook tab's cells, its regions and95+// other fields, then the workbook's names, macros and settings.96+func Compare(a, b *Book) []Change {97+ var out []Change98+ p := matchSheets(a.raw, b.raw)99+ out = append(out, sheetChanges(a.raw, b.raw, p)...)100+ empty := &rawSheet{fields: nil, cells: nil}101+ for _, i := range p.onlyA {102+ sa := a.raw.sheets[i]103+ out = append(out, sheetDiff(a.side(sa), side{raw: empty}, sa.name)...)104+ }105+ for _, pr := range p.pairs {106+ sa, sb := a.raw.sheets[pr[0]], b.raw.sheets[pr[1]]107+ out = append(out, sheetDiff(a.side(sa), b.side(sb), sb.name)...)108+ }109+ for _, j := range p.onlyB {110+ sb := b.raw.sheets[j]111+ out = append(out, sheetDiff(side{raw: empty}, b.side(sb), sb.name)...)112+ }113+ return append(out, bookChanges(a.raw, b.raw)...)114+}115+116+// side is one sheet of a comparison: its fields, and the sheet the117+// engine made of them, nil for a sheet that isn't there.118+type side struct {119+ raw *rawSheet120+ s *sheet.Sheet121+ vol map[sheet.Addr]bool // its cells whose values change on every recalculation122+}123+124+func (b *Book) side(s *rawSheet) side {125+ sd := side{raw: s}126+ if b.wb != nil {127+ sd.s = b.wb.Lookup(s.name)128+ sd.vol = b.vol[sd.s]129+ }130+ return sd131+}132+133+// sheetChanges are the sheets added, removed, renamed and moved.134+func sheetChanges(a, b *rawBook, p pairing) []Change {135+ var out []Change136+ for _, i := range p.onlyA {137+ name := a.sheets[i].name138+ out = append(out, textChange(KindSheet, name, "", "removed", name, ""))139+ }140+ for _, j := range p.onlyB {141+ name := b.sheets[j].name142+ out = append(out, textChange(KindSheet, name, "", "added", "", name))143+ }144+ for _, pr := range p.pairs {145+ na, nb := a.sheets[pr[0]].name, b.sheets[pr[1]].name146+ if na != nb {147+ out = append(out, textChange(KindSheet, nb, "", "renamed", na, nb))148+ }149+ }150+ // Moves: the fewest sheets that, taken out, leave the rest in the151+ // same order on both sides.152+ var orderB []int153+ for _, pr := range p.pairs {154+ orderB = append(orderB, pr[1])155+ }156+ stay := increasing(orderB)157+ for k, pr := range p.pairs {158+ if !stay[k] {159+ nb := b.sheets[pr[1]].name160+ c := textChange(KindSheet, nb, "", "moved", fmt.Sprint(pr[0]+1), fmt.Sprint(pr[1]+1))161+ c.Old, c.New = nuon.IntValue(int64(pr[0]+1)), nuon.IntValue(int64(pr[1]+1))162+ out = append(out, c)163+ }164+ }165+ return out166+}167+168+// increasing marks a longest increasing run (not necessarily adjacent)169+// of vs.170+func increasing(vs []int) []bool {171+ n := len(vs)172+ length, prev := make([]int, n), make([]int, n)173+ best := -1174+ for i := range vs {175+ length[i], prev[i] = 1, -1176+ for j := range i {177+ if vs[j] < vs[i] && length[j]+1 > length[i] {178+ length[i], prev[i] = length[j]+1, j179+ }180+ }181+ if best < 0 || length[i] > length[best] {182+ best = i183+ }184+ }185+ out := make([]bool, n)186+ for i := best; i >= 0; i = prev[i] {187+ out[i] = true188+ }189+ return out190+}191+192+// textChange is a change whose sides are text, "" for none.193+func textChange(kind, sheetName, item, field, old, new string) Change {194+ return Change{Kind: kind, Sheet: sheetName, Item: item, Field: field,195+ Old: textValue(old), New: textValue(new), oldText: old, newText: new}196+}197+198+func textValue(s string) nuon.Value {199+ if s == "" {200+ return nuon.NullValue()201+ }202+ return nuon.StringValue(s)203+}
A internal/diff/diff_test.go +216 -0
@@ -0,0 +1,216 @@
1+package diff2+3+import (4+ "bytes"5+ "strings"6+ "testing"7+8+ "github.com/FelineStateMachine/012/internal/nuon"9+ "github.com/FelineStateMachine/012/internal/sheet"10+)11+12+func addr(s string) sheet.Addr {13+ a, _ := sheet.ParseAddr(s)14+ return a15+}16+17+// file writes a workbook of sheets, each a map of cells to inputs, after18+// edit has its way with it.19+func file(t *testing.T, sheets []string, cells []map[string]string, edit func(w *sheet.Workbook)) []byte {20+ t.Helper()21+ w := sheet.NewBook()22+ for i, name := range sheets {23+ s := w.Sheet(0)24+ if i == 0 {25+ if err := w.RenameSheet(s, name); err != nil {26+ t.Fatal(err)27+ }28+ } else {29+ var err error30+ if s, err = w.AddSheet(name, i); err != nil {31+ t.Fatal(err)32+ }33+ }34+ for a, in := range cells[i] {35+ if err := s.Set(addr(a), in); err != nil {36+ t.Fatal(err)37+ }38+ }39+ }40+ if edit != nil {41+ edit(w)42+ }43+ var b bytes.Buffer44+ if err := w.Write(&b); err != nil {45+ t.Fatal(err)46+ }47+ return b.Bytes()48+}49+50+func read(t *testing.T, data []byte) *Book {51+ t.Helper()52+ b, err := Read(data)53+ if err != nil {54+ t.Fatal(err)55+ }56+ return b57+}58+59+// lines is the text output of comparing a with b.60+func lines(t *testing.T, a, b []byte) string {61+ t.Helper()62+ var out bytes.Buffer63+ if err := WriteChanges(&out, Compare(read(t, a), read(t, b)), "text", false); err != nil {64+ t.Fatal(err)65+ }66+ return out.String()67+}68+69+func TestCompareCells(t *testing.T) {70+ a := file(t, []string{"Sheet1"}, []map[string]string{{"A1": "Item", "B1": "5", "B2": "=B1*2", "C1": "gone", "D1": "=NOW()", "D2": "=D1+1"}}, nil)71+ b := file(t, []string{"Sheet1"}, []map[string]string{{"A1": "Item", "B1": "6", "B2": "=B1*2", "C2": "new", "D1": "=NOW()", "D2": "=D1+1"}}, func(w *sheet.Workbook) {72+ s := w.Sheet(0)73+ s.SetStyle(sheet.NewRect(addr("A1"), addr("A1")), func(st *sheet.Style) { st.Bold = true })74+ s.SetNote(addr("A1"), "the thing")75+ s.SetColWidth(2, 14)76+ })77+ want := `Sheet1!A1 format + bold78+Sheet1!A1 note + the thing79+Sheet1!B1 input 5 → 680+Sheet1!C1 input - gone81+Sheet1!B2 value 10 → 1282+Sheet1!C2 input + new83+Sheet1 widths C + 1484+`85+ if got := lines(t, a, b); got != want {86+ t.Errorf("got\n%s\nwant\n%s", got, want)87+ }88+ if got := lines(t, a, a); got != "" {89+ t.Errorf("a workbook against itself:\n%s", got)90+ }91+}92+93+func TestCompareSheets(t *testing.T) {94+ cells := map[string]string{"A1": "a", "A2": "b", "A3": "c", "A4": "d"}95+ edited := map[string]string{"A1": "a", "A2": "b", "A3": "c", "A4": "D"}96+ a := file(t, []string{"One", "Two", "Three", "Gone"}, []map[string]string{{"A1": "1"}, cells, {"A1": "3"}, {"A1": "x"}}, nil)97+ b := file(t, []string{"Three", "One", "Renamed", "Added"}, []map[string]string{{"A1": "3"}, {"A1": "1"}, edited, {"A1": "y"}}, nil)98+ got := lines(t, a, b)99+ for _, want := range []string{100+ "sheet Gone removed\n",101+ "sheet Added added\n",102+ "sheet Renamed renamed from Two\n",103+ "sheet Three moved from 3 to 1\n",104+ "Renamed!A4 input d → D\n",105+ "Gone!A1 input - x\n",106+ "Added!A1 input + y\n",107+ } {108+ if !strings.Contains(got, want) {109+ t.Errorf("lacks %q:\n%s", want, got)110+ }111+ }112+ if strings.Contains(got, "sheet One moved") {113+ t.Errorf("more moves than needed:\n%s", got)114+ }115+}116+117+func TestCompareShapes(t *testing.T) {118+ // One sheet written as a version 2 file and as a version 4 file is119+ // the same workbook.120+ v2 := []byte(`{"version": 2, "names": {"Sales": "A1:A2"}, "cells": {"A1": "1", "A2": "=A1+1"}}`)121+ v4 := []byte(`{"version": 4, "names": {"Sales": "Sheet1!A1:A2"}, "sheets": [{"name": "Sheet1", "cells": {"A1": "1", "A2": "=A1 + 1"}}]}`)122+ got := lines(t, v2, v4)123+ if got != "Sheet1!A2 input =A1+1 → =A1 + 1\n" {124+ t.Errorf("got\n%s", got)125+ }126+ empty := lines(t, nil, v2)127+ if !strings.HasPrefix(empty, "sheet Sheet1 added\n") || !strings.Contains(empty, "name Sales added + Sheet1!A1:A2") {128+ t.Errorf("from nothing:\n%s", empty)129+ }130+}131+132+func TestCompareRegionsAndBook(t *testing.T) {133+ a := []byte(`{"version": 4, "locale": "en-US", "macros": [{"name": "m1", "api": 1, "source": "x"}], "sheets": [{"name": "S", "regions": [{"name": "app", "at": "A1", "path": "app.csv"}, {"name": "old", "at": "D1", "path": "old.csv"}], "cells": {}}]}`)134+ b := []byte(`{"version": 4, "locale": "de-DE", "active": 0, "sheets": [{"name": "S", "regions": [{"name": "app", "at": "A1", "path": "app.log"}, {"name": "sales", "at": "H1", "output": true}], "cells": {}}]}`)135+ want := `S region app path app.csv → app.log136+S region old removed - old.csv137+S region sales added + a notebook cell's output138+macro m1 removed - x139+workbook locale en-US → de-DE140+`141+ if got := lines(t, a, b); got != want {142+ t.Errorf("got\n%s\nwant\n%s", got, want)143+ }144+}145+146+func TestCompareNotebooks(t *testing.T) {147+ nb := func(cells string) []byte {148+ return []byte(`{"version": 4, "sheets": [{"name": "Sheet1", "cells": {}}, {"name": "Notebook", "tab": "notebook", "notebookCells": [` + cells + `], "cells": {}}]}`)149+ }150+ a := nb(`{"kind": "note", "source": "# Sales"}, {"source": "sales = open s.csv", "output": "[[n]; [1]]"}, {"source": "$sales | length", "output": "1"}, {"source": "gone"}`)151+ b := nb(`{"source": "new = ls"}, {"kind": "note", "source": "# Sales"}, {"source": "sales = open s.csv", "output": "[[n]; [2]]"}, {"source": "$sales | length", "error": "boom"}`)152+ want := `Notebook cell 1 added + new = ls153+Notebook cell 3 output [[n]; [1]] → [[n]; [2]]154+Notebook cell 4 error + boom155+Notebook cell 4 output - 1156+Notebook cell 4 removed - gone157+`158+ if got := lines(t, a, b); got != want {159+ t.Errorf("got\n%s\nwant\n%s", got, want)160+ }161+ var out bytes.Buffer162+ Dump(&out, read(t, b))163+ if !strings.Contains(out.String(), "Notebook cell 3 sales = open s.csv = [[n]; [2]]\nNotebook cell 4 $sales | length error: boom\n") {164+ t.Errorf("dump:\n%s", out.String())165+ }166+}167+168+func TestWriteChanges(t *testing.T) {169+ a := file(t, []string{"Sheet1"}, []map[string]string{{"A1": "=1+1", "B1": "x"}}, nil)170+ b := file(t, []string{"Sheet1"}, []map[string]string{{"A1": "=1+2"}}, nil)171+ changes := Compare(read(t, a), read(t, b))172+ var out bytes.Buffer173+ WriteChanges(&out, changes, "nuon", false)174+ v, err := nuon.Parse(out.Bytes())175+ if err != nil || v.Kind != nuon.List || len(v.List) != 3 {176+ t.Fatalf("NUON %v: %s", err, out.String())177+ }178+ if got := v.List[1].String(); got != `{kind: cell, sheet: Sheet1, item: A1, field: value, old: 2, new: 3}` {179+ t.Errorf("a value's record: %s", got)180+ }181+ out.Reset()182+ WriteChanges(&out, changes, "json", false)183+ if !strings.Contains(out.String(), `{"kind":"cell","sheet":"Sheet1","item":"B1","field":"input","old":"x","new":null}`) {184+ t.Errorf("JSON:\n%s", out.String())185+ }186+ out.Reset()187+ WriteChanges(&out, changes[:1], "text", true)188+ if out.String() != "\x1b[1mSheet1!A1\x1b[0m \x1b[36minput\x1b[0m \x1b[31m=1+1\x1b[0m → \x1b[32m=1+2\x1b[0m\n" {189+ t.Errorf("colored: %q", out.String())190+ }191+ if err := WriteChanges(&out, changes, "xml", false); err == nil {192+ t.Error("xml")193+ }194+}195+196+func TestDump(t *testing.T) {197+ data := file(t, []string{"Sheet1"}, []map[string]string{{"A1": "Rent", "B1": "1450", "B2": "=B1*12", "C1": "=RAND()"}}, func(w *sheet.Workbook) {198+ s := w.Sheet(0)199+ s.SetNote(addr("A1"), "monthly")200+ s.SetColWidth(0, 12)201+ })202+ var out bytes.Buffer203+ if err := Dump(&out, read(t, data)); err != nil {204+ t.Fatal(err)205+ }206+ want := `sheet Sheet1207+Sheet1 widths {"A":12}208+Sheet1!A1 Rent note: monthly209+Sheet1!B1 1450210+Sheet1!C1 =RAND()211+Sheet1!B2 =B1*12 = 17400212+`213+ if out.String() != want {214+ t.Errorf("got\n%s\nwant\n%s", out.String(), want)215+ }216+}
A internal/diff/fields.go +187 -0
@@ -0,0 +1,187 @@
1+package diff2+3+import (4+ "encoding/json"5+ "maps"6+ "slices"7+8+ "github.com/FelineStateMachine/012/internal/nuon"9+)10+11+// quietKeys are the workbook's fields that change without the workbook12+// changing: the file version, the sheet shown, the computer its macros13+// were trusted on and the notebook prompt's history.14+var quietKeys = map[string]bool{"version": true, "active": true, "macroOrigin": true, "shellHistory": true}15+16+// bookChanges compares the workbook's fields: named ranges, macros and17+// settings.18+func bookChanges(a, b *rawBook) []Change {19+ var out []Change20+ out = append(out, keyedChanges(KindName, "range", a.top["names"], b.top["names"])...)21+ out = append(out, listChanges(KindMacro, "", a.top["macros"], b.top["macros"])...)22+ keys := map[string]bool{}23+ for _, t := range []map[string]json.RawMessage{a.top, b.top} {24+ for k := range t {25+ keys[k] = true26+ }27+ }28+ for _, k := range slices.Sorted(maps.Keys(keys)) {29+ if !quietKeys[k] && k != "names" && k != "macros" {30+ out = append(out, fieldChanges(KindWorkbook, "", k, a.top[k], b.top[k])...)31+ }32+ }33+ return out34+}35+36+// fieldChanges compares a field: key by key when it's an object (a37+// column's width), else whole.38+func fieldChanges(kind, sheetName, item string, a, b json.RawMessage) []Change {39+ if equal(a, b) {40+ return nil41+ }42+ oa, okA := object(a)43+ ob, okB := object(b)44+ if okA && okB {45+ var out []Change46+ for _, k := range unionKeys(oa, ob) {47+ if !equal(oa[k], ob[k]) {48+ out = append(out, rawChange(kind, sheetName, item, k, oa[k], ob[k]))49+ }50+ }51+ return out52+ }53+ return []Change{rawChange(kind, sheetName, item, "", a, b)}54+}55+56+// keyedChanges compares an object whose keys are things (named ranges):57+// each added, removed, or its value changed, as field.58+func keyedChanges(kind, field string, a, b json.RawMessage) []Change {59+ oa, _ := object(a)60+ ob, _ := object(b)61+ var out []Change62+ for _, k := range unionKeys(oa, ob) {63+ switch va, vb := oa[k], ob[k]; {64+ case va == nil:65+ out = append(out, rawChange(kind, "", k, "added", nil, vb))66+ case vb == nil:67+ out = append(out, rawChange(kind, "", k, "removed", va, nil))68+ case !equal(va, vb):69+ out = append(out, rawChange(kind, "", k, field, va, vb))70+ }71+ }72+ return out73+}74+75+// listChanges compares lists of things with names (regions, macros):76+// each added, removed, or changed field by field.77+func listChanges(kind, sheetName string, a, b json.RawMessage) []Change {78+ la, lb := named(a), named(b)79+ var out []Change80+ for _, k := range unionKeys(la, lb) {81+ switch ea, eb := la[k], lb[k]; {82+ case ea == nil:83+ c := rawChange(kind, sheetName, k, "added", nil, eb)84+ c.newText = summary(eb)85+ out = append(out, c)86+ case eb == nil:87+ c := rawChange(kind, sheetName, k, "removed", ea, nil)88+ c.oldText = summary(ea)89+ out = append(out, c)90+ default:91+ fa, _ := object(ea)92+ fb, _ := object(eb)93+ for _, f := range unionKeys(fa, fb) {94+ if !equal(fa[f], fb[f]) {95+ out = append(out, rawChange(kind, sheetName, k, f, fa[f], fb[f]))96+ }97+ }98+ }99+ }100+ return out101+}102+103+// named reads a list of objects by their "name" fields, as compact104+// JSON.105+func named(raw json.RawMessage) map[string]json.RawMessage {106+ var list []json.RawMessage107+ json.Unmarshal(raw, &list)108+ out := map[string]json.RawMessage{}109+ for _, e := range list {110+ var n struct{ Name string }111+ json.Unmarshal(e, &n)112+ out[n.Name] = compact(e)113+ }114+ return out115+}116+117+// summary is what the text output shows of a region or macro added or118+// removed: the file a region follows or that it's a notebook's output;119+// a macro's script's first line.120+func summary(raw json.RawMessage) string {121+ fields, _ := object(raw)122+ var s string123+ switch {124+ case json.Unmarshal(fields["path"], &s) == nil && s != "":125+ return s126+ case string(fields["output"]) == "true":127+ return "a notebook cell's output"128+ case json.Unmarshal(fields["source"], &s) == nil && s != "":129+ return firstLine(s)130+ }131+ return string(raw)132+}133+134+// rawChange is a change between two JSON values, nil for none.135+func rawChange(kind, sheetName, item, field string, a, b json.RawMessage) Change {136+ return Change{Kind: kind, Sheet: sheetName, Item: item, Field: field,137+ Old: jsonValue(a), New: jsonValue(b), oldText: rawText(a), newText: rawText(b)}138+}139+140+func jsonValue(raw json.RawMessage) nuon.Value {141+ if raw == nil {142+ return nuon.NullValue()143+ }144+ v, err := nuon.Parse(raw)145+ if err != nil {146+ return nuon.StringValue(string(raw))147+ }148+ return v149+}150+151+// rawText is a JSON value as the text output shows it: a string as it152+// is, anything else as compact JSON.153+func rawText(raw json.RawMessage) string {154+ var s string155+ if raw == nil {156+ return ""157+ }158+ if json.Unmarshal(raw, &s) == nil {159+ return s160+ }161+ return string(raw)162+}163+164+func object(raw json.RawMessage) (map[string]json.RawMessage, bool) {165+ var m map[string]json.RawMessage166+ if raw == nil {167+ return map[string]json.RawMessage{}, true168+ }169+ if json.Unmarshal(raw, &m) != nil || m == nil {170+ return nil, false171+ }172+ for k, v := range m {173+ m[k] = compact(v)174+ }175+ return m, true176+}177+178+func unionKeys(a, b map[string]json.RawMessage) []string {179+ keys := map[string]bool{}180+ for k := range a {181+ keys[k] = true182+ }183+ for k := range b {184+ keys[k] = true185+ }186+ return slices.Sorted(maps.Keys(keys))187+}
A internal/diff/match.go +103 -0
@@ -0,0 +1,103 @@
1+package diff2+3+import "sort"4+5+// minSimilar is how alike two sheets' cells must be, as the share of6+// cells they hold the same, for a sheet gone under one name and a sheet7+// come under another to count as one sheet renamed.8+const minSimilar = 0.59+10+// pairing says which sheets of two workbooks are the same sheet: by11+// index in a and in b.12+type pairing struct {13+ pairs [][2]int // matched, in a's order14+ onlyA []int // a's sheets without a match: removed15+ onlyB []int // b's sheets without a match: added16+}17+18+// matchSheets pairs a's sheets with b's: first by name, ignoring case,19+// then each sheet left in a with the most alike left in b, when they20+// share at least half their cells (a rename, perhaps with edits).21+func matchSheets(a, b *rawBook) pairing {22+ var p pairing23+ usedB := make([]bool, len(b.sheets))24+ var leftA []int25+ for i, s := range a.sheets {26+ j := b.sheetIndex(s.name)27+ if j < 0 || usedB[j] {28+ leftA = append(leftA, i)29+ continue30+ }31+ usedB[j] = true32+ p.pairs = append(p.pairs, [2]int{i, j})33+ }34+ type candidate struct {35+ i, j int36+ score float6437+ }38+ var cands []candidate39+ for _, i := range leftA {40+ for j := range b.sheets {41+ if !usedB[j] {42+ if sc := similarity(a.sheets[i], b.sheets[j]); sc >= minSimilar {43+ cands = append(cands, candidate{i, j, sc})44+ }45+ }46+ }47+ }48+ sort.SliceStable(cands, func(x, y int) bool { return cands[x].score > cands[y].score })49+ usedA := map[int]bool{}50+ for _, c := range cands {51+ if !usedA[c.i] && !usedB[c.j] {52+ usedA[c.i], usedB[c.j] = true, true53+ p.pairs = append(p.pairs, [2]int{c.i, c.j})54+ }55+ }56+ sort.Slice(p.pairs, func(x, y int) bool { return p.pairs[x][0] < p.pairs[y][0] })57+ for _, i := range leftA {58+ if !usedA[i] {59+ p.onlyA = append(p.onlyA, i)60+ }61+ }62+ for j := range b.sheets {63+ if !usedB[j] {64+ p.onlyB = append(p.onlyB, j)65+ }66+ }67+ return p68+}69+70+// similarity is the share of cells two sheets hold the same, 1 for two71+// empty sheets.72+func similarity(a, b *rawSheet) float64 {73+ n := max(len(a.cells), len(b.cells))74+ if n == 0 {75+ return 176+ }77+ same := 078+ for k, v := range a.cells {79+ if equal(v, b.cells[k]) {80+ same++81+ }82+ }83+ return float64(same) / float64(n)84+}85+86+// sameSheet reports whether two sheets are alike in every field, their87+// names included.88+func sameSheet(a, b *rawSheet) bool {89+ if a.name != b.name || len(a.cells) != len(b.cells) || len(a.fields) != len(b.fields) {90+ return false91+ }92+ for k, v := range a.cells {93+ if !equal(v, b.cells[k]) {94+ return false95+ }96+ }97+ for k, v := range a.fields {98+ if !equal(v, b.fields[k]) {99+ return false100+ }101+ }102+ return true103+}
A internal/diff/merge.go +287 -0
@@ -0,0 +1,287 @@
1+package diff2+3+import (4+ "encoding/json"5+ "maps"6+ "slices"7+ "strconv"8+9+ "github.com/FelineStateMachine/012/internal/sheet"10+)11+12+// Conflict is a part of a workbook both sides changed differently. The13+// merge keeps ours; the report says what theirs had.14+type Conflict struct {15+ Where string // as a Change's: Q3!B7, sheet Q3, Q3 widths, name Sales16+ Field string // what about it: input, format, note, a field's key, or ""17+ // Base, Ours and Theirs are the three versions as text, "" where18+ // the thing isn't there; What says what happened instead, when the19+ // conflict is about a whole sheet.20+ Base, Ours, Theirs string21+ What string22+}23+24+// String is the conflict as the merge report lists it.25+func (c Conflict) String() string {26+ s := c.Where27+ if c.Field != "" {28+ s += " " + c.Field29+ }30+ if c.What != "" {31+ return s + ": " + c.What32+ }33+ return s + ": ours " + side3(c.Ours) + ", theirs " + side3(c.Theirs) + ", base " + side3(c.Base)34+}35+36+func side3(s string) string {37+ if s == "" {38+ return "(none)"39+ }40+ return quoted(s)41+}42+43+// Merge combines ours and theirs, two workbook files changed from base,44+// and returns the merged file as 012 saves it. Each cell's input, format45+// and note, each field of a sheet or the workbook (a column's width, a46+// region, a named range, a macro) and each sheet's name and presence47+// merge on their own: a part changed on one side only takes that side's48+// version, and a part both changed alike takes it too. A part both49+// changed differently is a conflict: the merge keeps ours, and a cell's50+// note says what theirs had (on top of its own note), so the conflicts51+// can be found in the grid. Empty contents stand for a missing file.52+//53+// Sheets are matched by name or, when renamed, by their cells (see54+// matchSheets). Rows or columns inserted on one side shift the cells55+// below or right of them, which a cell-by-cell merge can't tell from56+// edits: merge such changes in 012 rather than here.57+func Merge(base, ours, theirs []byte) ([]byte, []Conflict, error) {58+ var books [3]*rawBook59+ for i, data := range [][]byte{base, ours, theirs} {60+ b, err := parseRaw(data)61+ if err != nil {62+ return nil, nil, err63+ }64+ books[i] = b65+ }66+ m := &merger{}67+ merged := m.books(books[0], books[1], books[2])68+ if len(merged.sheets) == 0 {69+ return nil, m.conflicts, nil70+ }71+ out, err := normalize(merged)72+ return out, m.conflicts, err73+}74+75+// merger collects the conflicts of a merge.76+type merger struct {77+ conflicts []Conflict78+}79+80+func (m *merger) conflict(where, field string, b, o, t json.RawMessage) {81+ m.conflicts = append(m.conflicts, Conflict{Where: where, Field: field, Base: rawText(b), Ours: rawText(o), Theirs: rawText(t)})82+}83+84+// said records a conflict that isn't between three values, in words.85+func (m *merger) said(where, field, what string) {86+ m.conflicts = append(m.conflicts, Conflict{Where: where, Field: field, What: what})87+}88+89+// merge3 merges one value: what changed on one side, or both alike.90+func merge3(b, o, t json.RawMessage) (json.RawMessage, bool) {91+ switch {92+ case equal(o, t), equal(b, t):93+ return o, false94+ case equal(b, o):95+ return t, false96+ }97+ return o, true98+}99+100+// value merges one value, reporting a conflict at where.101+func (m *merger) value(where, field string, b, o, t json.RawMessage) json.RawMessage {102+ v, clash := merge3(b, o, t)103+ if clash {104+ m.conflict(where, field, b, o, t)105+ }106+ return v107+}108+109+// books merges the workbooks' fields, then their sheets.110+func (m *merger) books(b, o, t *rawBook) *rawBook {111+ out := &rawBook{top: map[string]json.RawMessage{}}112+ for _, k := range unionKeys3(b.top, o.top, t.top) {113+ var v json.RawMessage114+ switch k {115+ case "version":116+ v = maxVersion(b.top[k], o.top[k], t.top[k])117+ case "names":118+ v = m.object(b.top[k], o.top[k], t.top[k], func(key string) (string, string) { return "name " + key, "" })119+ case "macros":120+ v = m.list(b.top[k], o.top[k], t.top[k], func(key string) string { return "macro " + key })121+ case "active", "shellHistory", "macroOrigin":122+ v, _ = merge3(b.top[k], o.top[k], t.top[k]) // what's shown or was typed: ours if both differ123+ default:124+ v = m.value("workbook "+k, "", b.top[k], o.top[k], t.top[k])125+ }126+ if v != nil {127+ out.top[k] = v128+ }129+ }130+ out.sheets = m.sheets(b, o, t)131+ if n, err := strconv.Atoi(string(out.top["active"])); err == nil && n >= len(out.sheets) {132+ delete(out.top, "active") // the sheet shown went: show the first133+ }134+ trust(out, o, t)135+ return out136+}137+138+// trust keeps the computer ours trusted its commands on only when the139+// merge's commands (macros, notebook cells and linked files) are ours',140+// or theirs were trusted on the same computer, so a merge never makes141+// someone else's commands run without asking.142+func trust(out, o, t *rawBook) {143+ if equal(o.top["macroOrigin"], t.top["macroOrigin"]) || code(out) == code(o) {144+ if origin := o.top["macroOrigin"]; origin != nil {145+ out.top["macroOrigin"] = origin146+ }147+ return148+ }149+ delete(out.top, "macroOrigin")150+}151+152+// code is what a workbook can make 012 run or read: its macros, its153+// notebooks' cells and the files it links, in one string to compare.154+func code(b *rawBook) string {155+ var parts []string156+ parts = append(parts, string(b.top["macros"]))157+ for _, s := range b.sheets {158+ parts = append(parts, string(sources(s.fields["notebookCells"])))159+ for _, r := range named(s.fields["regions"]) {160+ f, _ := object(r)161+ parts = append(parts, string(f["command"])+string(f["path"])+string(f["table"])+string(f["query"]))162+ }163+ }164+ slices.Sort(parts)165+ raw, _ := json.Marshal(parts)166+ return string(raw)167+}168+169+func maxVersion(vs ...json.RawMessage) json.RawMessage {170+ best, out := 0, json.RawMessage(nil)171+ for _, v := range vs {172+ if n, err := strconv.Atoi(string(v)); err == nil && n > best {173+ best, out = n, v174+ }175+ }176+ return out177+}178+179+// object merges an object key by key (named ranges, column widths);180+// where names each key's place and field in conflicts.181+func (m *merger) object(b, o, t json.RawMessage, where func(key string) (string, string)) json.RawMessage {182+ ob, okB := object(b)183+ oo, okO := object(o)184+ ot, okT := object(t)185+ if !okB || !okO || !okT {186+ at, _ := where("")187+ return m.value(at, "", b, o, t)188+ }189+ out := map[string]json.RawMessage{}190+ for _, k := range unionKeys3(ob, oo, ot) {191+ at, field := where(k)192+ if v := m.value(at, field, ob[k], oo[k], ot[k]); v != nil {193+ out[k] = v194+ }195+ }196+ if len(out) == 0 && o == nil && t == nil {197+ return nil198+ }199+ raw, _ := json.Marshal(out)200+ return raw201+}202+203+// list merges a list of things with names (regions, macros) thing by204+// thing, in ours' order with theirs' additions after.205+func (m *merger) list(b, o, t json.RawMessage, where func(string) string) json.RawMessage {206+ lb, lo, lt := named(b), named(o), named(t)207+ var order []string208+ for _, raw := range []json.RawMessage{o, t} {209+ var items []struct{ Name string }210+ json.Unmarshal(raw, &items)211+ for _, it := range items {212+ if !slices.Contains(order, it.Name) {213+ order = append(order, it.Name)214+ }215+ }216+ }217+ var out []json.RawMessage218+ for _, k := range order {219+ if v := m.value(where(k), "", lb[k], lo[k], lt[k]); v != nil {220+ out = append(out, v)221+ }222+ }223+ if len(out) == 0 {224+ return nil225+ }226+ raw, _ := json.Marshal(out)227+ return raw228+}229+230+// cell merges a cell's input, format and note apart. Where they231+// conflict, ours stays and the note says what theirs had.232+func (m *merger) cell(where string, b, o, t json.RawMessage) json.RawMessage {233+ pb, po, pt := splitCell(b), splitCell(o), splitCell(t)234+ str := func(s string) json.RawMessage {235+ if s == "" {236+ return nil237+ }238+ raw, _ := json.Marshal(s)239+ return raw240+ }241+ out := cellParts{}242+ var said []string243+ part := func(field string, b, o, t json.RawMessage) json.RawMessage {244+ v, clash := merge3(b, o, t)245+ if clash {246+ text := rawText247+ if field == "format" {248+ text = formatText249+ }250+ m.conflicts = append(m.conflicts, Conflict{Where: where, Field: field, Base: text(b), Ours: text(o), Theirs: text(t)})251+ said = append(said, field+" "+side3(text(t)))252+ }253+ return v254+ }255+ json.Unmarshal(part("input", str(pb.input), str(po.input), str(pt.input)), &out.input)256+ out.format = part("format", pb.format, po.format, pt.format)257+ json.Unmarshal(part("note", str(pb.note), str(po.note), str(pt.note)), &out.note)258+ if len(said) > 0 {259+ out.note = conflictNote(out.note, said)260+ }261+ return joinCell(out)262+}263+264+// conflictNote is a cell's note with what theirs had added below it.265+func conflictNote(note string, said []string) string {266+ text := "Merge conflict, kept ours; theirs had "267+ for i, s := range said {268+ if i > 0 {269+ text += "; "270+ }271+ text += s272+ }273+ if note != "" {274+ text = note + "\n\n" + text275+ }276+ return sheet.CleanNote(text)277+}278+279+func unionKeys3(a, b, c map[string]json.RawMessage) []string {280+ keys := map[string]bool{}281+ for _, m := range []map[string]json.RawMessage{a, b, c} {282+ for k := range m {283+ keys[k] = true284+ }285+ }286+ return slices.Sorted(maps.Keys(keys))287+}
A internal/diff/merge_test.go +180 -0

Not shown: this page already shows 6000 lines of diff.

A internal/diff/mergesheets.go +165 -0

Not shown: this page already shows 6000 lines of diff.

A internal/diff/notebook.go +160 -0

Not shown: this page already shows 6000 lines of diff.

A internal/diff/raw.go +211 -0

Not shown: this page already shows 6000 lines of diff.

A internal/diff/text.go +199 -0

Not shown: this page already shows 6000 lines of diff.

M internal/fileio/nuon_test.go +50 -8

Not shown: this page already shows 6000 lines of diff.

A internal/fileio/nuonrows.go +35 -0

Not shown: this page already shows 6000 lines of diff.

M internal/fileio/nuonwrite.go +4 -0

Not shown: this page already shows 6000 lines of diff.

A internal/fileio/sheetscorpus_test.go +241 -0

Not shown: this page already shows 6000 lines of diff.

A internal/fileio/sheetscorpus_xlsx_test.go +96 -0

Not shown: this page already shows 6000 lines of diff.

A internal/fileio/testdata/sheets_corpus.tsv +320 -0

Not shown: this page already shows 6000 lines of diff.

A internal/headless/get.go +179 -0

Not shown: this page already shows 6000 lines of diff.

A internal/headless/headless.go +125 -0

Not shown: this page already shows 6000 lines of diff.

A internal/headless/headless_test.go +316 -0

Not shown: this page already shows 6000 lines of diff.

A internal/headless/jev.go +54 -0

Not shown: this page already shows 6000 lines of diff.

A internal/headless/notebook.go +237 -0

Not shown: this page already shows 6000 lines of diff.

A internal/headless/recalc.go +51 -0

Not shown: this page already shows 6000 lines of diff.

A internal/headless/ref.go +95 -0

Not shown: this page already shows 6000 lines of diff.

A internal/headless/set.go +71 -0

Not shown: this page already shows 6000 lines of diff.

A internal/notebook/graph.go +224 -0

Not shown: this page already shows 6000 lines of diff.

A internal/notebook/notebook.go +158 -0

Not shown: this page already shows 6000 lines of diff.

A internal/notebook/notebook_test.go +135 -0

Not shown: this page already shows 6000 lines of diff.

A internal/notebook/refs.go +148 -0

Not shown: this page already shows 6000 lines of diff.

A internal/nushell/ide.go +219 -0

Not shown: this page already shows 6000 lines of diff.

A internal/nushell/ide_test.go +127 -0

Not shown: this page already shows 6000 lines of diff.

M internal/nushell/nushell.go +80 -87

Not shown: this page already shows 6000 lines of diff.

M internal/nushell/nushell_test.go +21 -64

Not shown: this page already shows 6000 lines of diff.

A internal/nushell/testdata/ide/ast.json +1 -0

Not shown: this page already shows 6000 lines of diff.

A internal/nushell/testdata/ide/ast.nu +1 -0

Not shown: this page already shows 6000 lines of diff.

A internal/nushell/testdata/ide/check.jsonl +6 -0

Not shown: this page already shows 6000 lines of diff.

A internal/nushell/testdata/ide/check.nu +1 -0

Not shown: this page already shows 6000 lines of diff.

A internal/nushell/testdata/ide/complete.json +1 -0

Not shown: this page already shows 6000 lines of diff.

A internal/nushell/testdata/ide/complete.nu +1 -0

Not shown: this page already shows 6000 lines of diff.

A internal/nushell/testdata/ide/version.txt +1 -0

Not shown: this page already shows 6000 lines of diff.

M internal/sheet/file.go +24 -20

Not shown: this page already shows 6000 lines of diff.

M internal/sheet/fileread.go +4 -2

Not shown: this page already shows 6000 lines of diff.

M internal/sheet/filestream_test.go +12 -2

Not shown: this page already shows 6000 lines of diff.

M internal/sheet/linked.go +6 -32

Not shown: this page already shows 6000 lines of diff.

M internal/sheet/linkedregions_test.go +18 -37

Not shown: this page already shows 6000 lines of diff.

M internal/sheet/live.go +4 -6

Not shown: this page already shows 6000 lines of diff.

A internal/sheet/notebook.go +229 -0

Not shown: this page already shows 6000 lines of diff.

A internal/sheet/notebookfile.go +201 -0

Not shown: this page already shows 6000 lines of diff.

M internal/sheet/randedit_ops_test.go +36 -12

Not shown: this page already shows 6000 lines of diff.

M internal/sheet/randedit_test.go +100 -30

Not shown: this page already shows 6000 lines of diff.

M internal/sheet/region.go +83 -260

Not shown: this page already shows 6000 lines of diff.

M internal/sheet/region_test.go +189 -181

Not shown: this page already shows 6000 lines of diff.

D internal/sheet/regiondata.go +0 -66

Not shown: this page already shows 6000 lines of diff.

M internal/sheet/regionfile.go +53 -72

Not shown: this page already shows 6000 lines of diff.

D internal/sheet/regiongraph.go +0 -161

Not shown: this page already shows 6000 lines of diff.

M internal/sheet/regionshape.go +33 -122

Not shown: this page already shows 6000 lines of diff.

D internal/sheet/regionsort.go +0 -68

Not shown: this page already shows 6000 lines of diff.

M internal/sheet/regionwrite.go +24 -155

Not shown: this page already shows 6000 lines of diff.

M internal/sheet/sheet.go +6 -5

Not shown: this page already shows 6000 lines of diff.

D internal/sheet/shellhistory.go +0 -35

Not shown: this page already shows 6000 lines of diff.

M internal/sheet/workbook.go +2 -2

Not shown: this page already shows 6000 lines of diff.

M internal/ui/commands.go +9 -2

Not shown: this page already shows 6000 lines of diff.

M internal/ui/docs_test.go +19 -0

Not shown: this page already shows 6000 lines of diff.

M internal/ui/file.go +10 -4

Not shown: this page already shows 6000 lines of diff.

M internal/ui/filter.go +4 -5

Not shown: this page already shows 6000 lines of diff.

M internal/ui/goto_test.go +19 -18

Not shown: this page already shows 6000 lines of diff.

M internal/ui/gridlines.go +0 -19

Not shown: this page already shows 6000 lines of diff.

M internal/ui/help.go +1 -0

Not shown: this page already shows 6000 lines of diff.

M internal/ui/hosts.go +0 -16

Not shown: this page already shows 6000 lines of diff.

A internal/ui/lineedit/area.go +246 -0

Not shown: this page already shows 6000 lines of diff.

A internal/ui/lineedit/area_test.go +93 -0

Not shown: this page already shows 6000 lines of diff.

M internal/ui/lineedit/lineedit.go +3 -0

Not shown: this page already shows 6000 lines of diff.

A internal/ui/lineedit/word.go +98 -0

Not shown: this page already shows 6000 lines of diff.

A internal/ui/lineedit/word_test.go +75 -0

Not shown: this page already shows 6000 lines of diff.

M internal/ui/linked.go +1 -1

Not shown: this page already shows 6000 lines of diff.

M internal/ui/menubar.go +1 -3

Not shown: this page already shows 6000 lines of diff.

M internal/ui/model.go +29 -13

Not shown: this page already shows 6000 lines of diff.

M internal/ui/monochrome_test.go +68 -15

Not shown: this page already shows 6000 lines of diff.

A internal/ui/nbjob.go +166 -0

Not shown: this page already shows 6000 lines of diff.

A internal/ui/nblang.go +83 -0

Not shown: this page already shows 6000 lines of diff.

A internal/ui/nblang_test.go +94 -0

Not shown: this page already shows 6000 lines of diff.

A internal/ui/nbrun.go +417 -0

Not shown: this page already shows 6000 lines of diff.

A internal/ui/nbscreen.go +292 -0

Not shown: this page already shows 6000 lines of diff.

A internal/ui/nbsend.go +201 -0

Not shown: this page already shows 6000 lines of diff.

A internal/ui/nbview/bench_test.go +68 -0

Not shown: this page already shows 6000 lines of diff.

A internal/ui/nbview/edit.go +320 -0

Not shown: this page already shows 6000 lines of diff.

A internal/ui/nbview/full.go +298 -0

Not shown: this page already shows 6000 lines of diff.

A internal/ui/nbview/highlight.go +173 -0

Not shown: this page already shows 6000 lines of diff.

A internal/ui/nbview/keys.go +375 -0

Not shown: this page already shows 6000 lines of diff.

A internal/ui/nbview/markdown.go +167 -0

Not shown: this page already shows 6000 lines of diff.

A internal/ui/nbview/nu.go +219 -0

Not shown: this page already shows 6000 lines of diff.

A internal/ui/nbview/nu_test.go +367 -0

Not shown: this page already shows 6000 lines of diff.

A internal/ui/nbview/nuprelude.go +146 -0

Not shown: this page already shows 6000 lines of diff.

A internal/ui/nbview/output.go +409 -0

Not shown: this page already shows 6000 lines of diff.

A internal/ui/nbview/panel.go +113 -0

Not shown: this page already shows 6000 lines of diff.

A internal/ui/nbview/syntax.go +241 -0

Not shown: this page already shows 6000 lines of diff.

A internal/ui/nbview/testdata/nu/check.jsonl +5 -0

Not shown: this page already shows 6000 lines of diff.

A internal/ui/nbview/testdata/nu/check.nu +4 -0

Not shown: this page already shows 6000 lines of diff.

A internal/ui/nbview/testdata/nu/complete.json +1 -0

Not shown: this page already shows 6000 lines of diff.

A internal/ui/nbview/testdata/nu/complete.nu +4 -0

Not shown: this page already shows 6000 lines of diff.

A internal/ui/nbview/testdata/nu/highlight.json +1 -0

Not shown: this page already shows 6000 lines of diff.

A internal/ui/nbview/testdata/nu/highlight.nu +4 -0

Not shown: this page already shows 6000 lines of diff.

A internal/ui/nbview/testdata/nu/sheet.json +1 -0

Not shown: this page already shows 6000 lines of diff.

A internal/ui/nbview/testdata/nu/sheet.nu +5 -0

Not shown: this page already shows 6000 lines of diff.

A internal/ui/nbview/view.go +405 -0

Not shown: this page already shows 6000 lines of diff.

A internal/ui/nbview/view_test.go +238 -0

Not shown: this page already shows 6000 lines of diff.

M internal/ui/notebook.go +318 -333

Not shown: this page already shows 6000 lines of diff.

M internal/ui/notebook_test.go +299 -180

Not shown: this page already shows 6000 lines of diff.

D internal/ui/nukey_test.go +0 -26

Not shown: this page already shows 6000 lines of diff.

D internal/ui/nuprompt/nuprompt.go +0 -331

Not shown: this page already shows 6000 lines of diff.

D internal/ui/nuprompt/nuprompt_test.go +0 -100

Not shown: this page already shows 6000 lines of diff.

D internal/ui/nurun.go +0 -325

Not shown: this page already shows 6000 lines of diff.

M internal/ui/overlay.go +4 -1

Not shown: this page already shows 6000 lines of diff.

M internal/ui/panel.go +38 -29

Not shown: this page already shows 6000 lines of diff.

M internal/ui/pivot.go +1 -1

Not shown: this page already shows 6000 lines of diff.

M internal/ui/selection.go +1 -1

Not shown: this page already shows 6000 lines of diff.

M internal/ui/sort.go +5 -1

Not shown: this page already shows 6000 lines of diff.

M internal/ui/tabs.go +4 -0

Not shown: this page already shows 6000 lines of diff.

M internal/ui/tabstrip/tabstrip.go +9 -2

Not shown: this page already shows 6000 lines of diff.

A internal/ui/theme/code.go +42 -0

Not shown: this page already shows 6000 lines of diff.

M internal/ui/theme/scheme.go +1 -1

Not shown: this page already shows 6000 lines of diff.

M internal/ui/theme/theme.go +15 -7

Not shown: this page already shows 6000 lines of diff.

M internal/ui/view.go +1 -4

Not shown: this page already shows 6000 lines of diff.

A internal/ui/wordkeys_test.go +20 -0

Not shown: this page already shows 6000 lines of diff.

M website/src/pages/index.tsx +1 -1

Not shown: this page already shows 6000 lines of diff.