Repository navigation
Merged
Conversation
On the phones, picking a theme from the command palette's Themes list raised the keyboard over the note being read (seen on Android 1.1.50's walk, and the iPhone shares the code). The palette's command path already treats a theme as a `stay` landing (lib/command-landing.ts): the focus goes back where it was before the palette opened, so a keyboard that was down stays down. The Themes list committed its pick on a separate path that called focusEditorNormalMode itself and skipped that rule. It now hands back through focusEditorAfterPaletteClose, like backing out of the palette or running a toggle. Desktop is unchanged: that hand-off focuses the editor there, as the theme pick always did.
…ked rows charles b. imports CSVs of up to 44 columns into databases and wants saved views that show only 4 or 5 of them, and only the rows he chose, by filter or by hand. The schema already had the per-view half of the columns story (DbView.hiddenFieldIds, which the Table honors), but nothing could set it, and there was no way to save a hand-picked set of rows at all. This is the data layer for both, with no UI yet. DbView gains an optional pickedRowIds. When present, the view shows only the rows it names, and its filters and sorts then apply to those; absent means every row. An empty list is kept as an empty list (the UI will say "0 picked rows") rather than read as "everything", so taking out the last pick never floods the view with every record. Ids of rows deleted since are ignored where the rows are computed instead of pruned when a row goes, so a row delete never costs a schema write; the next edit of the picks drops them. Older builds ignore the field and round-trip it untouched, since the sidecar parser keeps view objects whole. The rows a view shows now come from one shared function, viewRows (shared-domain): picks, then filters, then sorts. The Table and the Board both call it, so a view means the same records on either surface. The Board used to filter without sorting; it now runs the sorts too, which changes nothing today because no board view carries sorts (no UI sets them). The pure helpers the UI will dispatch live in database-cells: setColumnHidden, showAllColumns and hideAllColumns write one view's hiddenFieldIds and refuse to hide its last visible column (hide all keeps the title column, which carries the page icon); duplicateView makes a deep copy of every setting, picks included, right after the source, named "<name> copy"; setViewPicks, removeFromViewPicks and clearViewPicks edit the picks; addRowInView adds a row that joins the active view's picks so it cannot vanish the moment it is made, and says when the schema changed so the caller writes it. New views get unique names (Table 2, Board 2), now that a database will commonly carry several table views. The column-order normalization moveColumn and moveColumnToField each spelled out is one helper, normalizedColumnOrder, with the same behavior. zn and the MCP server do not apply views at all (zn base rows lists every row, and the MCP server has no database tools), so they are left as they are; picks change only what the app's views show.
…shows With a 44-column CSV imported, the grid drew all 44 and there was no way to see fewer: each view could already store hidden columns, but no control set them. A table view now has a Columns button in the database header, beside Filter. It reads "Columns" while every column shows and "Columns 5 of 44" once some are hidden, and opens a popover with a find field, Show all and Hide all, and a checklist of the view's columns in its column order. Every toggle writes the active view's hiddenFieldIds, so each saved view keeps its own columns: a "Pipeline" view can show 5 while the default Table keeps all 44. The last visible column cannot be hidden (its row says why on hover), and Hide all keeps the title column, which carries the page icon, as the start of picking the few you want. The row id stays out of the list, as it stays out of the grid. Switching views closes the popover, since the next view has columns of its own. A board view has no Columns button: its cards choose their fields separately. The column header menu gains Hide column (this view only) and Choose columns..., and in the grid c opens the popover with Vim mode on. c was free among the grid's keys; like O it is a new single-key shortcut, so it is off with Vim mode off, and it does nothing while a menu or dialog is open. In the popover, with Vim on, the list holds the keyboard: j / k move, Space or x toggles, / jumps to the find field, Home / End reach the ends. With Vim off the find field takes the typing and no letter is a shortcut: the arrows move between the field and the columns, Space or Enter toggles the focused column. Escape closes it in both, and focus goes back to the grid unless a click put it somewhere else. The panel is one of the SELF_KEYED_SURFACES, so the global Vim layer (and its Vim-off panel keys) leave its keys alone instead of walking the sidebar behind it.
The + beside a database's view tabs could only add a board view, so the saved views charles b. asked for (several tables over one 44-column CSV, each showing its own few columns) could not be made at all. The + now opens a menu: New table view, New board view, and Duplicate this view. New table view adds a table showing every column, makes it the active view and opens the Columns popover straight away, so a wide database becomes a 4- or 5-column view in one go. New views get names that tell them apart (Table 2, Board 2). Duplicate copies everything the view is: its filters and whether they match all or any, its sorts, column order, hidden columns and widths, a board's grouping, column order and card fields, and its picked rows. The copy is deep, lands right after the source as "<name> copy", and becomes the active view, ready to be changed without touching the original. The view tab's right-click menu gains Duplicate view, between Rename view and Delete view.
charles b. wanted to choose a view's rows "either by field filter or selecting the rows". Filters existed; picking rows by hand did not. Now, with rows selected in the Table (the checkboxes, or Space / x in the grid), the selection bar gains "Show only these in this view". It saves the selection as the view's picks (DbView.pickedRowIds), and the view shows only those rows from then on, its filters and sorts applied on top. A view with picks always says so: a strip under the header reads "Showing N picked rows", with Edit picks and Show all rows. Edit picks shows every row (picks and filters set aside, sorts kept) with the picked ones selected, and the selection bar offers Update picks or Cancel instead of its usual actions; Escape in the grid cancels too. Show all rows drops the picks. The row menu gains "Remove from this view's picks" (for the whole selection when the row is part of one), and "Show only selected rows" while rows are selected, so x then m reaches it from the keyboard. Taking out the last pick leaves the view at "Showing 0 picked rows" rather than quietly showing every record again. A row added while a picks view is on screen joins its picks, or it would vanish the moment it was made: the grid's a, the New row control and the header's + Row all add it there, and while editing picks the new row is selected so Update picks keeps it. Picks that name deleted rows count for nothing and drop out at the next edit. The Board honors picks the same way, since both surfaces get their rows from viewRows. It shows the strip with Show all rows but no Edit picks: a board has no row selection, so a board's picks come from duplicating a board that has them, or from schema.json. Switching to another view tab now leaves edit mode and clears the selection, since the other view has picks and columns of its own; before, a selection made in one view stayed checked in the next.
Every new database control gets a command palette entry, so it can take a key of its own like any other command (#971): Choose Database Columns, New Table View, Duplicate View, Show Only Selected Rows and Show All Rows, in the Database category next to Create Pages for All Records. They act on the database in the active pane, opened as a database tab or as a .csv file, and its active view, and each lists only where it can do something: Choose Database Columns and Show Only Selected Rows on a table view, Show All Rows on a view with picks, the other two on any database. New Table View opens the Columns popover for the new view, as the + menu does. Show Only Selected Rows with nothing selected says how to select rows instead of doing nothing. What they act on lives in component state rather than the store (whether the Columns popover is open is the header's; the row selection is the Table's), so a command names its database on a window event, zen:database-view-command, and only the DatabaseView in the active pane answers it; the same database open in a second pane stays as it is. COMMAND_CATALOG is regenerated from buildCommands, so the five ids are bindable in Settings, config.toml and :bind.
The in-app manual says what the database views now do, in the words the UI uses. "Any CSV is a database" covers the Columns button and its "Columns 5 of 44" label, Hide column, the + menu (New table view, New board view, duplicate), Duplicate view on a view tab, Show only these in this view and the "Showing N picked rows" strip with Edit picks and Show all rows, rows added in a picks view, the Board, and the five palette commands. The Database grid shortcuts gain c (Vim mode, with what the popover's keys are in both modes), and the m, Space / x, a and Esc rows say what they now do around picks and the column menu.
Run with nothing selected, the command says how to select rows. Its first wording named x, a key hint that, like every other in the app, belongs to Vim mode, so with Vim off it now reads "the row checkboxes, or Space in the grid" instead.
…of it Make a note and start typing: the text went in front of the heading, as `Body# My note`. On desktop and on both phones, whichever way the note was made: naming it in the name field and pressing Enter or Escape, or a New Note command from the palette that hands the editor the keyboard itself (seen on Android). iPhone build 52 (core 2.78.0) does the same, so it is not new in 2.79.0. A new note's body is `# <title>` and an empty line, and its editor starts with the caret at 0. The name field's Enter hands the focus to the editor (#214's "Enter to drop into the body") and a writing command does the same, but nothing moved the caret; a rename rewrites the heading as a minimal change, which leaves a caret at 0 where it was. focusEditorNormalMode, the hand-off every one of those paths goes through, now drops the caret of a note that holds only its heading, with the caret still at the very start, to the end, on the empty line under the heading. A note with more in it, a caret already placed, a selection, or a heading with no line under it is left alone. Found by the iPhone 1.34.0 walk (cause traced there) and the Android 1.1.50 walk (the palette path); reproduced on the desktop build over CDP: Vim on, name, Enter, `i`, `Body` saved `Body# Caret check`.
The database grid answered h/j/k/l, H/L, 0 ^ $, gg/G, i, x, dd, a, o and m
whether Vim mode was on or not, so with it off a stray letter typed at a
focused grid moved the cursor, reordered a column, added a row, opened a
page, or (dd) deleted the record under the cursor. This release already
made the sidebar, the note list, Connections, the Outline and Comments
follow the rule every list in the app keeps: single-key motions and verbs
are Vim mode's; the arrows, Enter and Escape stay live. The grid was the
one surface left.
Which keys answer in which mode now lives in one function,
databaseGridKeyAction (app-core), and the grid's handler acts on what it
returns rather than on e.key. With Vim mode off the grid answers:
arrows move the cell cursor; up past the first row reaches the
column header, as k does
Enter edit the cell (toggle a checkbox cell; rename the field
on the header)
Space select the row (toggle a checkbox cell)
Escape leave Edit picks, clear the selection, leave the grid
Shift+F10 / open the row menu, or the column menu on the header, as
Context Menu in the sidebar and the note list
and nothing else: every letter, digit and Vim symbol is ignored, as is
any chord, which stays the app's. With Vim mode on everything works as
before, and Shift+F10 and the Context Menu key now open the menus there
too. Space already selected rows alongside x, so it stays. Everything the
letters did is still reachable without them: Open page, Delete row and the
picks items in the row menu, Move left / Move right, Hide column and
Choose columns in the column menu, New row and + Row, the Columns button,
and the command palette.
Delete and Backspace deliberately do not delete selected rows. Deleting a
row asks first only when the row has a linked page; a plain row goes at
once, through the selection bar's Delete, the row menu's Delete row or
dd, and a key that sits next to text editing should not get that power
without a confirm the grid does not have. Row deletion stays on those
explicit controls.
The column menu named H and L next to Move left and Move right in either
mode; those hints now show only with Vim mode on, like c beside Choose
columns and O in the database menu. The Database grid table in the manual
now leads with the keys that work in both modes, marks every letter row
"Vim mode." with where its action lives otherwise, and says so in its
description, as the other tables do; the card text names x and o only as
Vim mode keys.
Verified with unit tests for the gating (every grid letter answers with
Vim on and none with it off, the universal keys in both modes, chords and
plain F10 left alone, Delete and Backspace untaken), app-core typecheck and
its full suite, the three Vim smoke suites, and the CDP driver on the
built app: with Vim off the arrows moved the cursor two rows and two
columns, j k h l H L G gg 0 $ x i o a m c O and dd changed nothing (cursor,
rows, columns, selection, menus, tabs), Space selected, Enter edited,
Shift+F10 opened the row menu, the Context Menu key on the header opened a
column menu without H or L; with Vim on every letter still did its job.
…cked rows, and the + makes table views and copies
… the picture each card draws charles b. asked, once covers shipped in 2.78.0, for "a gallery view of all notes in a folder by cover thumbnail". This is the part of it that needs no screen: pure functions the view is a thin shell over, so each rule is tested on its own. lib/folder-gallery.ts decides: - the tab: a gallery is `zen://gallery/<folder>/<encoded subpath>`, one tab per folder, and it follows its folder through a rename or a move (and closes when the folder is deleted) through the workspace's own path rewrite, root-mode and remapped system folders included; - which notes: the folder's own notes, or every note below it with subfolders on. Drawings and a database's record pages stay out; - the order: Updated (last edited first, the default), Created (newest first) or Title (natural, case-insensitive), ties by title and path so the grid never shuffles; the title filter keeps a note whose title holds every word typed; - the grid: columns that fill the width at 200 px or more a card, never fewer than two (a phone shows two small cards a row) and never more than eight, every card the same height, which is what lets the view mount only the rows on screen; the cursor moves (left and right in reading order, up and down keeping the column, down past a short last row landing on the last card) and the scroll that keeps the cursor's card in view; - the picture: a vault image is the file the host serves, a built-in gradient falls back to its own SVG when the vault cannot hand the file over yet, an Unsplash photo is asked for at 800 px instead of the cover's 2400 (the rest of the URL, its ixid included, kept byte for byte, since Unsplash wants its photos hotlinked), any other https cover as it is. No cover, or one this device cannot find, is the placeholder with the title's first letter; a card is not where a cover gets fixed; - `:gallery <folder>`: a path in the vault, a folder in the notes area as :newtask takes it, or a system folder by key or label. lib/gallery-covers.ts reads covers on demand. NoteMeta carries no cover and does not gain one: that would change the desktop scan, the Go server and the phones' vault code. A card asks when it comes on screen; the note is read once, an open buffer first, and the answer is kept against the note's path and updatedAt, so an edited note is read again and an unchanged one never is. Cards asking at once share one read, a note that cannot be read has no cover until it changes, a workspace switch empties the cache and drops a read that lands after it. runLimited bounds how many reads run at once and stops taking new ones when a scroll moves on. Tests: tab paths and their rewrite on rename and delete (inbox, root mode, remapped archive), membership with and without subfolders, the three sorts, the filter, initials, dates, the folder argument, the grid's geometry and row window, cursor moves, scroll-into-view, the thumbnail choice for every cover kind, and the cover cache (read once, re-read on change, shared reads, failed reads, scope switch, eviction).
…older's menu Right-click a folder in the sidebar and choose Open as Gallery: the folder opens as a tab, named after it, with its notes as cards. Each card leads with the note's cover (an image in the vault, an Unsplash photo or a gradient, cropped to fill at the cover's banner_y), then the title and the date; a note without a cover gets a quiet card with its title's first letter. The header carries the folder's name, "Gallery" and the note count, a Sort menu (Updated, the default; Created; Title) and an Include subfolders switch. A database folder has no Gallery: its record pages have the database's own grid. The view mounts only the rows on screen (plus two either side) and reads a note for its cover only once its card is on screen, after a short pause so a row flying past during a scroll costs nothing, four reads at a time. A 1000-note folder in the built app: first cards in about 20 ms, 11 note reads to fill the screen, 47 after walking to the last card. Cards are memoized on plain values, so a cover landing redraws its own card. Keys, the Unresolved Links view's way: the view holds the keyboard while the editor panel would, and VimNav stands down for it except for the leader. With Vim on, h/j/k/l move across the grid, gg/G jump to the ends, Enter or o opens and / moves into the filter; with Vim off only the arrows, Enter and Escape act, and the filter field takes typing. Escape clears the filter. A chord bound to one of the nav actions works in either mode (#972). Every handler stands aside while a modal, a palette or a menu is open. The cursor card has an accent ring; opening a note moves into the editor the way other openers do, and Ctrl+O comes back to the Gallery (a gallery tab is a jump-history stop like Tasks and Tags), on the card it left from: each tab remembers its cursor, scroll and filter. Sort and subfolders are portable preferences, gallery_sort and gallery_include_subfolders under [view] in config.toml, beside asset_sort_order and timeline_range: they are how a person likes to browse, the same on every machine, and change only when chosen. They apply to every Gallery rather than per folder, which would turn a view setting into per-vault state. At phone width the grid keeps two columns and the header wraps. An Unsplash cover's credit shows on the card on hover, through NoteCoverCredit's new compact frame, and always on a screen that cannot hover, as on the note's own cover. Verified with the next commit's openers in the built app over CDP, with isolated userData and config dir (68 checks: the folder menu, sort, subfolders, filter, Vim on and off, the modal bail, open and Ctrl+O, phone width, the 1000-note folder), on a self-hosted server vault (the desktop app in remote mode against znserver) and in the web app (Vite client over znserver, headless Chrome). No console errors. Tests: the gallery prefs' defaults, round trip and normalizing, the opener (one tab per folder, focus to the view), a Gallery tab following a renamed folder and closing with a deleted one in both root modes, and the config.toml round trip.
Every new feature ships with a Vim binding, and the folder menu is a mouse route, so the Gallery gets three keyboard openers: - Open Folder Gallery in the command palette (view.folder-gallery). It is in the commands registry, so it can take a key of its own (#971). - Space G with Vim on. `g` is the Atlas, so the Gallery takes the capital: after the leader `G` is a key of its own, as `<leader>q` and `<leader>Q` are. The free lowercase keys (j, k, x, y, z) say nothing about a gallery, and `x` is where the manual's examples put people's own command keys. A user command sequence can no longer start on `G`, since built-in leader keys win. The leader hints list it as Folder gallery. - `:gallery` (`:gal`) on the ex line, with an optional folder: `:gallery Projects/Website`, `:gallery inbox/Projects` or `:gallery archive`. A folder nothing in the vault answers says so in a toast instead of opening an empty tab. Which folder, when none is named (lib/folder-gallery-open.ts): the row under the sidebar cursor while the sidebar holds the keyboard (a folder row is that folder, a note row its note's folder, the Archive row the archive), the way the palette's Move Note to Trash follows the sidebar (#972); otherwise the open note's folder; from a Gallery its own folder; and last the top of the notes area. Verified in the built app over CDP: :gallery from a note opens its folder, the palette row shows Space G and opens the open note's folder (a subfolder, Recipes/Desserts), Space G from a note opens its folder, and with the sidebar focused and its cursor walked onto the Recipes folder row, Space G opens Recipes. Tests: the folder each context means (sidebar folder, note and Archive rows, the open note, an open Gallery, the fallback) and the argument path, including the toast for a folder that is not there.
The Help view now has: - a how-to card, "See a folder as a gallery of its covers", after the two cover cards: what a card shows, the four ways to open it and which folder each one picks, sort and subfolders with their config.toml keys, what is left out, the keys with Vim on and off, Ctrl+O back to the card, and that it reads only the covers on screen, works on a self-hosted server and in the web app, and keeps two columns on a phone; - Space G in the leader table, next to Space u; - `:gallery [folder]` in the ex commands, before `:unresolved`; - a Gallery view section with its keys. HelpView resolves those keys from the nav.* bindings and Space G from vim.leaderFolderGallery, so the manual follows a rebinding; - the Gallery among the list views a chord can reach (#972). Open Folder Gallery reaches the manual's command list on its own, from the palette. The website text, the same facts, is in docs/releases/v2.79.0/website-docs-folder-gallery.md for the website pass.
…iews that keep their own columns charles b. asked for both on release morning and they joined 2.79.0: a folder opens as a Gallery of its notes by cover, and a database table view keeps its own columns and picked rows. The What's New card names them in the words the app shows (Open as Gallery, Columns, Show only these in this view) and the keys that open them.
… one, as one edit (#1016) williamcheuk03 asked for a right-click way to make text a callout and to switch an existing callout between types. Before any menu, this lands the edits themselves as pure functions over the note text, so the menu, the palette and a leader key can share one rule for what they touch. What a caret or a selection targets: - Inside a callout, the innermost callout around the whole selection. - Otherwise a selection covers its whole lines (one that ends at the very start of a line leaves that line out), and a caret takes its paragraph: the non-blank lines around it, stopping at a heading, a code fence and the edge of a quote. A caret anywhere in a list takes the whole list, and a paragraph inside a list item stays in that item, indentation and all. - A block is never cut in half. A selection that reaches into part of a fence or a quote grows to hold all of it, so a callout it touches ends up nested, whole, inside the new one. - Nothing in a code fence or in the frontmatter: the lines there are code and YAML, and `> ` in front of them would change what they say. The three edits: - Turn into writes `> [!type]` and prefixes each line `> ` (a blank line `>`). A plain blockquote already has its markers and only gets the title line above it. Markdown reads text right under a quote as more of that quote, so a callout made from part of a paragraph, or next to a quote, gets a blank line where it would run into its neighbor; without it the title under a quote is no callout at all. - Change rewrites only the keyword, so the fold marker and the title stay. A callout already of that type, alias included (`[!error]` is danger), is left as written. - Remove drops the title line, keeping a custom title as a plain first line, and strips one level of `> `, the callout's own, so a quote or callout nested inside keeps its markers. Lines split at `\n` with any `\r` kept, so a CRLF note stays CRLF. The editor side dispatches each edit with its own user event, not `input.type`, so typing just before it never folds into the same Undo.
… type (#1016) The editor's right-click menu gains a callout group after Paste, behind a separator: - On text, a selection or the paragraph under the pointer: "Turn into callout…". - Inside a callout, written out or drawn as a card in Live Preview: "Change callout type…" and "Remove callout". - In a code block or the frontmatter, "Turn into callout…" shows disabled with the reason beside it, the way Cut and Copy show disabled without a selection, so the menu says why rather than hiding the row. A blank line gets no row: there is nothing there to turn into anything, and it is where a paste usually goes. The "…" items open a second menu at the same spot, titled "Callout type": all twelve types in CALLOUT_TYPES order with their icons, the current type checked and the cursor starting on it. The issue asked for Error by name; `error` is danger's alias, so that row reads "Danger (Error)" and typing "error" finds it. The filter also matches the other aliases, so "caution" finds Warning and "tldr" Abstract. ContextMenu learns three small things for it, no submenus: a title, a checked row (a check mark and menuitemradio), and keywords the filter matches. What the items act on follows the pointer, not the caret. A callout in Live Preview is line decorations over editable text, so its right-click already reached this menu, but the menu acted on the selection, which can sit anywhere: a click on a rendered callout has to mean that callout. A right-click inside the selection still means the selection. The pane notes the selection when the right button goes down, because on a Mac the press selects the word under the pointer before the menu opens. A menu opened from the keyboard has no pointer and acts on the selection. The reading view stays read-only. Its only write is ticking a task; double-click a callout there and it opens in the editor, where the menu is.
…and Space l a (#1016) Every feature ships with a key, so the callout menu gets three ways in that need no mouse: - "Set Callout Type…" (editor.callout-type) opens the type menu at the caret. Outside a callout a pick turns the paragraph or the selected lines into one; inside, it changes that callout's type, the current one checked. In a code block, the frontmatter or on a blank line it says why in a toast instead. - "Remove Callout" (editor.remove-callout) takes the callout at the caret back to plain text, or says the cursor is not in one. - `Space l a` in Vim mode runs Set Callout Type…. `c` is the cover, so it takes `a`, for admonition and for the alerts GitHub calls them. It sits in the note-actions group with its which-key row ("Callout type"), and the right-click menu shows it beside Turn into and Change type, in Vim mode only: with Vim off the chord does nothing, so no row names it. Both commands are listed whenever a note is open, like Convert Table to Database, and both can take a key of their own (#971); the catalogs in shared-domain carry them, and the leader group knows `a` is taken. Remove Callout has no default key: it is one row away in the menu, from the right-click or from Shift+F10 on a selection. Running Set Callout Type… from the palette first left the menu open without the keyboard. The palette, closing, hands the focus back to the editor once the command has run, unless the command opened something it knows of; a ContextMenu was not on that list, so the letters typed to filter the menu went into the note as Vim commands. A command that leaves a menu open now keeps the keyboard in it.
The shared ContextMenu moved its cursor on j and k whatever the Vim setting, while its other letters filter the list. The rule since the sidebar work is that with Vim off only the arrows, Enter and Escape act on a list; single letters are text. The callout type menu (#1016) made the gap plain: with Vim off, typing "j" should look for a type, and it jumped a row instead. Now j and k move only with Vim on. With it off they join the filter like every other letter. Ctrl+N and Ctrl+P keep moving either way, since a chord is not text. The arrows, Enter, Escape and Backspace are unchanged.
The Help view now has: - a how-to card after "Callouts highlight the important bits", "Turn text into a callout from the menu": the three right-click items and the "Callout type" menu by the names they carry, Danger (Error) and the alias filter, what a selection, a paragraph, a list and a plain quote become, the blank line a callout gets beside its neighbors, what Change keeps and what Remove drops, Set Callout Type… and Remove Callout in the palette, Space l a, `m` or Shift+F10 on a selection in Vim mode, one Undo step, why code and properties are refused, and the keys with Vim off; - a pointer to it at the end of the callouts card; - Space l a in the leader table. HelpView resolves it from vim.leaderNoteActions and vim.leaderCalloutType, so the row follows a rebinding. Set Callout Type… and Remove Callout reach the manual's command list on their own, from the palette. The website text, the same facts, is in docs/releases/v2.79.0/website-docs-callout-menu.md for the website pass.
…anges its type or removes it (#1016) A correction to 1e1d7d1's message: Remove Callout is reachable from Shift+F10 on a selection in Vim mode only; with Vim mode off on macOS, Shift+F10 opens no editor menu (as before this branch), and the palette's Remove Callout and the right-click menu are the routes.
…or 80 ms The gate run on 14b2159 failed "lists two blocks under a note, then a row for the rest" once: the rows were not drawn yet. The test waited a fixed 80 ms for the tagged blocks' reads, which land through a notify timer, and with every package's suite running at once (turbo test:run) that chain took longer. On its own the file passed 5 of 5 and the app-core suite 4184 of 4184. After typing a #tag query the tests now wait until the first block row is drawn (3 s at most), then settle as before. The test that turns tagged blocks off, and the wait after Enter, keep the plain settle: no block row ever appears there. No product code changed.
The code block language picker (#1014) lists every language the editor can highlight, some 130 of them. The shared ContextMenu was built for a dozen rows: it filtered on typed letters without a field to show them, it never scrolled, and a letter matched anywhere in a name, so `c` then Enter picked Brainfuck. A phone has no keyboard to type with until a field is tapped. A menu given `findPlaceholder` now has: - a find field above the rows, holding the query. With Vim off it takes the keyboard when the menu opens and every letter is text. With Vim on the list keeps it, as in every other menu: j and k move, and `/` goes to the field, where j and k are letters too. The arrows, Ctrl+N / Ctrl+P, Enter and Escape act from either place, and an input method's Enter commits its word instead of picking a row. - rows that scroll under the field (a 52vh cap) and keep the cursor's row in sight, at a fixed width so filtering does not resize the menu. - ranked matches: a label or keyword that is the query, then one that starts with it, then a word that starts with it, then anything holding it. Equal matches keep their order. - on a touch screen, the list in charge when it opens, so the keyboard does not rise over the rows it would pick from (the PromptModal rule). `anchorTop` lets a menu opened just below something open above it when there is no room below, instead of sliding up over what opened it. Menus without `findPlaceholder` behave as before.
…e it lacks (#1014) williamcheuk03 asked for a language drop-down on code blocks that lists the supported languages and allows a manual override, alongside automatic detection. The label at the top right of each fenced block in Edit mode read the fence's language and, pressed, copied the block. Changing the language meant finding the fence and typing a tag the editor might not know, and a block with no tag stayed grey in the editor while the reading view coloured it with highlight.js' best guess, however poor. The label is now a language button, "typescript ▾", with copy moved to an icon button beside it (aria-label "Copy code", a check while copied). The button opens the "Code block language" picker, the shared menu with a find field: every language the editor highlights by name (the eager grammars, language-data and the user's TextMate languages), the current one checked, tags searchable (`golang` finds Go). A pick rewrites only the opening fence's language word: the backticks or tildes, their count, a quote's markers and anything after the word stay, the caret stays on its character, and it is one Undo step of its own. "Text (plain)" takes the language away (or writes `text` when attributes follow, which deleting the word would promote to the language). Each row writes a tag the editor reads back as that language: `cpp`, `csharp`, `bash` for Shell (highlight.js reads `shell` as a terminal session); a language no fence tag can reach, such as VB.NET, is left out rather than offered as a pick that would not highlight. A block that names no language is guessed at, off the typing path: 400 ms after edits or scrolling settle, in idle time, a few blocks per slice, cached by the block's text. highlight.js scores the block in 22 languages that both lowlight's common set and the editor know, and a guess must be confident: - at least 2 lines and 24 characters; - relevance 8 or more on the first 2,000 characters (a block over 50,000 characters is not guessed at); - a hallmark of the language in the text, since relevance only adds up and a long paragraph clears any bar as CSS ("a", "code" and "line" are tag names a selector may use): `fn` or `::` for Rust, an `end` line for Ruby, a `prop: value;` rule for CSS, a statement opening a line for SQL; - no language of another family within 2 points (a class that is valid Java and valid C# stays text). A shebang, `<?php` and a JSON document are taken at their word. The bar was tuned on 122 real snippets in code-language-detect.samples.ts: every medium snippet in 22 languages is named right, none of 22 prose, log and data texts is guessed at even repeated twelve times, nor are 28 snippets in other languages; a short snippet is either named right or left as text. A guessed block is parsed with the guessed language's own CodeMirror parser, over the same ranges the markdown parser hands a labeled block's language, and drawn with `highlightTree` through the editor's own highlight style, so it looks exactly like the block labeled. Its label reads "python · detected", muted, in a dashed outline. The guess is never written to the file; the picker's first row, "Python (detected)", writes it. A guess stays with its block through edits until the next guess replaces it, and a block longer than 6,000 characters keeps its colours mapped while it is typed in and is redrawn once typing pauses, so typing there costs what it does in a labeled block. `text` and its spellings now read as plain text in the editor too: left to language-data's fuzzy matcher, `text` contains `tex` and was highlighted as LaTeX. The guessing is the `detectCodeLanguages` preference (on); Settings and the reading view follow in the next commit.
…sses as the editor does (#1014) Settings → Editor → Writing gets "Detect code block languages", on by default. Off, Edit mode guesses nothing (the detection extension is not loaded) and an unlabeled block is plain text. The reading view guessed on its own: rehype-highlight ran with `detect`, which takes highlight.js' best score over all of lowlight's languages however low, so `x = 1` came out as INI and a paragraph as CSS, and a block the editor now calls "python · detected" could read as another language there. The reading view now names an unlabeled block with the editor's guesser, the same subset and bar, and rehype-highlight colours it by that class with its own `detect` off. Both surfaces agree on every block, and the setting switches both: off, the reading view leaves an unlabeled block plain as well. A tiny block that `detect` used to colour stays plain now. An indented code block, which the editor cannot label, is still guessed at in the reading view, with the same bar. The setting is portable: `detect_code_languages` under `[editor]` in config.toml, beside `render_tables`. It is a writing preference with nothing machine-specific in it, like its neighbours, so it travels with the rest of the editor's settings. markdown-settings carries it to the renderer, with its revision in the render cache key, so a switch redraws the reading view at once.
…1014) Every feature ships with a key, so the picker has two ways in that need no mouse: - "Set Code Block Language…" (editor.code-language) opens the picker for the code block the cursor is in, under the block's label when it is on screen, at the cursor in Split, which has no label. Outside a code block a toast says "The cursor is not in a code block." It is listed whenever a note is open, like Set Callout Type…, and can take a key of its own (#971); the catalogs in shared-domain carry it. - `Space l l` in Vim mode runs it, `l` for language: the note-actions group's own key, pressed twice. f, y, s, d, c, u and a were taken. It has its which-key row ("Code block language"), the leader group knows `l` is taken, and the Help view's shortcut table resolves it from vim.leaderNoteActions and vim.leaderCodeLanguage, so a rebinding shows. With Vim off the chord does nothing. The palette hands the keyboard to the picker it opened, as it does for the callout menu: the picker is a ContextMenu. The request goes through code-language-request.ts, a module with nothing behind it. Imported from the picker's module, the command list pulled language-data into the app's entry chunk.
…icker (#1014) The Help view now has: - a how-to card before "Add syntax highlighting for another language", "Set a code block’s language": the language button and the copy button beside it, the "Code block language" picker and its find field (tags count, an exact name first), what a pick rewrites and what it keeps, one Undo step, "Text (plain)", the user's own languages in the list, the guess for a block with no language ("python · detected", never written, "Python (detected)" writes it), tiny and ambiguous blocks staying text, the reading view agreeing, Set Code Block Language… and `Space l l`, the keys with Vim on and off, and the setting; - `Space l l` in the leader table, resolved from vim.leaderNoteActions and vim.leaderCodeLanguage; - "Detect code block languages" in the Editor behavior settings, with its config.toml key. Set Code Block Language… reaches the manual's command list on its own, from the palette. The website text, the same facts, is in docs/releases/v2.79.0/website-docs-code-language.md for the website pass.
…age and guesses one it lacks (#1014)
…from a menu #1016 and #1014 (both asked for by williamcheuk03) joined 2.79.0 on release day: right-click to turn lines into a callout or change one's type, and a code block's label that picks its language and shows a guess for a block without one. One What's New card names both, in the words the app shows.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two features asked for by charles b. on release morning, and two phone-walk fixes, joining 2.79.0 before the tag.
Database table views (merge efbf7e3, 8 commits)
cin the grid with Vim on.DbView.pickedRowIds.Folder Gallery (merge ee5d1dc, 4 commits)
:gallery [folder], orSpace G.NoteMetais unchanged. A 1000-note folder took 11 reads to fill the screen.gallery_sortandgallery_include_subfoldersunder[view]in config.toml.Fixes from the phone walks
# Title, not in front of it. This is pre-existing (core 2.78.0 too). It covers the name field's Enter and Escape and the palette's New Note, throughfocusEditorNormalMode.What's New lists both features (a7b5540).
Verification so far
How to test locally
Body. The note reads# <name>, an empty line, thenBody.