API dokumentace
Nibble je rozdělený do tří balíčků: @nibble/core (model, výběr,
historie, příkazy, registr ovládání), @nibble/ui (lišta, nabídka,
dialogy, CSS) a @nibble/plugins (odkazy, obrázky, tabulky, videa,
nástroje, písma, úkoly, Markdown při psaní). Jádro běží i bez lišty — hodí se to v testech a při dávkovém
zpracování obsahu.
Instalace
git clone https://github.com/svatekr70/nibble npm install npm run build
Build vytvoří ESM bundly a stylopis. Do stránky se vkládají takhle:
import { Nibble } from '@nibble/core' import { attachToolbar } from '@nibble/ui' import '@nibble/ui/nibble.css'
Knihovna do stránky sama nic nevkládá. Buď načtěte nibble.css,
nebo si napište vlastní — všechno používá jen třídy s prefixem
nb- a proměnné --nb-*.
Bez balíčkovače (CDN)
Sestavená knihovna je jeden soubor se vším, co je v core,
ui i plugins. Nic se neinstaluje a nic se
nesestavuje — stačí adresa:
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/svatekr70/nibble@v0.6.0/dist/nibble.css"> <div id="obsah"><p>Ahoj.</p></div> <script type="module"> import { Nibble, attachToolbar, link, image, table } from 'https://cdn.jsdelivr.net/gh/svatekr70/nibble@v0.6.0/dist/nibble.min.js' const editor = await Nibble.create({ target: '#obsah', plugins: [link, image, table], }) attachToolbar(editor, { menubar: true }) </script>
Je to modul, takže <script> musí mít
type="module".
@v0.6.0 je neměnné: ten soubor už se nikdy nezmění a jsDelivr ho
drží v mezipaměti natrvalo. Bez verze (@main) se tahá poslední stav
hlavní větve — dobré na zkoušení, nebezpečné v ostrém provozu.
Proto je dist/ v repozitáři, i když se sestavuje: jsDelivr
servíruje soubory přímo z tagu a bez commitnutého buildu by ta adresa
neexistovala. Že bundle sedí se zdrojem, hlídá CI — po sestavení musí být
pracovní strom čistý.
Totéž leží i tady na webu
(https://svatekr70.github.io/nibble/dist/nibble.min.js), jen je to
vždy poslední stav hlavní větve. Jednotlivé balíčky jdou z webu načíst i zvlášť —
dist/core/src/index.js, dist/ui/src/index.js,
dist/plugins/src/index.js — když nechcete tahat všechno.
Rychlý start
import { Nibble } from '@nibble/core' import { attachToolbar } from '@nibble/ui' import { link, createImagePlugin, table } from '@nibble/plugins' import '@nibble/ui/nibble.css' const editor = await Nibble.create({ target: '#obsah', schema: 'legacy', plugins: [ link, createImagePlugin({ // Bez adaptéru se obrázek vloží jako data: URL. upload: async (file) => { const body = new FormData() body.append('file', file) const res = await fetch('/api/upload', { method: 'POST', body }) return (await res.json()).url }, }), table, ], }) attachToolbar(editor) editor.on('change', ({ html }) => uloz(html))
Při napojení na <textarea> zůstává textarea zdrojem pravdy pro
odeslání formuláře — stačí vyměnit jeden řádek a odesílání funguje dál.
Nibble.create()
Vrací Promise, protože pluginy smí mít asynchronní přípravu (třeba
načtení seznamu písem). Existuje i pojmenovaný export create() se
stejným chováním.
target je CSS selektor, prvek nebo <textarea>.
U textarey se převezme její obsah, textarea se schová a při každé změně se do ní
zapisuje výsledek — odeslání formuláře pak funguje beze změny v backendu.
Přehled voleb
| Volba | Typ | Význam |
|---|---|---|
target | string | HTMLElement | Kam se editor postaví. Povinné. |
content | string | Počáteční HTML. Bez něj se převezme obsah cílového prvku. |
schema | 'strict' | 'legacy' | Jak přísně se hlídá tvar obsahu. Výchozí legacy. |
entityEncoding | 'named' | 'utf8' | 'auto' | Jak se zapisují entity. auto se řídí tím, co bylo v původním obsahu. |
height | number | Minimální výška plochy s obsahem v pixelech. |
readonly | boolean | Editor se otevře jen ke čtení. Jde přepnout přes setMode(). |
autofocus | boolean | Zaostřit obsah hned po vytvoření. |
placeholder | string | Šedá nápověda v prázdném editoru. Bez uvedení se převezme z <textarea placeholder>. Je jen v CSS, do getHTML() se nedostane. |
plugins | Plugin[] | Hodnoty, ne jména. Bundler tak vidí, co se opravdu použije. |
paste | PasteOptions | Chování při vkládání ze schránky — viz níž. |
allowedEmbedHosts | string[] | Hostitelé, jejichž <iframe> smí být v obsahu. |
Schema
Schema říká, co smí kde být. Dva režimy:
| Režim | Chování |
|---|---|
legacy | Pouští dál i značky a atributy, které by dnes nikdo nenapsal — <font>, align, <center>. Pro obsah, který vznikal roky v jiných nástrojích. |
strict | Sémantické značky a inline styly. Pro nový obsah a nové projekty. |
Srovnávání tvaru je líné: běží jen na tom bloku, do kterého uživatel opravdu zasáhl. Načtený dokument se nikdy nepřepisuje jen proto, že by se dnes zapsal jinak.
Zkontroluje obsah proti schématu, aniž by cokoli měnil. Bez argumentu kontroluje aktuální obsah editoru. Hodí se na dávkovou revizi databáze před migrací.
Vkládání
await Nibble.create({ target: '#obsah', paste: { keepStyles: ['color', 'background-color', 'text-align'], markdown: true, }, })
| Volba | Typ | Význam |
|---|---|---|
keepStyles | string[] | Které vlastnosti stylu si z vloženého obsahu nechat. |
allowedTags | Set<string> | Povolené značky. Co v seznamu není, se rozbalí a text zůstane. |
markdown | boolean | Převádět Markdown, když přijde čistý text. Výchozí true. |
Zdroj vloženého obsahu se rozpoznává a čistí se podle něj. Událost
pasteclean nese, co se stalo:
editor.on('pasteclean', ({ source, removed }) => { // source: 'word' | 'gdocs' | 'libreoffice' | 'html' | 'text' console.log(source, removed) })
Vložené rámce
<iframe> je jediná značka, která do stránky pouští cizí kód.
Nibble ho proto neřeší seznamem zakázaného, ale seznamem povoleného —
co v něm není, se zahodí:
await Nibble.create({ target: '#obsah', // Prázdné pole zakáže rámce úplně. allowedEmbedHosts: ['youtube.com', 'youtube-nocookie.com', 'vimeo.com'], })
Bez uvedení platí DEFAULT_EMBED_HOSTS — běžné videoslužby. Kontrola
porovnává hostitele včetně poddomén, ne podřetězec v URL.
Obsah
Serializovaný obsah. Bloky, kterých se nikdo nedotkl, se vypíšou v původním znění — proto je výsledek po pouhém načtení znak po znaku shodný se vstupem.
Nahradí obsah. Zahodí historii oblastí a založí nové snímky, takže záruka round-tripu platí i pro nově nastavený obsah.
Vloží HTML na pozici kurzoru. Obsah projde sanitizací, takže je bezpečné sem poslat i něco, co přišlo zvenčí.
Které uzly nejvyšší úrovně se od načtení změnily. Užitečné při ladění i v testech — demo na tomhle webu jimi ukazuje, kolik bloků úprava opravdu zasáhla.
Stav a služby
| Vlastnost | K čemu je |
|---|---|
editor.root | Prvek s contenteditable. Sem míří všechno, co se dotýká DOMu. |
editor.document | Dokument, ve kterém editor žije. Příkazy z něj vytvářejí uzly. |
editor.selection | getRange(), setRange(), collapseTo(), save(), restore(). |
editor.schema | Kontrola tvaru obsahu. |
editor.history | undo(), redo(), canUndo(), transact(fn) — zápisy do historie uvnitř se zahodí, takže úprava složená z více příkazů je po editor.commit() jeden krok. Jde vnořit. |
editor.formatter | apply(), remove(), toggle(), match(), queryStyle(). |
editor.commands | Registr příkazů: add(), has(). |
editor.ui | Registr ovládání, dialogy, hlášky, stavový řádek. |
editor.plugins | Jména načtených pluginů v pořadí, ve kterém se nastavovaly. |
Akce a dotazy
editor.exec('link', { href }) // → boolean (šlo to?) editor.can('link') // smí se to teď? → stav tlačítka editor.is('bold') // je výběr tučný? → aktivní tlačítko editor.focus() editor.setMode('design') // 'design' | 'readonly' editor.destroy()
Proti TinyMCE zmizel globální singleton: žádné nibble.activeEditor.
Instanci si drží ten, kdo ji vytvořil.
Pomocné dotazy na aktuální blok používá lišta a hodí se i vlastnímu ovládání:
editor.getBlockTag() // 'p' | 'h2' | 'blockquote' | null editor.isInList() // 'ul' | 'ol' | null editor.getAlignment() // 'left' | 'center' | 'right' | 'justify' | '' editor.currentBlock() // Element | null
bullist a numlist berou nepovinné { type } —
data-type nově zakládaného seznamu. Nastaví se dřív, než se seznam
slije se sousedy; sousední seznamy se totiž slévají jen při stejném
data-type, takže úkoly pod odrážkami zůstanou úkoly.
editor.exec('bullist', { type: 'taskList' }) editor.exec('tasklist') // tasks: přepne seznam úkolů editor.exec('details') // details: obalí označené bloky editor.exec('detailsopen') // přepne atribut open editor.exec('undetails') // zruší blok, obsah zůstane
Blokové příkazy (nadpis, seznam, citace) v nadpisu rozbalovacího bloku
(<summary>) nic neudělají a vrátí false — nadpis
je jeden řádek.
Pro pluginy, které reagují na psaní:
Text bloku od začátku po kurzor, i když je blok rozdělený do více uzlů a značek. Na něm stojí Markdown zkratky i lomítková nabídka.
Je v editoru jen jeden prázdný odstavec? Podle toho se ukazuje
placeholder.
Události
on() vrací funkci pro odhlášení — nemusíte si držet referenci na
posluchače:
const off = editor.on('change', ({ html }) => uloz(html)) off()
| Událost | Kdy nastane |
|---|---|
change | Obsah se změnil a změna se uzavřela do kroku historie. Nese { html }. |
input | Každý zásah do obsahu, ještě před uzavřením kroku. |
selectionchange | Kurzor nebo výběr se pohnul. Podle toho se sesynchronizuje lišta. |
setcontent | Obsah byl nahrazen přes setHTML(). |
modechange | Přepnul se režim (design / readonly). |
pasteclean | Doběhlo čištění vloženého obsahu. Nese { source, removed, html }. |
schemaviolation | Do obsahu se dostalo něco, co schema nepovoluje. |
limit | Počet znaků přešel přes maxChars nebo se vrátil pod něj (createWordcountPlugin). Nese { count, max, over }. Jen při přechodu, ne po každém úhozu. |
itemsplit | Enter rozdělil položku seznamu. Nese { from, to } — původní a novou <li>. |
listretag | bullist / numlist přepnul značku existujícího seznamu. Nese { list, tag }. |
attachToolbar()
const ui = attachToolbar(editor, { layout: [ ['undo', 'redo'], ['bold', 'italic', 'underline'], ['link', 'image', 'table'], ], layoutBottom: [['code', 'fullscreen']], menubar: true, prefsKey: 'clanky', prefs: { height: '500px' }, settings: true, })
| Volba | Typ | Význam |
|---|---|---|
layout | string[][] | Skupiny tlačítek v prvním řádku lišty. |
layoutBottom | string[][] | Skupiny, které mají začít ve druhém řádku. |
menubar | boolean | MenubarMenu[] | Nabídkový pruh nad lištou. true použije výchozí rozvržení. |
prefsKey | string | Klíč pro uložení nastavení uživatele (nibble:prefs:<id>). |
prefs | Partial<EditorPrefs> | Výchozí hodnoty nastavení. Uživatel je může přebít. |
settings | boolean | Ozubené kolo vpravo nahoře. Jediné místo, kde se dá schovat. |
Vrácené EditorUI nese toolbar, menubar,
contextToolbar, prefs a destroy().
Výchozí rozvržení obsahuje: undo redo · blocks fontfamily
fontsize lineheight · bold italic underline strike ·
forecolor backcolor · bullist numlist tasklist ·
alignleft aligncenter alignright alignjustify · link image
media table emoji charmap · blockquote hr removeformat · code
searchreplace fullscreen. Tlačítko, jehož plugin není načtený, se
v liště prostě neobjeví.
Registr ovládání
Plugin si přihlásí ovládací prvek a lišta ho vykreslí. Žádné další zařizování není potřeba — a jádro nemusí o liště vědět.
editor.ui.addButton('zvyraznit', { icon: 'forecolor', tooltip: 'Zvýraznit', shortcut: 'Ctrl+H', onAction: () => editor.exec('zvyraznit'), active: (ed) => ed.is('zvyraznit'), enabled: (ed) => ed.can('zvyraznit'), })
| Metoda | Vykreslí se jako |
|---|---|
addButton(name, spec) | Tlačítko, případně přepínač. |
addSelect(name, spec) | Rozbalovací seznam (druh bloku, písmo, velikost). |
addColor(name, spec) | Výběr barvy s kolečkem a paletami. |
addMenu(name, spec) | Tlačítko s nabídkou položek. |
addGrid(name, spec) | Mřížka — používá ji výběr rozměru tabulky. |
addContextToolbar(name, spec) | Plovoucí lišta nad vybraným prvkem. |
addSuggestion(name, spec) | Nabídka u kurzoru vyvolaná znakem (lomítkové příkazy). |
Nabídka u kurzoru
addSuggestion je obecná verze lomítkové nabídky — jiný znak, jiné
položky. Fokus zůstává v editoru: psaní za spouštěčem nabídku filtruje, šipky
vybírají, Enter nebo Tab potvrdí, Escape zavře.
import { filterSuggestions } from '@nibble/core' editor.ui.addSuggestion('mention', { trigger: '@', items: (ed, query) => filterSuggestions(lide, query), onPick: (ed, item) => ed.exec('mention', item.id), })
Položka SuggestionSpec | Význam |
|---|---|
trigger | Znak, který nabídku otevře. |
startOfBlock | Jen na začátku bloku, jako lomítko. Jinak stačí mezera před spouštěčem. |
items(editor, query) | Položky pro napsaný dotaz. Prázdné pole nabídku schová. |
onPick(editor, item) | Vybraná položka. Napsaný dotaz už je smazaný. |
opensDialog(item) | Otevírá položka dialog? Smazání dotazu se pak zapíše jako samostatný krok, takže zavřený dialog nechá prázdný řádek. |
Položka (SuggestionItem) má id, label
a nepovinně icon a keywords.
filterSuggestions(items, query) hledá bez ohledu na diakritiku
a velikost písmen a dává napřed položky, jejichž popisek dotazem začíná.
suggestionQuery(textBefore, trigger, startOfBlock) vrátí dotaz za
spouštěčem, nebo null, když nabídka otevřená být nemá.
Data jsou v jádře, vykresluje je bindCaretMenu(editor)
z @nibble/ui. attachToolbar() ho zapojí sám; ručně je
potřeba jen bez lišty. Vrací funkci pro úklid.
editor.ui.get(name) vrátí přihlášený prvek, names()
seznam všech. Toho využívá i nastavení, aby nenabízelo tlačítka, jejichž plugin
se nenačetl.
Dialogy a hlášky
Dialog se popíše polem polí; vykreslení, ozvučení i validaci obstará lišta.
Vrací null, když uživatel dialog zavřel.
const data = await editor.ui.dialog({ title: 'Vložit odkaz', fields: [ { type: 'url', name: 'href', label: 'Adresa', required: true }, { type: 'text', name: 'text', label: 'Text odkazu' }, { type: 'checkbox', name: 'blank', label: 'Otevřít v novém okně' }, ], submit: 'Vložit', }) if (data) editor.exec('link', data)
Typy polí: text, url, number,
select, checkbox, color,
textarea, code (velké okno se zvýrazněnou syntaxí)
a dvojice emoji / chars (mřížka pojmenovaných znaků
s kategoriemi a hledáním; seznam se předává v field.glyphs,
liší se jen sazbou políček).
Otevření modálního okna přesune fokus a s ním i výběr. ui.dialog()
si ho proto uloží a před spuštěním příkazu vrátí zpátky — jednou pro všechny
dialogy, aby se na to nedalo zapomenout v jednotlivých pluginech.
Stav se ukládá i tehdy, když stavový řádek zrovna neexistuje. Když si ho uživatel
zapne, hodnoty se do něj přehrají — plugin se nemusí spouštět znovu.
'over' položku zbarví jako překročenou; tak počítadlo ukazuje limit.
Nastavení uživatele
Rozvržení lišty navrhne programátor, ale poslední slovo má ten, kdo u editoru
sedí. Nastavení se ukládá do localStorage a přebíjí
konfiguraci.
ui.prefs.get() // aktuální nastavení ui.prefs.set({ sticky: false }) // ovládání se postaví znovu ui.prefs.layoutFor('top') // co se vykreslí v prvním řádku ui.prefs.reset() // zpátky podle konfigurace ui.prefs.onChange(fn) // vrací funkci pro odhlášení
| Položka | Typ | Výchozí | Význam |
|---|---|---|---|
width | string | '' | Šířka editoru. Prázdné = podle obsahu. |
height | string | '' | Výška plochy s obsahem. Zadaná výška zapne rolování uvnitř. |
menubar | boolean | false | Nabídkový pruh. |
sticky | boolean | true | Lišta se drží u horního okraje. |
statusbar | boolean | true | Informační řádek s cestou k prvku. |
resizable | boolean | true | Změna velikosti tažením za pravý dolní roh. |
groups | PrefGroup[] | z layout | Skupiny tlačítek, jejich pořadí, zapnutí a řádek. |
Sedí v prohlížeči uživatele a může být staré půl roku. Slučuje se proto s aktuální konfigurací: prvek, který uživatel zná, si drží pořadí i zapnutí, prvek, který mezitím přibyl, se doplní na konec své skupiny. Bez toho by upgrade editoru znamenal, že nové tlačítko nikdo nikdy neuvidí.
Dialog nastavení umí vypsat hotovou konfiguraci podle aktuálního stavu — když si uživatel lištu přeskládá tak, že to dává smysl, je to nejlepší podklad pro to, jak má editor vypadat pro všechny ostatní.
Dodávané pluginy
| Plugin | Přináší |
|---|---|
link | Vložení a úprava odkazu, plovoucí lišta nad odkazem, Ctrl+K. |
image / createImagePlugin({ upload }) | Obrázky. Bez adaptéru jako data: URL, s adaptérem nahrání na server. |
table | Tabulky: mřížkový model, řádky a sloupce, slučování, tažení šířky sloupce, vlastnosti tabulky i řádku. |
media | Videa přes <iframe> z povolených hostitelů. |
emoji / createEmojiPlugin({ emoji }) | Emotikony: mřížka s kategoriemi a hledáním, česky. Vkládá znak, ne obrázek. |
charmap / createCharmapPlugin({ chars }) | Mapa speciálních znaků v téže mřížce: interpunkce, mezery, měny, matematika, zlomky, šipky, diakritika, řecká abeceda, symboly. Hledá i podle U+00A9. |
code | Editace zdrojového HTML se zvýrazněnou syntaxí a pamatováním pozice kurzoru. |
autolink | Z napsané adresy udělá odkaz po mezeře nebo Enteru. Adresa nebo e-mail vložený ze schránky je rovnou odkaz; vložený na označený text z něj udělá odkaz bez dialogu. V <pre> a <code> zůstane textem. |
wordcount / createWordcountPlugin({ maxChars }) | Počet slov a znaků do stavového řádku. S maxChars ukáže „152 / 160 znaků“ (včetně mezer, jako maxlength), po překročení zčervená a vyšle limit. Nic neusekne. Stejný výpočet je veřejný jako wordcountLabel(text, maxChars?) → { label, count, over }. |
fullscreen | Editor přes celou obrazovku, F11. |
searchreplace | Hledání a nahrazování v textu, ne ve značkách. Ctrl+F. |
typography | České uvozovky, výpustka, pomlčka — při psaní. |
fonts | Rodina a velikost písma včetně webových písem z Google Fonts. |
markdownShortcuts | Markdown při psaní: ## udělá nadpis, **slovo** tučné slovo. První Ctrl+Z převod vrátí. Seznam zkratek je níž. |
tasks | Tlačítko a příkaz tasklist: seznam úkolů <ul data-type="taskList"> s <li data-type="taskItem" data-checked>, formát jako TipTap. Zaškrtávátko kreslí jen CSS editoru. |
details | Rozbalovací blok <details><summary>: příkazy details, detailsopen, undetails a plovoucí lišta. V editoru je obsah vždy rozbalený. |
slash | / na začátku řádku otevře nabídku: nadpisy, seznamy, úkoly, citace, kód, čára, tabulka, obrázek, video, rozbalovací blok. Ukazuje jen to, co je v editoru zapnuté. |
dragHandle | Úchyt ⠿ vlevo u bloku: tažením blok přesune, klikem ho označí. Přesun je jeden krok historie a blok se nemění. Na dotyku se neukazuje. |
Vlastní plugin
Plugin je objekt se jménem a funkcí setup. Ta dostane celý editor —
jakýkoli mezistupeň by musel jen předávat dál. Vrácená funkce je úklid; volá se
v destroy(), takže inicializace i úklid stojí na jednom místě.
import type { Plugin } from '@nibble/core' export const poznamka: Plugin = { name: 'poznamka', setup(editor) { editor.commands.add('poznamka', (ed) => ed.insertHTML('<aside class="pozn">…</aside>')) editor.ui.addButton('poznamka', { icon: 'blockquote', tooltip: 'Poznámka na okraj', onAction: () => editor.exec('poznamka'), }) const off = editor.on('change', spocitejPoznamky) return () => { off() } }, }
Pluginy s volbami se dělají tovární funkcí — createImagePlugin(),
createTablePlugin(), createMediaPlugin(),
createFontPlugin() — aby se konfigurace nemusela protahovat globálním
stavem.
Serializace a round-trip
Parser si u každé oblasti nejvyšší úrovně pamatuje původní zdroj a snímek DOMu. Serializace pak vypíše zdroj doslova — dokud se DOM od snímku neliší. Znamená to mimo jiné, že:
- entity zůstanou tak, jak byly (
se nepřepíše na mezeru a naopak), - pořadí a zápis atributů se nemění, včetně uvozovek,
- komentáře, podmíněné komentáře i podivné mezery přežijí,
- úprava jednoho odstavce nepřeformátuje zbytek dokumentu.
Jediná změna, která se dělá vždy, je převod CRLF na LF. Prohlížeč
ho udělá sám v textarea.value i při parsování HTML — slib, že se
zachová, by se nedal splnit. Pomocná funkce normalizeNewlines() je
veřejná, aby šlo porovnávat se stejným základem.
Obojí je dostupné samostatně — hodí se při dávkovém zpracování obsahu mimo editor.
CSS proměnné
Žádný generátor motivů, jen proměnné. Sedí na :root, protože dialogy
se vkládají do <body> — mimo obal editoru — a jinak by je
nezdědily.
| Proměnná | Světlá | K čemu je |
|---|---|---|
--nb-bg | #ffffff | Pozadí lišty, dialogů a plochy. |
--nb-fg | #16191c | Základní barva textu. |
--nb-muted | #6b7280 | Popisky, nápovědy, stavový řádek. |
--nb-border | #dcdfe3 | Rámečky a oddělovače. |
--nb-hover | #f1f3f5 | Podbarvení pod myší. |
--nb-active | #e3ecfb | Zapnuté tlačítko. |
--nb-accent | #1f5f5b | Přízvučná barva, fokus, hlavní tlačítko. |
--nb-radius | 6px | Zaoblení rohů. |
--nb-selection | průsvitná | Označený text ve zdrojovém kódu. Musí zůstat průsvitná, jinak zmizí obarvená syntaxe. |
Tmavý režim se řídí prefers-color-scheme a přepisuje stejné
proměnné. Kdo chce vlastní vzhled, přebije je ve svém CSS.
Klávesové zkratky
| Zkratka | Akce |
|---|---|
| Ctrl+B / I / U | Tučně, kurzíva, podtržení. |
| Ctrl+Z | Zpět. |
| Ctrl+Shift+Z | Znovu. |
| Ctrl+K | Vložit nebo upravit odkaz. |
| Ctrl+F | Hledat a nahradit. |
| F11 | Celá obrazovka. |
| Tab / Shift+Tab | V seznamu zanoří a vysune položku, v tabulce přejde na další buňku. |
| Enter / Shift+Enter | Nový blok / zalomení řádku. V nadpisu rozbalovacího bloku skočí do obsahu. |
| Ctrl+Shift+V | Vložit jako čistý text — i adresu, ze které by jinak byl odkaz. |
| / na začátku řádku | Lomítková nabídka (slash). Psaní filtruje, šipky vybírají, Enter / Tab potvrdí, Esc zavře a text nechá. |
| klik do zaškrtávátka | Přepne úkol (tasks). Enter na zaškrtnutém úkolu založí nový nezaškrtnutý. |
| Esc při tažení bloku | Zruší přesun (dragHandle). |
Markdown při psaní
S pluginem markdownShortcuts. Blokové zkratky platí, jen když je vzor
celý začátek odstavce:
| Napíšu | Spouští | Výsledek |
|---|---|---|
#–###### | mezera | nadpis h1–h6 |
-, * | mezera | odrážkový seznam |
1. (libovolné číslo) | mezera | číslovaný seznam; číslo ≠ 1 nastaví start |
> | mezera | citace |
[ ], [x] | mezera | úkol nezaškrtnutý / zaškrtnutý (jen s tasks) |
--- | Enter | <hr> a pod ním nový odstavec |
``` | Enter | <pre> |
Řádkové se spouštějí zavírací značkou: **x** tučně,
*x* a _x_ kurzíva, `x` kód,
~~x~~ přeškrtnutí. Obsah nesmí začínat ani končit mezerou
(2 * 3 * 4 je násobení) a _ uprostřed slova nic
nespustí. V kódu a v odkazech se zkratky nespouštějí. První Ctrl+Z
převod vrátí a nechá napsaný text.
Zkratky pluginů se objevují i v nabídkovém pruhu vedle názvu položky, takže se dají odkoukat bez čtení dokumentace.