`.
+Sweep tuhle únikovou cestu nemá a dostane dvě rány: `x-on:click="toggle(key)"` na
+tlačítku (`:952`) a `@click="onPointer('click', $event)"` na tbody
+(`record-actions.js:396`).
+
+```js
+// jednou v init(), na selection rootu — capture fáze běží před target fází,
+// takže stopPropagation zabije i listener navěšený přímo na tlačítku
+this.$el.addEventListener('click', (e) => {
+ if (! this.suppressClick) return
+ this.suppressClick = false
+ e.stopPropagation()
+ e.preventDefault()
+}, true)
+```
+
+Pointer capture tohle neřeší — pokrývá pointer stream, ne kompatibilní `click`.
+Použít ho jde (v `try/catch`, jako `controller.js:224-228`), ale jako enhancement.
+Pojistka pro `pointerup` mimo dokument: `setTimeout(() => suppressClick = false, 0)`
+ve `stopSweep()`; `click` se dispatchuje synchronně, takže se stihne dřív.
+
+**Dotyk mimo.** Tři guardy na `pointerdown`: `pointerType !== 'mouse'`,
+`button !== 0`, `! isPrimary`. A hlavně **`touch-action` neměnit** — fillí
+`touch-action: none` by na mobilu zablokovalo vertikální scroll v celém
+checkboxovém sloupci. Protože sweep dotyk vůbec nezpracuje, není co nastavovat.
+
+### 6.3 Sortable nekoliduje, ale nechává past
+
+Ověřeno ze zdroje (`packages/sortable/resources/views/partials/scripts.blade.php`):
+řádkový Sortable má `handle: '.wire-sortable-handle'` (`:88`), **`filter` ani
+`draggable` nastavené nejsou**, takže gating dělá výhradně handle — pointerdown
+v checkboxové buňce instanci nespustí. Dvojitá pojistka: instancuje se jen
+v reorder módu (`:70-71`, entanglované `isReordering`).
+
+**Past:** `addRowDragHandles()` (`:206-215`) **prependuje** `` do každého
+řádku z JS po renderu → checkboxový sloupec se posune z indexu 0 na 1. Sweep
+proto nikdy nesmí hledat buňku podle pozice (`cells[0]`, `:first-child`,
+`nth-child`), výhradně přes `[data-select-cell]`.
+
+### 6.4 Mobilní karty
+
+Nic navíc. Karty nemají checkboxový sloupec (je to `` + ` `
+v hlavičce karty, `:1198-1206`), `wireRecordActions` na nich vůbec není
+(komentář `:1180-1184`). Zároveň desktopová tabulka **zůstává v DOMu** i pod
+breakpointem (`$tableHiddenClass`, jen `display:none`), takže listenery existují
+dál a `rowAtY()` by se choval nedefinovaně — guard `pointerType === 'mouse'` to
+řeší úplně, na mobilu žádný mouse pointer nepřijde.
+
+---
+
+## 7. Etapa 5 — nápověda `?`
+
+### 7.1 Rozdělit legendu na tři vrstvy
+
+Plán má jednu třídu `table/src/Support/ShortcutLegend.php`. Podle
+`CLAUDE.md` §Architectural Invariants jsou to tři různá vlastnictví:
+
+| Kam | Co | Proč |
+|---|---|---|
+| `core/Foundation/Support/ShortcutLabelFormatter.php` | formátování `mod+d` → `⌘D` / `Ctrl+D` | dnes je to **`protected` metoda v traitě** (`HasKeyboardShortcut.php:133`) — business logika v traitě, a nedostupná komukoli mimo objekty s tou traitou; legenda přitom popisuje i `Shift`+`↑`, což žádná `Action` není |
+| `core/Foundation/ValueObjects/ShortcutHint.php` | řádek přehledu (`keys`, `label`, `group`) | není to nic table-specifického — command palette, wizard i forms to budou chtít stejně |
+| `table/src/Support/TableShortcutLegend.php` | **co** za gesta tabulka má | table-only, plán to sám říká ve `scope:` |
+
+Věcná chyba, kterou to odhalilo: `formatShortcutLabel()` mapuje `'mod' => 'Ctrl'`
+natvrdo (`HasKeyboardShortcut.php:140`), což odporuje §2 plánu. Server platformu
+nezná → label musí být buď platform-neutral, nebo se `mod` nechá jako token
+a přeloží se na klientovi.
+
+Legenda je **data, ne markup** — žádný `render()`/`toHtml()`, ten vlastní modal.
+`RecordActionResolver::shortcuts()` (`:143`) vrací jen `klávesa → jméno`, bez
+labelů, takže mu přibude bohatší metoda; resolver už `Action` instance drží
+(`instancesFor()`, `:174`) a legenda by je jinak resolvovala podruhé.
+
+### 7.2 Modal
+
+Konvence platí a je doložená (`AI_CODING_STANDARD.md:360-397`, reálná použití
+`{{ new … }}` v `modal-host.blade.php:65,92,113`, `action-modal.blade.php:53,79,101`,
+`select-option-modals.blade.php:26,41`). Správný tvar je
+`Modals\Html\Modal(heading:, bodyView: 'wire-table::tables.partials.shortcut-help', bodyData: [...])`.
+
+Bloker z §1.3 se řeší rozšířením kanonického shellu o event-driven otevření
+(`openOn:` → `x-data="{ show: false }" x-on:{event}.window="show = true"` jako
+alternativa k `@entangle`, když `$wireModel === null`). Je to změna v core, tedy
+seam první.
+
+Scope funguje: `action-modal` i `halt-modal` se includují na `index.blade.php:1386`
+a `:1389`, tedy uvnitř selection rootu (`:253`–`:1392`), a Alpine `x-teleport`
+zachovává scope místa deklarace.
+
+Pozor na zaznamenaný gotcha „stabilní show flag proti Alpine morph-reset" —
+ověřit CDP, že Livewire update s otevřenou nápovědou ji nezavře.
+
+### 7.3 Dvě pasti u klávesy
+
+- **`event.key === '?'`, nikdy `code`.** Na CZ klávesnici je to `Shift`+`,`;
+ matchování přes `code` by zkratku na neanglických rozloženích tiše zabilo.
+- **Zopakovat guard „fokus je na řádku".** Jinak otazník napsaný do vyhledávání,
+ inline-edit buňky nebo filtru otevře nápovědu místo toho, aby se napsal.
+
+---
+
+## 8. Etapa 6 — přístupnost
+
+Rozhodovací pravidlo, které v repu platí (a `index.blade.php:919-921` ho má
+napsané v komentáři): **binding potřebuje jen atribut, který mezi dvěma
+serverovými rendery mění JS.**
+
+| Atribut | Kde | Binding? |
+|---|---|---|
+| `aria-multiselectable="true"` | `:710`, uvnitř `@if($tableRole)` | **statický** — na holé `` bez `role="grid"` je to neplatné ARIA |
+| `aria-selected` na řádku | `:916-926` | **binding** — výběr žije jen v Alpine, statická hodnota by se po morphu vrátila na serverovou pravdu a smazala klik |
+| `aria-rowcount` | `:710` | statický |
+| `aria-rowindex` | `:714`, `:815` (hlavičkové řádky = 1, 2) a `:916-926` (tělo od `headerRowCount + 1`) | statický, **ale vyžaduje refaktor** |
+| `aria-live` region | `:314-315`, hned za otevřením selection rootu | **binding** |
+
+**Refaktor, který plán nezmiňuje:** `$from`/`$to`/`$total` se počítají až
+v patičce na `:1337-1341`, tedy po vykreslení tabulky. Pro `aria-rowindex` je
+nutné je vyzvednout do preambule k `$recordCount` (`:189`). A `$headerRowCount`
+musí odrážet, jestli se rendruje řádek sloupcových filtrů (`$hasColumnFilters`,
+`:142`) — jinak indexy o jedna ujedou, jakmile někdo zapne header filtry.
+
+**`aria-live` nesmí být v bulk baru** (`:615`), a to ze tří důvodů: bulk bar je
+pod `x-show="selectedCount > 0"` a **skrytý live region neoznamuje**; zmizí při
+nulovém výběru, takže „odznačeno vše" by se neohlásilo nikdy; a plurály jsou tam
+řešené třemi `x-show` spany, což by ve live regionu udělalo nesmyslné hlášení.
+Region musí být v DOM od začátku a prázdný, s jedním `x-text`.
+
+Mobilní karty `aria-selected` **nedostanou** — karta není `role="row"` a bez
+zavedení `role="listbox"`/`option` by to bylo neplatné. Mimo scope.
+
+---
+
+## 9. Testovací a verifikační strategie
+
+### 9.1 V repu není žádný JS test runner
+
+Ověřeno: `package.json` nemá vitest ani jest, žádné `*.test.js`/`*.spec.js`.
+A coverage gate (`scripts/verify-coverage.php:117`) počítá diff **jen z
+`packages/*/src/*.php`**. Tedy:
+
+| Co | V bráně? |
+|---|---|
+| `selection.js`, `record-actions.js` | **ne** |
+| `index.blade.php`, partialy | **ne** |
+| `Table.php`, `TableShortcutLegend`, `ShortcutHint`, `ShortcutLabelFormatter` | ano, 100 % změněných řádků |
+
+Největší část téhle práce tedy neprojde žádnou automatickou branou. **CDP driver
+není nice-to-have, je to primární testovací nástroj etap 2–4** a musí se pouštět
+po každé etapě.
+
+Zavádět vitest v rámci tohohle plánu nedoporučuju — `wireRecordSelection` závisí
+na `$wire.entangle` a Alpine reaktivitě, takže by šlo mockovat jen kostru
+a reálné bugy (morph reset, deferred commit) by to nechytlo. Za zvážení stojí
+jediné: napsat `pageStep`, `rowPitch`, `blockAround` a `snapshotBase` jako čisté
+funkce bez `this.$el` — pak by šly otestovat i bez prohlížeče, a je to jediná
+netriviální matematika v celé změně.
+
+### 9.2 Testy, které se rozbijí
+
+| Soubor | Co a kdy |
+|---|---|
+| `WithTablePerformanceTest.php:584-591` | hlídá `entangle('tableState.selection.records')` v HTML — po extrakci literál zmizí. **Etapa 1** |
+| `MobileControlsRenderTest.php:118-123` | doslovný tri-state ternár — rozbije se, jakmile se přesune do getteru |
+| `verify-record-active-row.mjs:231-233` | „`Shift`+šipka po select-all zmenší blok o jedna" — viz §1.2; s návrhem base-minus-block **projde beze změny**, s doslovným sjednocením spadne |
+| `verify-record-active-row.mjs:245` | `ctrl().anchorKey` → kotva se stěhuje; helper `window.sel` (`:70`) extrakci přežije |
+| `RecordActionRenderTest.php:130-149` | doslovné `bg-primary-100 dark:bg-primary-900` a `substr_count(':class="{')` — **etapa 6** mění class objekt nebarevným markerem |
+| `RecordActionTest.php:400-417` | `->toBe(['Delete' => 'remove'])` — rozbije se **jen** kdyby se `Backspace` řešil v PHP; další argument pro JS alias |
+
+Regresní guard, který se rozbít nesmí: `CanSelectRecordsTest.php` (524 ř.)
+definuje serverovou sémantiku, kterou JS kopíruje, a `MobileControlsRenderTest`
+hlídá tři konzumenty selection komponenty.
+
+### 9.3 CDP — co v repu chybí
+
+Vzor je `workbench/scripts/verify-record-active-row.mjs` (boot, `waitForDevtools`,
+raw WebSocket bez puppeteeru, `Emulation.setDeviceMetricsOverride` místo
+`--window-size`, `check()` + exit kód, cleanup ve `finally`).
+
+**Ale: `grep "modifiers" workbench/scripts/` vrací prázdno.** Ani jeden existující
+skript nepředává CDP modifikátory a **tažení myší v repu neexistuje vůbec**.
+K dopsání je tedy celá modifikátorová vrstva:
+
+```
+Alt = 1, Ctrl = 2, Meta/⌘ = 4, Shift = 8 // bitmaska, sčítá se
+modBit = isMac ? 4 : 2 // "mod" z plánu
+```
+
+- Klávesy přes `Input.dispatchKeyEvent` s `modifiers`, ne přes syntetický
+ `KeyboardEvent` — syntetický nespustí nativní chování, takže nedokáže ověřit,
+ že `preventDefault()` opravdu zabránil scrollu u `PageDown` ani že
+ `Shift`+`F10` neotevřel prohlížečové menu.
+- Tažení: `mousePressed` → N× `mouseMoved` **s `buttons: 1`** → `mouseReleased`.
+ Bez `buttons: 1` stránka vidí hover, ne drag; existující skripty posílají
+ `button: 'none'`, což je přesně opak.
+- Ověřit obě větve `mod` (⌘ i Ctrl), jinak se Mac/Win rozjede.
+
+### 9.4 Preview — 4 řádky nestačí
+
+`TablePreview::usersTable()` má `selectable()`, bulk akce, record actions
+i context menu, ale `paginated(false)` (`:511`) a **4 řádky**
+(`DatabaseSeeder.php` vytváří přesně čtyři uživatele).
+
+Nesouvislý výběr potřebuje ≥ 16 řádků, `PageUp`/`PageDown` ≥ 30 (jinak se skok
+clampne a je nerozlišitelný od `Home`/`End`), a „hranice stránky, ne datové sady"
+potřebuje paginovanou variantu.
+
+**Uživatele do seederu nepřidávat.** Workbench má persistentní SQLite, takže by
+to rozbilo `verify-mobile-selection.mjs:122` (natvrdo očekává 4 matching),
+`verify-record-active-row.mjs:260-264` a docs screenshoty. Správně je nový model
++ migrace + vlastní tabulka (~40 řádků) a dvě varianty
+(`selection-gestures`, `selection-gestures-paged`) v `TablePreview`.
+
+Provider měnit netřeba — `TablePreview` už je ve `WorkbenchServiceProvider`
+zaregistrovaná a nová *varianta* existující třídy 419 nezpůsobí.
+
+Pro koexistenci se sortable je potřeba `SortablePreview` doplnit `->selectable()`.
+
+### 9.5 DB matice není potřeba
+
+Změna je frontend + markup + in-memory PHP konfigurace, žádné nové SQL. Jediná
+výjimka: kdyby `aria-rowcount` sáhlo po celkovém počtu jinak než přes existující
+`$recordCount`, přibyl by count dotaz — pak DB matici spustit a ověřit
+`MobilePerformanceTest.php:190` a `CanSelectRecordsTest.php:407`, které přesně
+tenhle regres hlídají.
+
+---
+
+## 10. Do ADR 0024
+
+1. `mod`, nikdy doslovný `Ctrl` — a **proč**: `Ctrl`+klik je na Macu pravý klik,
+ `Ctrl`+šipka je Mission Control (systémová, nedá se `preventDefault`nout).
+2. `base ∪ rozsah` kde **base = snapshot mínus souvislý blok kolem kotvy** — a že
+ to je to, co smiřuje „nezahodit cizí výběr" se „zmenšit blok po `mod`+`A`".
+3. Rozsahová gesta se zapisují jako sjednocení do `selected`; `mode` se nikdy
+ nepřepisuje. **V `all` módu tedy rozsah odznačuje** a „souvislý blok" znamená
+ blok nevyloučených řádků.
+4. `mod`+`A` není rozsahové gesto → jeden `if` na mód je tam správně.
+5. Kotva je jednorázová a bez vizuálu; kdyby přibylo gesto, které ji potřebuje
+ napříč navigací, vrací se s ním i marker a slaďování s §4.
+6. Rozsah gest = jedna stránka.
+7. Sweep jen aditivní, jen v checkboxovém sloupci, jen myší; hledání buňky
+ výhradně přes `[data-select-cell]` (sortable prependuje ``).
+8. `Backspace` je platformní alias v JS, ne položka v PHP mapě zkratek.
+9. První cross-package JS import v repu (core → table).
+10. Grid semantika (`role="grid"`, tabindex, ARIA) patří `selectable()` tabulkám
+ stejně jako tabulkám s record actions.
+
+---
+
+## 11. Revidované pořadí etap
+
+| # | Etapa | Blokuje |
+|---|---|---|
+| **0** | Předpoklady — grid semantika, `matching`, `INTERACTIVE`, rezervované klávesy | 3, 5, 6 |
+| 1 | Extrakce `wireRecordSelection` (+ `entangle` past, dist, drift test) | 2, 4 |
+| 2 | Kotva a rozsahy + oprava `mode` v `selectRange`/`selectPage` | 3, 4 |
+| 3 | Klávesnice | — |
+| 4 | Sweep (+ promoce `autoscroll` a `rowAtY` do `core/support/`) | — |
+| **4b** | Seam v core: modal s event-driven otevřením | 5 |
+| 5 | Nápověda `?` (+ rozdělení legendy na tři vrstvy) | — |
+| 6 | Přístupnost (+ refaktor `$from` do preambule) | — |
+| 7 | Docs EN+CZ, boost guidelines, `boost:sync-docs` | — |
+
+Preview infrastruktura (§9.4) musí vzniknout před etapou 2 — bez ní není na čem
+nic z toho ověřit.
diff --git a/architecture/plans/table-selection-gestures-progress.md b/architecture/plans/table-selection-gestures-progress.md
new file mode 100644
index 00000000..ffd923e4
--- /dev/null
+++ b/architecture/plans/table-selection-gestures-progress.md
@@ -0,0 +1,208 @@
+---
+title: Rollout výběrových gest — stav provedení
+date: 2026-07-27
+plan: architecture/plans/table-selection-gestures-rollout.md
+status: HOTOVO — všech 28 kroků provedeno a zacommitováno
+---
+
+# Stav provedení rolloutu
+
+Jeden krok = jeden commit. Každý krok prošel bránou (composer test:table,
+Integration, analyse, lint, CDP drivery; coverage při zásahu do `src/*.php`).
+
+## Hotové kroky → commity
+
+| Krok | Commit | Poznámka |
+|---|---|---|
+| 0 | `f66181a` + `b2b4303` | WIP aktivního řádku + pint fixup (docblock core→forms bez importu) |
+| 1 | `2fd9ff7` | drift test `wire-table-records.js` |
+| 2 | `91f855f` | GestureRow (40 řádků), 3 preview varianty, `SortablePreview->selectable()` |
+| 3 | `eef2a68` + `4662ce5` | `verify-selection-gestures.mjs` (C1–C13) + oprava očekávání mobilního driveru |
+| 4 | `6d2d235` | `onKey()` rezervovanou klávesu vyhazuje (`TableConfigurationException`) |
+| 5 | `c0879a2` | rozšířený rezervovaný seznam (bez `backspace`), CHANGELOG |
+| 6 | `fb5b845` | `usesGridSemantics()` vlastní „je to grid“; mount přišpendlen přes `mountsRecordActionController()` |
+| 7 | `19eb53a` | mount controlleru na každém gridu; CHANGELOG (BC) |
+| 8 | `be28ee4` | `matching` z `data-matching` getterem (morph-safe) |
+| 9 | `b9fb825` | serverová normalizace `selection.mode` (jen korupce; validní tvary nechává) |
+| 10 | `b92f502` | `toggleAll()` v all módu neinvertuje |
+| 11 | `491efa2` | kanonika: stránkové gesto v all módu edituje výjimky, mód neopouští (otočeny 2 aserce v `CanSelectRecordsTest`) |
+| 12 | `1394504` | extrakce `wireRecordSelection` → `dist/wire-table-selection.js`; inline fallback ze zdrojáku |
+| 13 | `e194511` | `SelectionAssetTest` + `SelectionRenderTest` |
+| 14 | `8fde063` | kotva/rozsahy do selection komponenty; `data-selection-version="1"`; stale view → hlasitý console.error |
+| 15 | `be9e38a` | rozsahy nepřepisují `mode`; `selectPage` = union + guard v all módu (C9 otočeno) |
+| 16 | `5ccb981` | `base ∪ blok` se snapshotem; invalidace: setAnchor/clearAnchor pár, `$watch('mode')`, MutationObserver jen při zmizení kotvy |
+| 17 | `193444a` | `Shift`/`mod`/`mod`+`Shift` klik (additive flag v `selectRange`) |
+| 18 | `8cdffa6` | `Home`/`End`, `PageUp`/`PageDown` (pitch × skutečně scrollující předek), `mod`+`Shift`+šipky, fix `mod`+`A` shiftKey |
+| 19 | `e674790` | `Backspace` alias v JS matcheru, `Shift`+`F10` (+ `_menuFromKey` obrana, headless neověřitelná), fokus do menu a zpět |
+| 20 | `eb51ae4` | `data-select-cell` (td, karta, oba poziční spacery) |
+| 21 | `a71ef61` | `createAutoScroller` + `bodyRows`/`rowAtY` → `core/resources/js/support/`; core dist rebuild |
+| 22 | `351aeff` | sweep v `record-actions.js` (arm→engage, capture click-kill, jen myš, additive, morph guard, reduced-motion) |
+| 23 | `5efaab0` + `4aad86c` | `openOn:` ve 3 shellech + Htmlable objektech + View komponentách; preview `core-open-on`; `verify-modal-open-on.mjs` 14/14; hardening proti injekci do jména atributu |
+| 24 | `fd368fa` | `ShortcutLabelFormatter` + `ShortcutHint` (core Foundation) + `TableShortcutLegend` (table Support) + `Table::shortcutLegend()`; i18n EN+CS |
+| 25 | `c7f8f2a` | `?` otevírá nápovědu (`shortcut-help` + `shortcut-help-modal` partial, `kb.help` v controlleru); driver 62/62 |
+| 25b | `b217ca8` + `2c05921` | teleport `wire:key` z `$id` (dva Modal shelly v jedné komponentě); + předexistující díra ve forms select-option modalech |
+| 26 | `a20e61e` | ARIA grid (`aria-rowcount`/`aria-rowindex` přes celou sadu, `aria-multiselectable`, bindnuté `aria-selected`) + `aria-live` region; driver 70/70 |
+
+| 27 | `efce0ab` | klikatelná plocha = celá buňka, `[data-select-cell]` v `INTERACTIVE`, marker + pruh (kontrast 4.3/4.79 light, 3.98/7.95 dark); fix Shift+klik na checkbox; driver 77/77 |
+
+| 28 | `52d567d` + `340b865` + `e350d15` | docs EN+CZ (nová `selection.md` + revize `record-actions.md`), upgrade guide, boost guidelines + mirror; screenshoty; fixture fix stacked-selection |
+
+## Rozhodnutí učiněná při provádění (nad rámec plánu)
+
+- **Krok 6/7 split:** interim vlastník mountu `Table::mountsRecordActionController()`;
+ v kroku 7 rozšířen o `usesGridSemantics()` a zůstává jako trvalý vlastník.
+- **Krok 9:** records se maže JEN při korekci neznámého módu. Validní keys↔all
+ přechody jdou z klienta párově (mode+records) a wipe by zápis rozbil.
+- **Krok 11:** kanonika „stránkové gesto v all módu edituje výjimky a mód
+ neopouští“ — `selectAllRecords()`/`deselectPageRecords()` v all módu zůstávají
+ v all; zúžení na stránku zůstává jen explicitní `selectOnlyPageRecords()`.
+- **Krok 12:** `record-selection.js` je záměrně **bez importů** — asset partial
+ ho při chybějícím bundlu inlinuje doslovně (`SelectionAssetTest` to hlídá).
+- **Krok 22:** sweep proto žije v `record-actions.js` (bundluje se s importy
+ z core supportu), ne ve `wireRecordSelection`; listenery pointerdown/click-kill
+ jsou na selection rootu, move/up na dokumentu.
+- **Trailing click po sweepu:** flag musí přežít déle než `setTimeout(0)` —
+ click může přijít v pozdějším tasku; backstop je 150 ms, primárně one-shot
+ clear v capture handleru.
+- **Krok 23:** `openOn` se ctí JEN při `wireModel === null` (jediný vlastník
+ `show`); detekce „bez bindingu“ na component path musí jít přes
+ `WireDirective::value()` — chybějící `wire:model` vrací directive s value
+ `false` a `filled(false)` je `true`. `x-on:{event}` je pozice **jména
+ atributu**, kde Blade escapuje jen uvozovky → mezera by vložila nový atribut;
+ proto whitelist `[a-zA-Z][a-zA-Z0-9_-]*` (nález background security review).
+- **Krok 24:** `formatShortcutLabel()` v `HasKeyboardShortcut` deleguje na
+ `ShortcutLabelFormatter` — mění to i label akcí v `dropdown-item.blade.php`
+ a `header-action.blade.php` (šipky nově glyfy). `Foundation/ValueObjects/`
+ byl nový adresář, ale `AI_CODING_STANDARD.md:165` ho předepisuje.
+- **Krok 25:** jméno `openOn` eventu je `wire-table-shortcut-help-` + prvních
+ 12 znaků `md5($component->getId())`. Hash **musí** být lowercase: listener
+ sedí v **jménu atributu** (`x-on:{event}.window`) a DOM jména atributů
+ lowercasuje → syrové Livewire ID s velkými písmeny by se nikdy netrefilo
+ (CDP to odhalilo: modal v DOM, `show` zůstal false). Per-komponentní jméno
+ je nutné, aby `?` na stránce s více tabulkami neotevřel všechny nápovědy.
+ Mac/non-Mac labely: server rendruje Ctrl variantu, `x-text` ji na Macu
+ přepíše (platforma je klientský fakt) — headless Chrome na macOS hlásí Mac,
+ takže driver musí očekávat obě sady.
+- **Krok 25 → oprava `b217ca8`:** shelly měly `wire:key` teleportu natvrdo
+ (`wire-modal-modal`). Nápověda to porušila: tabulka s formulářovou akcí
+ rendruje **dva Modal shelly v jedné Livewire komponentě** a morph klíčuje
+ podle `wire:key` → záměna obsahu. Klíč teď bere `$id`, když je zadané
+ (bez `$id` beze změny); nápověda posílá jako `id` jméno svého eventu.
+
+- **Krok 28:** `boost:sync-docs` zrcadlí `docs/`, ale **ne** guidelines — ty se
+ editují ručně a mirror se pak přesyncuje. Screenshoty: workbench seed generuje
+ časy relativně k dnešku, takže každý `docs:refresh` změní datum na většině
+ snímků (šum, ne regrese) — a `workbench:build` reseedem posunul pořadí na
+ první stránce, což shodilo 2 checky `verify-mobile-selection.mjs`; fixture
+ proto dostal `defaultSort('id')`.
+- **Krok 26:** `aria-rowindex` je pozice v CELÉ sadě, ne na stránce → nutné
+ vyzvednout `$from`/`$to`/`$total` z patičky do preambule (patička se rendruje
+ až po těle) a `$headerRowCount` musí započítat řádek column filtrů.
+ `aria-selected` MUSÍ být binding (`:aria-selected`) — statická hodnota se
+ morphem vrátí na serverovou pravdu. Live region: v DOM od prvního renderu
+ a **prázdný** (region oznamuje jen ZMĚNY obsahu; naplněný při bootu neřekne
+ nic a při bootu s předvybranými řádky by četl výběr, který uživatel neudělal)
+ → `announceReady` se zapíná až prvním `$watch('selected'/'mode')`. Hlášky
+ chodí hotové z PHP (překlad je serverová věc), čísla se dosazují v JS.
+
+- **Krok 27:** marker je `::before` overlay na `[&>td:first-of-type]`, NE border.
+ Důvody: (a) border posouvá obsah a rezervace transparentním borderem nejde —
+ `border-transparent` je v CSS ZA `border-primary-600`, takže by vždy vyhrála
+ bez ohledu na pořadí ve `class`; (b) overlay nastavuje vlastnost, kterou
+ klidové řádky vůbec nenastavují → žádný souboj v kaskádě. **`first-of-type`,
+ ne `first-child`** — prvním dítětem ` ` je teleport ``
+ kontextového menu (jinak selektor nesedí na nic a vypadá to jako chyba
+ Tailwindu). Klikatelná plocha: handler JEN na ``, button vlastní nemá
+ (klik i Enter/Space z něj bublají) — dva handlery = dvojí toggle, a `.stop`
+ na buttonu by zabil nastavení kotvy v controlleru.
+
+## Objevené gotchas (platí i pro další kroky)
+
+- Blade `x-data` atribut: dvojité uvozovky v JS komentáři ukousnou atribut
+ (Alpine Expression Error) — komentáře v x-data bez `"`.
+- `overflow-x-auto` wrapper má computed `overflow-y: auto` (CSS páruje osy) —
+ „scrollující předek“ musí splnit i `scrollHeight > clientHeight`.
+- Mid-sweep se objeví bulk bar a posune layout → CDP drag musí přeměřovat
+ waypointy (driver `drag()` to dělá).
+- Perf testy v table sadě umí flaknout pod zátěží stroje (1× za rollout);
+ composer `process-timeout` 300 s zabije pest na vytíženém stroji — spustit
+ `vendor/bin/pest --configuration packages/core/phpunit.xml` přímo.
+- `verify-mobile-selection.mjs`: stacked-selection preview má předseedovaný
+ výběr → první tap na strip odznačuje (spravené očekávání, 13/13).
+- CDP checky počítající prvky (backdropy, markery) musí filtrovat na
+ **vykreslené**, ne na přítomné v DOM: zavřený modal si backdrop v dokumentu
+ nechává (`display:none`). `verify-nested-modal.mjs` na tom spadl, jakmile na
+ stránku přibyla nápověda (`painted=1 of 2`).
+
+## Gesture lab — ruční i automatické ověření
+
+`/previews/gesture-lab` (+ `-paged`) je jediná tabulka se **vším** zapnutým:
+výběrová gesta, record actions (click/dblclick/onKey/contextmenu), přeřazování
+sloupců, filtr sloupce, `?` nápověda, mobilní karty. Vedle tabulky je živý panel
+(`data-testid="lab-*"`) se stavem, který gesta řídí — mode, výběr, kotva, base,
+aktivní řádek, poslední hlášení live regionu a pořadí sloupců.
+
+Jednotlivé drivery izolují jednu vlastnost; `verify-gesture-lab.mjs` (23/23) jde
+naopak po **švech mezi nimi**: sweep → klávesnicový rozsah nad jedním výběrem,
+akce nad označeným řádkem, přeřazení sloupce s aktivním výběrem (checkbox se
+nesmí hnout, výběr přežít), nápověda vypisující akce této tabulky, `all` mód
+a modifikátorové kliky. Driver na konci volá `resetColumnOrder()` — pořadí
+sloupců se persistuje per user, takže bez úklidu by druhý běh startoval z cizího
+stavu.
+
+**Gotchas z labu:** panel nesmí číst live region/hlavičku getterem (plain DOM
+není reaktivní závislost → getter se přepočítá jen náhodou; nutný
+`MutationObserver`). Lab má filtrovatelný sloupec, takže má **dva** hlavičkové
+řádky → `aria-rowcount` je 42, tělo začíná na indexu 3. Nápověda uvádí kontextové
+menu jako jedno gesto, ne jednotlivé akce v něm.
+
+## Stav sítě
+
+- `verify-selection-gestures.mjs` — **77/77** (C1–C13 + myš, klávesnice, sweep,
+ selection-only, reduced-motion, sortable koexistence, `?` nápověda, ARIA +
+ live region, kontrast markeru, velikost cíle)
+- `verify-nested-modal.mjs` 8/8, `verify-confirmation-object.mjs` 8/8,
+ `verify-modal-layering.mjs` 13/13 (modalové regrese po kroku 23/25)
+- `verify-gesture-lab.mjs` — **23/23** (integrace napříč vlastnostmi),
+ `verify-column-reorder.mjs` 12/12
+- `verify-record-active-row.mjs` 18/18, `verify-record-actions.mjs` 14/14,
+ `verify-record-actions-dual.mjs` 5/5, `verify-mobile-selection.mjs` 13/13,
+ `verify-fill-handle.mjs` 26/26, `verify-modal-open-on.mjs` 14/14
+- PHP: celkem 4621 (table 1667, core 1751, forms 908, sortable 39), Integration 39;
+ analyse + lint OK; coverage diff 100 %, floors OK
+
+## Rollout dokončen
+
+Všech 28 kroků je hotových. Závěrečná brána (2026-07-27): `composer test`
+4621 prošlo / 2 přeskočeno, Integration 39, analyse + lint OK, coverage diff
+100 % a floors OK, **10 CDP driverů zelených** (selection-gestures 77/77,
+record-active-row 18/18, record-actions 14/14, record-actions-dual 5/5,
+mobile-selection 13/13, fill-handle 26/26, modal-open-on 14/14, nested-modal
+8/8, confirmation-object 8/8, modal-layering 13/13), `npm run docs:check` OK
+(228 souborů, obě lokalizace), `verify-api-docs` OK.
+
+### Dořešeno po rolloutu
+
+- `reorderBodyColumns()` — opraveno v `c21531a`. Skutečná příčina NEBYLA
+ selection buňka (ta je v hlavičce i těle symetricky), ale **teleport
+ ``** kontextového menu na začátku ``: první datový sloupec je
+ v hlavičce na indexu 2, ale v těle na 3, takže se přeřazení trefilo do
+ checkboxu a ten skončil mezi datovými sloupci. Nově se páruje podle
+ `data-column`, ne podle indexu; sub-rows a group headery se nechávají být.
+ Nový driver `verify-column-reorder.mjs` (12/12) + preview `sortable-columns`.
+ **Gotcha:** Livewire re-render po `reorderColumns` poškozený DOM přepíše,
+ takže bug je normálně vidět jen jako poskok — TRVALÝ zůstane tam, kde server
+ pořadí neuloží (nepřihlášený uživatel, chybějící migrace). Driver proto měří
+ i s odpojeným voláním Livewiru, jinak by regresi neodhalil.
+
+- **Fokus kolem modalu** — opraveno v `8c28d2d`. Modal shelly fokus vůbec
+ neřešily: zůstal na řádku ZA dialogem, takže Tab procházel stránku za modalem
+ (dialog nešel ovládat klávesnicí) a po zavření zůstal fokus na checkboxu →
+ šipky mrtvé (naměřeno ArrowDown 6 → 6), dokud uživatel neklikl na řádek. Nový
+ sdílený partial `modals/partials/focus-trap.blade.php` — expression-only (bez
+ závislosti na JS bundlu) a **bez dvojitých uvozovek**, které by Alpine atribut
+ usekly. 4 regresní checky v gesture labu, ověřeno že bez opravy padají.
+
+- Coverage floor pro `table` zvednut 87 → 88 % (`bda75c5`). Rezerva je ~0,3 bodu
+ a floor gate je v CI **blokující**, přičemž CI měří pcov a lokál xdebug — při
+ poklesu pokrytí to spadne dřív než u ostatních balíčků.
diff --git a/architecture/plans/table-selection-gestures-rollout.md b/architecture/plans/table-selection-gestures-rollout.md
new file mode 100644
index 00000000..0ca3431f
--- /dev/null
+++ b/architecture/plans/table-selection-gestures-rollout.md
@@ -0,0 +1,702 @@
+---
+title: Implementační plán — výběr a klávesové zkratky tabulky
+date: 2026-07-26
+scope: packages/table, packages/core (2 seamy), packages/sortable (1 nález), workbench
+status: ověřeno čtyřmi nezávislými sondami, připraveno k provedení
+parent: architecture/plans/table-selection-gestures.md
+analysis: architecture/plans/table-selection-gestures-implementation.md
+---
+
+# Implementační plán
+
+Plán drží **co** (`table-selection-gestures.md`), analýza **proč tak**
+(`…-implementation.md`), tenhle dokument **v jakém pořadí, čím se to ověří a jak
+se to vrátí zpátky**.
+
+Všechna fakta níže jsou ověřená proti kódu. Kde něco ověřit nešlo, je to
+označené.
+
+## Tři pravidla, ze kterých je plán odvozený
+
+1. **Nejdřív síť, potom kód.** Coverage gate nevidí ani JS, ani Blade — diff
+ i floors filtrují `packages/*/src/*.php` (`scripts/verify-coverage.php:117`,
+ `:79`) a JS test runner v repu není. Pest na `index.blade.php` sáhne, ale vidí
+ z něj zhruba pětinu; `selection.js` neuvidí vůbec. Bez CDP driveru, který
+ popisuje výchozí chování, je refaktor slepý.
+2. **Nikdy nemíchat přesun se změnou chování.** Extrakce je vlastní fáze a její
+ kritérium je, že se nezmění žádná aserce popisující *chování*.
+3. **Každý krok končí zeleně a jde vydat.** Rollback je vždy revert jednoho
+ commitu.
+
+---
+
+## Přehled kroků
+
+| # | Krok | Blokuje | Riziko |
+|---|---|---|---|
+| 0 | Dokončit a zacommitovat rozpracovanou práci na aktivním řádku | vše | — |
+| 1 | Drift test na `wire-table-records.js` + 4. driver do brány | 3 | nízké |
+| 2 | Preview infrastruktura (3 varianty, 40 řádků) | 3, 7 | nulové |
+| 3 | CDP driver — charakterizace výchozího chování | 12 | nulové |
+| 4 | `onKey()` rezervovanou klávesu odmítne hlasitě | 5 | nízké |
+| 5 | Rozšířit rezervovaný seznam | 18 | BC |
+| 6 | Grid semantika — role, tabindex, ARIA | 18, 25, 26 | **BC, viditelné** |
+| 7 | Grid semantika — mount pointer controlleru | — | **BC, viditelné** |
+| 8 | `matching` přestane být zapečené | — | oprava chyby |
+| 9 | Serverová normalizace `selection.mode` | 15 | nízké |
+| 10 | Oprava `toggleAll()` v `all` módu | 12 | **oprava vážné chyby** |
+| 11 | Sjednotit sémantiku „vybrat stránku" | 15 | střední |
+| 12 | Extrakce `wireRecordSelection` | 14, 16 | **nejvyšší** |
+| 13 | Testy po extrakci | — | — |
+| 14 | Přesun kotvy + verzovací značka markupu | 15, 16 | střední |
+| 15 | `selectRange`/`selectPage` přestanou přepisovat `mode` | 16 | střední |
+| 16 | `base ∪ rozsah` | 17, 18 | střední |
+| 17 | Myš: `Shift`/`mod`/`mod`+`Shift` klik | — | nízké |
+| 18 | Klávesnice | — | nízké |
+| 19 | `Backspace`, `Shift`+`F10`, fokus v kontextovém menu | — | nízké |
+| 20 | `[data-select-cell]` do markupu | 22, 27 | nulové |
+| 21 | Promoce `autoscroll` + `rowAtY` do core `support/` | 22 | cross-package |
+| 22 | Sweep | — | vysoké |
+| 23 | Core seam: modal otevíratelný z JS | 25 | cross-package |
+| 24 | Legenda zkratek — tři vrstvy vlastnictví | 25 | nízké |
+| 25 | Nápověda `?` | — | nízké |
+| 26 | ARIA, `aria-live`, refaktor `$from` | — | střední |
+| 27 | Nebarevný marker, klikatelná plocha, `INTERACTIVE` | — | střední |
+| 28 | Docs, CHANGELOG, upgrade guide, i18n, boost, screenshoty | — | — |
+
+---
+
+## 0 — Dokončit a zacommitovat práci na aktivním řádku
+
+Tvrdý předpoklad, ne administrativa.
+
+Celý plán popisuje „výchozí" chování, jenže to v HEAD (`74da9c8`) neexistuje.
+Necommitnutými přírůstky jsou `anchorFor()` (`record-actions.js:227-247`), guard
+fokusu `:141`, `dialogOpen()` `:148`, `markActive`, `rowClass`, `rowTabindex`
+i pravidlo „klik na checkbox nastaví kotvu" (`onPointer:399-404`). V HEAD nastavuje
+`moveActive` kotvu natvrdo na výchozí řádek, takže `Shift`+`↓` po `mod`+`A` výběr
+**zhroutí z N na 2**, nezmenší ho o jedna.
+
+Untracked je i `workbench/scripts/verify-record-active-row.mjs` a odstavec
+`docs/table/record-actions.md:112-118`, který dokumentuje dnešní sémantiku.
+
+Baseline je zelený — 1606 table / 1704 core / 39 sortable testů, driver 18/18 —
+ale zelený je **rozpracovaný strom**.
+
+**Po commitu přepočítat souřadnice v tomto dokumentu i v analýze.**
+
+---
+
+## Fáze I — síť (kroky 1–3, žádná produkční změna)
+
+### 1 — Drift test a čtvrtý driver do brány
+
+Na `packages/table/dist/wire-table-records.js` neexistuje drift test
+(`grep ASSETS_PATH packages/table/tests/` je prázdný). Zapomenuté
+`npm run build:table-assets` nechá všechny PHP testy zelené a driver bude
+charakterizovat starý bundle. Doplnit podle `DropdownAssetTest.php:116-133`.
+
+`verify-record-actions.mjs` je **čtvrtý** CDP konzument selection komponenty —
+sahá na `Alpine.$data([data-selection-root]).selected` (`:157`, `:163`, `:170`)
+a `ctrl().anchorKey` (`:157`). Doplnit do brány.
+
+### 2 — Preview infrastruktura
+
+| Soubor | Co |
+|---|---|
+| `workbench/app/Models/GestureRow.php` | nový model (precedens: `Task`, `Invoice`, `InvoiceItem`) |
+| `workbench/database/migrations/*_create_gesture_rows_table.php` | migrace |
+| `workbench/database/seeders/DatabaseSeeder.php` | 40 řádků, **`users` nesahat** |
+| `workbench/app/Livewire/Previews/TablePreview.php` | nová větev v `table()` před `:136` + private `gestureTable()` |
+| `workbench/routes/web.php` | slugy do **anonymního `foreach` na `:288`** |
+
+Tři varianty: `selection-gestures` (40 řádků, `paginated(false)`),
+`selection-gestures-paged` (perPage 20) a **`selection-only`** — `selectable()`
+**bez** record actions. Bez třetí je ověření kroků 6 a 7 bezobsažné: jediná
+dnešní selectable preview má record actions taky, takže by check prošel, i kdyby
+se `isSelectable()` nikdy nezapojilo.
+
+`$screens` (`:23-264`) je **jen galerie**, routy neregistruje — důkaz:
+`table-paginated` je pouze ve druhé mapě a vrací 200. `WorkbenchServiceProvider`
+měnit netřeba, registruje třídy a varianta je mount argument.
+`SortablePreview` doplnit `->selectable()` (potřeba v kroku 22).
+
+Databáze je persistentní a `serve` ji nepřestavuje:
+
+```bash
+vendor/bin/testbench workbench:build
+```
+
+### 3 — CDP driver, charakterizace výchozího chování
+
+`workbench/scripts/verify-selection-gestures.mjs`, vzor
+`verify-record-active-row.mjs` (boot, `waitForDevtools`, raw WebSocket,
+`Emulation.setDeviceMetricsOverride` místo `--window-size`, `check()`, cleanup
+ve `finally`).
+
+Chybí v repu úplně: modifikátorová vrstva (`grep "modifiers" workbench/scripts/`
+je prázdný) a tažení myší.
+
+```js
+const MOD = { alt: 1, ctrl: 2, meta: 4, shift: 8 } // ověřeno, aditivní
+const modBit = isMac ? MOD.meta : MOD.ctrl
+// tažení: mousePressed → N× mouseMoved s buttons:1 → mouseReleased
+```
+
+Čtyři vlastnosti prostředí ověřené proti reálnému Chromu:
+
+- **Viewport připnout na `1400×1200`.** Rozteč řádku je 64,5 px, tabulka nemá
+ vertikální scroll kontejner, takže `pageStep` vychází z `window.innerHeight`
+ a při zděděném viewportu by byl napříč presety nedeterministický.
+- **40 řádků poprvé způsobí, že dokument scrolluje.** Žádný existující driver
+ s tím nepočítá — souřadnice měří jednou. `activate()` navíc volá `focus()` bez
+ `preventScroll`. Souřadnicové checky přeměřovat po každém pohybu.
+- **`mod`+klik nejde na macOS testovat jako `ctrl`+klik** — `ctrl`+klik je tam
+ sekundární klik a Chrome `click` spolkne úplně.
+- **`Shift`+`F10` v headless Chromu nativní `contextmenu` negeneruje.** Reálný
+ `Input.dispatchKeyEvent` vystřelí jen `keydown`. Viz krok 19.
+
+Sekce, které musí projít proti stavu po kroku 0:
+
+| # | Charakterizuje |
+|---|---|
+| C1 | klik na checkbox toggluje a nastaví kotvu |
+| C2 | `Shift`+`↓` dělá rozsah od kotvy |
+| C3 | `Shift`+`↓` po `mod`+`A` blok zmenší o jedna |
+| C4 | `mod`+`A` vybere stránku |
+| C5 | `Space` toggluje aktivní řádek |
+| C6 | šipka výběr nemění |
+| C7 | `Enter` / dvojklik spustí akci, `Delete` spustí `onKey` |
+| C8 | pravý klik otevře menu |
+| C9 | v `all` módu `Shift`+`↓` shodí výběr na stránku — **chyba, obrací se v 15** |
+| C10 | `toggleAll()` v `all` módu invertuje výběr — **chyba, obrací se v 10** |
+| C11 | mobilní karty: toggle, select-all pruh, počet |
+| C12 | bulk bar: počet a čtyři akce |
+| C13 | klávesy nefungují při fokusu uvnitř řádku (`record-actions.js:141`) |
+
+C3, C9 a C10 popisují chování, které se **záměrně** změní. Charakterizovat je teď
+znamená, že se aserce později obrátí vědomě.
+
+---
+
+## Fáze II — PHP předpoklady (kroky 4–11)
+
+### 4 — `onKey()` odmítne rezervovanou klávesu hlasitě
+
+`RecordActionResolver::shortcuts()` (`:145`) rezervované klávesy dnes **tiše
+zahazuje** — `$reserved = ['enter','return','space','']`. Krok 5 by tak byl tichá
+BC změna: aplikace s `->onKey('Home')` by o něj přišla bez jediného signálu.
+
+Nejdřív tedy výjimka nebo `trigger_error`, s testem.
+
+### 5 — Rozšířit rezervovaný seznam
+
+Přidat `arrowup`, `arrowdown`, `home`, `end`, `pageup`, `pagedown`,
+`contextmenu`, `f10`, `?`.
+
+**`backspace` do seznamu ne** — kolidovalo by s krokem 19, kde je platformním
+aliasem v JS; rezervace v PHP by `->onKey('Backspace')` navždy znemožnila.
+
+Do CHANGELOGu a upgrade guide.
+
+**Ověření:** `RecordActionTest.php:400-407`.
+
+### 6 — Grid semantika: role, tabindex, ARIA
+
+Bez tohohle jsou kroky 18, 25 a 26 na tabulce bez record actions inertní a ARIA
+neplatná: `role="grid"`, `role="row"`, tabindex i `@keydown` visí na
+`keyboardNavEnabled()` (`Table.php:1686`) = `recordActionKeyboard ?? hasRecordActions()`,
+a guard `record-actions.js:141` vyžaduje fokus přímo na ` `.
+
+- nový `Table::usesGridSemantics()` jako jediný vlastník rozhodnutí „je to grid"
+- `keyboardNavEnabled()` → `recordActionKeyboard ?? (hasRecordActions() || isSelectable())`
+
+**Netýká se to jen `->selectable()` tabulek.** `isSelectable()`
+(`Table.php:849-852`) je `selectable || ! empty($bulkActions)`, takže se zgriduje
+**každá tabulka s bulk akcemi**.
+
+**Ověření:**
+- `RecordActionRenderTest.php:110-114` zůstává zelený (`NoRecordActionComponent`
+ dědí `$selectable = false`, `CtxNoMenuComponent` také není selectable) —
+ ověřeno, žádný existující PHP test nezčervená
+- `RecordActionTest.php:430-435` — explicitní `recordActionKeyboard(false)` dál vyhrává
+- **nový render test pro `selectable()` bez record actions** — dnes žádný neexistuje
+- CDP na variantě `selection-only` z kroku 2
+
+### 7 — Grid semantika: mount pointer controlleru
+
+`keyboardNavEnabled()` krmí i `$recordActionsRootEnabled` (`index.blade.php:76`),
+takže krok 6 by jinak naráz přinesl celý `wireRecordActions` na ` ` —
+`@click`, `@dblclick`, `@focusin`, `record-actions-assets` bundle,
+`...rowClass(%key%)` v `:class` a `focus-visible:ring-*`.
+
+**Viditelná změna vzhledu u všech stávajících konzumentů:** řádky se stanou
+fokusovatelnými, klik jim sebere fokus (`activate()` → `rows[i].focus()`),
+dostanou marker `bg-primary-100 dark:bg-primary-900/30` a vypne se jim
+`hover:bg-*`, dokud jsou označené.
+
+Do CHANGELOGu a upgrade guide.
+
+### 8 — `matching` přestane být zapečené
+
+`index.blade.php:267` má `matching: {{ $recordCount }}` v `x-data`, ale root má
+`wire:key="table-wrapper"` (`:255`), takže ho Livewire morphuje a `x-data` se už
+nevyhodnotí. Projev: vyfiltruj na 7 řádků → „Vybrat všech 7" → bulk bar ukáže
+původní počet. Server počítá správně, rozchází se jen klient.
+
+Oprava vzorem, který v souboru už je (`pageKeys`, `:257` + getter `:269`):
+`data-matching` + getter.
+
+### 9 — Serverová normalizace `selection.mode`
+
+`TableStateSynthesizer::hydrate()` (`:76-77`) dělá `array_intersect_key` jen na
+top-level klíčích; vnořené hodnoty neprochází ničím a `set()` (`:107-110`) je
+syrový zápis.
+
+`mode: 'all'` s neprázdným `records` je **legitimní tvar** — přesně tak vypadají
+výjimky. Právě v tom je problém: o významu `records` rozhoduje sám `mode`, a nic
+je nesvazuje. Každá cesta, která přehodí `mode` na `'keys'`, zatímco `records`
+drží výjimky, výběr invertuje.
+
+`TableStateSchema` navíc nemá verzi a `selection.mode` nemá legacy alias
+(`legacyPropertyMap():96-126` mapuje jen `selectedRecords → selection.records`).
+
+Minimum: normalizovat mód (`in_array($mode, ['keys','all'], true) ?: 'keys'`)
+a při změně tvaru vyprázdnit `records`, serverově v `CanSelectRecords`.
+
+### 10 — Oprava `toggleAll()` v `all` módu
+
+`index.blade.php:281-291`: v `all` módu drží `selected` výjimky a `clear` je tam
+vždy `true`, takže **jeden klik na hlavičkový checkbox** nastaví `mode='keys'`
+a nechá v `selected` právě klíče, které uživatel odškrtl. Výběr se invertuje na
+opačnou množinu a bulk akce z toho čtou (`InteractsWithTableModals.php:30-53`).
+
+Server to dělá správně: `CanSelectRecords.php:76-78` a `:98-100`
+(`selectsAllMatching() ? [] : $records`).
+
+Musí se opravit **před** krokem 12, jinak se chyba beze změny přestěhuje do
+nového modulu.
+
+**Ověření:** obrátit C10.
+
+### 11 — Sjednotit sémantiku „vybrat stránku"
+
+V repu existují tři různé odpovědi:
+
+| Kde | Chování |
+|---|---|
+| blade `toggleAll()` | v `all` módu maže |
+| JS `selectPage()` (`record-actions.js:266-273`) | nahrazuje (`selected = [...pageKeys]`) |
+| PHP `selectAllRecords()` | podle `CanSelectRecordsTest:108` sjednocuje, podle `:263` z `all` módu zužuje na stránku |
+
+Rozhodnout jednu kanonickou, srovnat na ni všechna tři místa a doplnit PHP test,
+který ji **opravdu** připíná.
+
+Bez tohohle kroku je verifikační kritérium kroku 15 bezcenné: staré aserce
+v `CanSelectRecordsTest` jsou čistě PHP a zůstanou zelené bez ohledu na to, co se
+v JS stane — a dvě z nich chystanou změnu navíc přímo popírají.
+
+---
+
+## Fáze III — extrakce (kroky 12–14)
+
+### 12 — `wireRecordSelection`
+
+Nejrizikovější krok plánu: čtyři konzumenti a Pest z toho vidí pětinu.
+
+**Opatření, které to riziko srazí: `x-data` zůstane na tomtéž elementu.** Změna
+je doslova `x-data="{ …54 řádků… }"` → `x-data="wireRecordSelection({…})"`.
+Element, `data-selection-root`, `data-page-keys` i Alpine scope chain zůstávají,
+takže se žádný konzument nedotkne.
+
+**Tři závazná pravidla kvůli `entangle`** (ověřeno ve zdroji: `injectDataProviders`
+váže `this` factory na magic kontext s `$wire` — `livewire.esm.js:2937`,
+`:3647-3649`; `initInterceptors` běží jednou na `:3655`, jeden řádek před `init()`
+na `:3656`):
+
+```js
+// function, NE arrow — this v těle factory je magic kontext s $wire
+window.Alpine.data('wireRecordSelection', function (config = {}) {
+ return {
+ // MUSÍ být v návratovém literálu, ne v init(): tam by se uložil syrový
+ // interceptor objekt a výběr by tiše přestal fungovat ($wire je do
+ // objektu injektovaný zvlášť, takže by nic nespadlo)
+ selected: this.$wire.entangle(config.statePath + '.records'),
+ mode: this.$wire.entangle(config.statePath + '.mode'),
+ }
+})
+```
+
+Config předává PHP (`statePath`, `syncLive`, `commitDelay`), aby sémantika
+zůstala v PHP a testy měly na co assertovat.
+
+**Doručení:**
+
+- `package.json` → druhý esbuild příkaz s vlastním `--outfile`. **Ne `--outdir`** —
+ ověřeno spuštěním, emituje `record-actions.js` místo `wire-table-records.js`.
+- Route měnit netřeba, `{asset}` je volný parametr — **ale výstup se musí jmenovat
+ `wire-table-*.js`**, provider skládá cestu jako
+ `ASSETS_PATH.'/wire-table-'.basename($asset).'.js'`.
+- Nový partial `selection-assets.blade.php` (mtime cache-bust).
+- Include **uvnitř `@if($isSelectable)`, ne uvnitř ` `** — to se nerendruje
+ bez viditelných sloupců, ale výběr je aktivní i v kartách.
+- **`dist/wire-table-selection.js` commitnout.** Dist je verzovaný, CI ho
+ negeneruje.
+
+**Fallback na chybějící bundle je povinný.** Route vrací
+`abort_unless(is_file($file), 404)` a selection `x-data` sedí na **vnějším
+wrapperu** (`:253`), který obsahuje vyhledávání, filtry, dropdowny, bulk bar,
+stránkování, mobilní karty i hosty modalů (`:1386`, `:1389`). Jedna 404 tedy
+rozbije Alpine pro **celé UI tabulky**, a pro uživatele tiše: chyba v konzoli,
+trvale viditelný bulk bar, mrtvé checkboxy. Totéž platí pro `wireRecordActions`,
+který je taky odkaz na factory. Partial už `is_file()` volá kvůli cache-bustu —
+stejná kontrola může fallbacknout na inline `x-data`.
+
+### 13 — Testy po extrakci
+
+| Soubor | Změna |
+|---|---|
+| `WithTablePerformanceTest.php:589` | `entangle('tableState.selection.records')` → `wireRecordSelection(` + `statePath` v configu |
+| `WithTablePerformanceTest.php:588, :590, :591` | `data-page-keys=`, `x-show="selectedCount > 0"`, `not->toContain('wire:click="toggleRecordSelection')` |
+| `MobileControlsRenderTest.php:122` | tri-state ternár se stěhuje do getteru |
+| `MobileControlsRenderTest.php:110, :115` | `assertDontSee('table-card-select-all')` |
+| `RecordActionRenderTest.php:120`, **`:125`** | `assertSee`/`assertDontSee('data-selection-root')`; `:125` připíná umístění includu |
+| `RecordActionRenderTest.php:148` | `toContain('isSelected(')` — název metody musí zůstat |
+| nový `SelectionRenderTest.php` | komponenta právě jednou; bez `selectable()` ani komponenta ani bundle |
+| nový drift test | bundle obsahuje `wireRecordSelection` |
+
+**Kritérium fáze III:** celá síť z kroku 3 projde beze změny jediné aserce
+popisující **chování**. Změnit se smí jen aserce na doslovné stringy, které se
+přesunem nutně mění (`entangle(…)`, tri-state ternár) — to jsou artefakty
+přesunu. Cokoli jiného je signál k revertu, ne k úpravě testu.
+
+### 14 — Přesun kotvy a verzovací značka
+
+`anchorKey` je deklarovaný v `record-actions.js:57`, **ne** v blade `x-data`,
+který krok 12 stěhuje. Jeho přesun do `wireRecordSelection` je změna vlastnictví
+a stěhuje s sebou i `moveActive:199-210`, `anchorFor:234-247`,
+`selectRange:250-264`, `selectPage:266-273`, `onPointer:399-404` a větev `Space`
+`:177-181`. Proto vlastní krok, ne součást 12.
+
+Dotkne se `verify-record-active-row.mjs:245` a `verify-record-actions.mjs:157`.
+
+**Zároveň zavést `data-selection-version` do markupu.** `wire-table::views` je
+dokumentovaný publish tag (`docs/theming.md:126`): JS se veze s balíčkem
+a aktualizuje se, publikovaný Blade ne. Od tohohle kroku dál čte dodávaný
+`record-actions.js` stav, který poskytuje jen nová komponenta — a protože
+`[data-selection-root]` pořád existuje a pořád vrátí objekt, **nespadne nic, jen
+se rozsahy budou vybírat špatně.** `wireRecordSelection` musí starší nebo
+chybějící značku odmítnout hlasitě.
+
+---
+
+## Fáze IV — chování (kroky 15–19)
+
+### 15 — `mode` se přestane přepisovat
+
+- `selectRange` (`:261`) — zápis `mode` úplně pryč, místo něj sjednocení
+- `selectPage` (`:270`) — zápis `mode` pryč **a přidat `if (sel.selectsAll) return`**
+ (dnes tam žádný takový guard není, `selectsAll` se v `record-actions.js`
+ nevyskytuje). `mod`+`A` není rozsahové gesto: v `all` módu je vybráno všechno
+ a sjednocení `pageKeys` do `selected` (= výjimek) by stránku odznačilo.
+
+**Ověření:** obrátit C9 + nový PHP test z kroku 11.
+
+### 16 — `base ∪ rozsah`
+
+Snapshot je povinný: sjednocení je monotónní, takže z aktuálního `selected` se
+zmenšení rozsahu odvodit nedá.
+
+```
+base = selected \ blockAround(kotva)
+```
+
+Ověřeno na obou scénářích — po `mod`+`A` je blok celý výběr, base prázdná
+a zmenšování funguje; u výběru 2–6 a pak 8–12 je blok kolem kotvy `{8}`, base
+`{2..6}` a sjednocení dá `{2..6, 8..12}`.
+
+- faktorizovat smyčku z `anchorFor:241-245` do `blockAround(rows, idx)`
+- `snapshotBase(rows)` — **`[...sel.selected]`, ne reference** (entangle proxy by
+ se pod rukama změnila při prvním zápisu)
+- `baseSelection = null` všude, kde se nuluje `anchorKey`, **plus** `selectPage()`
+ a `MutationObserver` (`:65-71`) — tam **jen když kotevní řádek zmizel**, jinak
+ by se base s klíči z jiné stránky sjednotila zpátky a vzkřísila neviditelné řádky
+- **plus na `resetSelectionScope()`** (`WithTable.php:391-395`), které se volá při
+ každé změně state path kromě sortu a perPage; invalidace jen přes DOM nestačí
+- **`onRowFocus` do toho seznamu NESMÍ** — `activate()` volá `focus()`, což
+ vystřelí `focusin`, a čištění base by ji smazalo hned po nastavení
+
+### 17 — Myš
+
+`Shift`+klik, `mod`+klik (i mimo checkbox), `mod`+`Shift`+klik. Sdílí
+`blockAround` i `snapshotBase` s krokem 16.
+
+### 18 — Klávesnice
+
+Pořadí guardů v `onKeydown` je závazné (`:135` → `:141` → `:148` → `:151`).
+Nová klávesa před `:141` znamená, že `Backspace` v editované buňce spustí mazací
+akci; před `:148`, že `PageDown` hýbe markerem pod otevřeným modalem.
+
+- `mod`+`Shift`+`↑`/`↓` dovnitř existujících `case`ů
+- `Home`/`End`, `PageUp`/`PageDown` nové `case`y, všechny přes tentýž
+ `moveActive(rows, idx, target, event.shiftKey)`
+- `Shift`+`Home`/`End` vychází na identické indexy jako `mod`+`Shift`+šipka →
+ jedna implementace
+- `pageStep` z **rozteče**, ne z výšky řádku, s guardem
+ `if (! pitch || ! viewport) return 1` (`display:none` tabulka na mobilu vrací
+ nuly a `Math.floor(x/0)` = `Infinity` → throw v `activate()`)
+- **viewport hledat u nejbližšího scrollujícího předka, ne z `window`** — balíček
+ sám scroll kontejner nemá, ale tabulky se rendrují v modalech
+ (`modals/modal.blade.php:18` je `fixed inset-0 overflow-y-auto`, `:55` a `:115`
+ přidávají `overflow-y-auto` tělo při `maxHeight`) a konzument si tabulku může
+ obalit `max-h-96 overflow-y-auto`
+- `mod`+`PageUp`/`PageDown` nevázat (Chrome, přepínání panelů)
+- opravit `:155`, kde `mod`+`A` větev nekontroluje `shiftKey`
+
+### 19 — `Backspace`, `Shift`+`F10`, fokus v menu
+
+- `Backspace` jako ekvivalenční třída `['delete','backspace']` ve dvou průchodech
+ `matchShortcut` (přesná shoda vyhrává). **V JS, ne v PHP** — v PHP by rozbil
+ `RecordActionTest.php:406` a `:417` a v legendě by se vypsal jako samostatný
+ řádek místo „`Delete` / `⌫`".
+- `Shift`+`F10` **nemůže jít matcherem** — `kb.shortcuts` mapuje na jméno akce
+ a otevření menu není akce. Vlastní `case 'F10'` s `if (! event.shiftKey) return`
+ a fallthrough do `case 'ContextMenu'`.
+- **Headless Chrome neověří, jestli po `Shift`+`F10` přijde nativní `contextmenu`**
+ (vystřelí jen `keydown`), takže check by byl falešně zelený. Buď headed Chrome,
+ nebo obranu (flag `_menuFromKey` zahozený v `setTimeout(…, 0)`) napsat rovnou
+ a v kódu označit jako netestovanou.
+- **Fokus do kontextového menu** patří sem, ne až do fáze VII. `openMenuForRow`
+ (`:342-347`) fokus nepřesouvá a `dialogOpen()` (`:313-317`) panel nevidí, protože
+ je `role="menu"`, ne `role="dialog"` — šipky tedy hýbou markerem za otevřeným
+ menu. `Shift`+`F10` tu vadu zpřístupní klávesnicí.
+
+---
+
+## Fáze V — sweep (kroky 20–22)
+
+### 20 — `[data-select-cell]` do markupu
+
+Atribut **v repu neexistuje** a stojí na něm kroky 22 i 27. Vlastní malý krok bez
+chování: přidat na checkboxovou `` (`index.blade.php:948`), na mobilní
+protějšek **a na poziční placeholdery** v `summary-footer.blade.php:47-50`
+a `group-subtotal.blade.php:13-16`, jinak se sloupce rozjedou.
+
+### 21 — Promoce sdílených helperů do core
+
+`createAutoScroller` (`fill/autoscroll.js`) a `bodyRows`/`rowAtY`
+(`fill/grid.js:17-23`, `:100-115`) do `core/resources/js/support/`.
+
+Jsou bundlované do `wire-core-dropdown.js` přes `dropdown.js:3`, takže krok
+vyžaduje **`npm run build:core-assets` a commit core distu**.
+`DropdownAssetTest.php:116-133` zastaralý bundle nechytí — grepuje na stringy,
+které se přesunem mezi soubory nemění.
+
+### 22 — Sweep
+
+Precedens je `wireFillHandle`, ale ve třech bodech se odchyluje:
+
+- **Žádný `preventDefault()` na `pointerdown`** — zabilo by fokus na
+ ``. Dvoufázově **arm → engage**: pointerdown jen
+ zapamatuje řádek, teprve první `pointermove` měnící řádek gesto nastartuje.
+- **`click` po tažení zabít capture listenerem** na selection rootu. Ověřeno, že
+ se trailing `click` retargetuje na společného předka (`BODY`), takže listener
+ musí být na předkovi obou — `[data-selection-root]` jím je. Pointer capture
+ tohle neřeší. Pojistka `setTimeout(…, 0)` ve `stopSweep()`.
+- **Dotyk mimo:** `pointerType !== 'mouse'`, `button !== 0`, `! isPrimary`.
+ `touch-action` **neměnit** — fillí `touch-action: none` by zablokovalo scroll
+ v celém checkboxovém sloupci.
+
+Buňku hledat výhradně přes `[data-select-cell]` — sortable `addRowDragHandles()`
+(`:206-215`) prependuje `` a posune sloupec z indexu 0 na 1. Řádkový Sortable
+je gatovaný `handle: '.wire-sortable-handle'` a instancuje se jen v reorder módu,
+takže tažení z checkboxové buňky drag nespustí.
+
+Morph guard po vzoru `controller.js:38-50`, sdílený s fillem.
+
+Teleportované dropdowny opouštějí DOM podstrom selection rootu, takže na ně
+capture listener nedosáhne — pro sweep neškodné, ale nedá se na něj spoléhat
+u teleportovaných povrchů.
+
+**Známý přilehlý problém:** `reorderBodyColumns()`
+(`sortable/…/scripts.blade.php:319-334`) je poziční bez offsetu na selection
+buňku, takže přeřazování sloupců je u selectable+sortable tabulky **rozbité už
+dnes**. Krok 2 přidává `->selectable()` do `SortablePreview`, takže to vyplave
+jako zdánlivá regrese. Buď na to vyhradit čas, nebo vědomě odložit a poznamenat.
+
+**Ověření:** sweep 2→6 přidá; sweep mimo sloupec nevybere nic a
+`getSelection().toString()` je neprázdný (ověřeno, že nativní označování textu
+při tažení skutečně nastává); po `mouseReleased` žádný toggle navíc; koexistence
+na `SortablePreview`; `prefers-reduced-motion: reduce` → `transitionDuration === '0s'`.
+
+---
+
+## Fáze VI — nápověda (kroky 23–25)
+
+### 23 — Core seam: modal otevíratelný z JS
+
+Všechny tři shelly mají natvrdo `x-data="{ show: @entangle($modelBinding) }"`
+(`modals/modal.blade.php:13`, `confirmation:15`, `slide-over:13`).
+
+Rozšířit **kanonický shell**, ne udělat lokální variantu: volitelný `openOn:`
+→ `x-data="{ show: false }" x-on:{event}.window="show = true"`, když
+`$wireModel === null`. Seam se mění před downstream callery.
+
+`ModalHtmlObjectTest.php:45/53` a `ConfirmationObjectTest.php:52/97` assertují
+`wireModel:` config, ne doslovný `x-data` string, takže aditivní `openOn:` je
+bezpečný. Ověřit CDP, že Livewire update s otevřenou nápovědou ji nezavře.
+
+### 24 — Legenda zkratek
+
+Tři vlastnictví, ne jedna třída:
+
+| Kam | Co |
+|---|---|
+| `core/Foundation/Support/ShortcutLabelFormatter` | formátování `mod+d` → `⌘D`/`Ctrl+D`; dnes `protected` metoda v traitě (`HasKeyboardShortcut.php:133`) mapující `mod → 'Ctrl'` natvrdo, což odporuje §2 plánu |
+| `core/Foundation/ValueObjects/ShortcutHint` | řádek přehledu — chtějí ho i palette, wizard, forms |
+| `table/src/Support/TableShortcutLegend` | co za gesta tabulka má; table-only |
+
+Pozor: `HasKeyboardShortcut.php` existuje dvakrát — `core/src/Concerns/` je
+12řádkový deprecated `class_alias` shim, skutečná je `core/src/Actions/Concerns/`.
+
+Legenda je data, ne markup — žádný `toHtml()`.
+
+**Coverage:** nový soubor v `src/`, tedy 100 % změněných řádků. Test musí pokrýt
+prázdnou legendu, jen `selectable()`, jen record actions, promítnutí `onKey()`,
+lokalizaci a deduplikaci.
+
+### 25 — Nápověda `?`
+
+`Modals\Html\Modal(heading:, bodyView: 'wire-table::tables.partials.shortcut-help',
+bodyData: […])`.
+
+Dvě pasti: matchovat `event.key === '?'` (na CZ je to `Shift`+`,`, `code` by
+zkratku zabil) a zopakovat guard „fokus je na řádku", jinak otazník napsaný do
+vyhledávání otevře nápovědu.
+
+---
+
+## Fáze VII — přístupnost (kroky 26–27)
+
+### 26 — ARIA a `aria-live`
+
+| Atribut | Kde | Binding? |
+|---|---|---|
+| `aria-multiselectable` | `:710`, uvnitř `@if($tableRole)` | statický |
+| `aria-selected` | `:916-926` | **binding** — statická hodnota by se po morphu vrátila na serverovou pravdu |
+| `aria-rowcount` | `:710` | statický |
+| `aria-rowindex` | `:714`, `:815` (hlavičky = 1, 2), `:916-926` (tělo od `headerRowCount + 1`) | statický |
+| `aria-live` | `:314-315`, hned za otevřením selection rootu | **binding** |
+
+**Refaktor:** `$from`/`$to`/`$total` se počítají až v patičce (`:1337-1341`), pro
+`aria-rowindex` je nutné je vyzvednout do preambule; `$headerRowCount` musí
+odrážet `$hasColumnFilters` (`:142`), jinak indexy ujedou.
+
+**`aria-live` nesmí do bulk baru** (`:615`) — je pod `x-show`, a skrytý live
+region neoznamuje; navíc by zmizel při nulovém výběru, takže „odznačeno vše" by
+se neohlásilo nikdy. Region musí být v DOM od začátku a prázdný, s jedním
+`x-text`. Plurály nejdou řešit třemi `x-show` spany jako v bulk baru — potřebují
+hotové hlášky z PHP.
+
+Mobilní karty `aria-selected` nedostanou (nejsou `role="row"`).
+
+### 27 — Marker, klikatelná plocha, `INTERACTIVE`
+
+`[data-select-cell]` do `INTERACTIVE` (`record-actions.js:25-38`) musí jít **týmž
+commitem** jako zvětšení klikatelné plochy na celou buňku — jinak klik do
+paddingu propadne do `onPointer` a spustí record action.
+
+Doslovné třídy hlídá pět míst: `RecordActionRenderTest.php:130-149`,
+`RecordActionTest.php:454-472` (`getActiveRowConfig()` s hover variantami),
+`RecordActionTest.php:85-86` a `:326-365`, `TableTest.php:518-576`, a v CDP
+`verify-record-active-row.mjs:71` **i** `verify-record-actions.mjs:141`
+(obojí zadrátované `classList.contains('bg-primary-100')`).
+
+**CDP:** kontrast ≥ 3:1 z luminance, `MutationObserver` nad live regionem,
+klikatelná plocha ≥ 24×24.
+
+---
+
+## 28 — Dokumentace a doprovod
+
+- `docs/table/record-actions.md` + `docs/cs/…` — EN i CZ v synchronu
+- **`CHANGELOG.md`** — nové veřejné API, změna chování u každé selectable tabulky
+ (6, 7), obrácené chování v `all` módu (15), rezervované klávesy (5)
+- **`docs/upgrade.md` + `docs/cs/upgrade.md`** — kroky 5, 6, 7, 15 a seznam kroků,
+ po kterých je nutné přepublikovat view
+- **i18n** — `packages/table/lang/{en,cs}/messages.php`: klíče pro `?` modal
+ (krok 25) a hlášení live regionu (krok 26)
+- **`packages/boost/…/guidelines/wire-table.blade.php:95-105`** popisuje sémantiku
+ gest ručně; `boost:sync-docs` synchronizuje `docs/`, ne guidelines
+- **Screenshoty** — `capture-previews.mjs:32` snímá `table-selection`, `:39-40`
+ sortable; krok 2 přidává checkboxový sloupec do `SortablePreview`, krok 27 mění
+ marker. `npm run docs:refresh`.
+
+---
+
+## Konzumenti, na které je potřeba myslet průběžně
+
+| Co | Kde | Proč |
+|---|---|---|
+| `$colSpan` aritmetika | `index.blade.php:150` → `group-header:4`, `sub-rows:10,53`, `summary-footer:23,95` | počítá selection sloupec |
+| `$selectionSyncLive = $isSelectable && $hasSummaries` | `index.blade.php:116` | **na tabulce bez summary se výběr na server necommituje vůbec**; bulk akce hned po sweepu může jet na zastaralém stavu → flush před bulk akcí |
+| `queueCommit()` debounce 350 ms | `index.blade.php:307-311` | 30řádkový sweep queueuje commity uprostřed tažení |
+| `getSelectedRecordKeys()` vrací `[]` v `all` módu | `CanSelectRecords.php:235-240` | konzument, kterého nová gesta dostanou do `all` módu, dostane prázdné pole → upgrade note |
+| výběr není v query stringu | `WithTableQueryString.php` | „vybrat vše odpovídající → změnit filtr" výběr tiše zahodí |
+
+---
+
+## Brána po každém kroku
+
+```bash
+composer test:table
+vendor/bin/pest --configuration phpunit.xml --testsuite "Integration"
+composer analyse && composer lint
+npm run build:table-assets # kdykoli se sáhne na JS
+
+vendor/bin/testbench serve --host=127.0.0.1 --port=8085 &
+node workbench/scripts/verify-selection-gestures.mjs
+node workbench/scripts/verify-record-active-row.mjs # regrese
+node workbench/scripts/verify-record-actions.mjs # regrese
+node workbench/scripts/verify-record-actions-dual.mjs # regrese
+node workbench/scripts/verify-mobile-selection.mjs # regrese
+pkill -f "testbench serve"
+```
+
+Coverage jen když se sáhlo do `src/*.php`:
+
+```bash
+php -d memory_limit=-1 vendor/bin/pest --coverage-clover=build/clover.xml
+php scripts/verify-coverage.php build/clover.xml --diff=origin/1.x
+php scripts/verify-coverage.php build/clover.xml # floors
+```
+
+Navíc: krok 21 a 22 — `npm run build:core-assets`, commit `packages/core/dist/`,
+`composer test:core`, `composer test:sortable`,
+`node workbench/scripts/verify-fill-handle.mjs`. Krok 23 — `composer test:core`
+**i `composer test:forms`** (shelly konzumuje i wire-forms).
+
+DB matice nikde — žádné nové SQL. Jedinou výjimkou by byl `aria-rowcount`, kdyby
+sáhl po počtu jinak než přes existující `$recordCount`; pak ověřit
+`MobilePerformanceTest.php:190` a `CanSelectRecordsTest.php:407`.
+
+---
+
+## Rozhodnutí do ADR 0024
+
+1. `mod`, nikdy doslovný `Ctrl` — `Ctrl`+klik je na Macu pravý klik (a Chrome
+ `click` spolkne), `Ctrl`+šipka je Mission Control, systémová a nepotlačitelná.
+2. `base ∪ rozsah`, kde **base = snapshot mínus souvislý blok kolem kotvy**.
+3. Rozsahová gesta se zapisují jako sjednocení do `selected`; `mode` se nikdy
+ nepřepisuje. **V `all` módu tedy rozsah odznačuje** a „souvislý blok" znamená
+ blok nevyloučených řádků.
+4. `mod`+`A` není rozsahové gesto → guard na mód je tam správně.
+5. Kotva je jednorázová a bez vizuálu.
+6. Rozsah gest = jedna stránka.
+7. Sweep jen aditivní, jen v checkboxovém sloupci, jen myší; buňka výhradně přes
+ `[data-select-cell]`.
+8. `Backspace` je platformní alias v JS, ne položka v PHP mapě zkratek.
+9. Grid semantika patří `selectable()` tabulkám (a tím i tabulkám s bulk akcemi)
+ stejně jako tabulkám s record actions.
+10. Markup výběru je verzovaný a starší publikovaný view se odmítá hlasitě.
+11. Cross-package JS import core → table (a s ním povinnost rebuildovat oba disty).
diff --git a/architecture/plans/table-selection-gestures.md b/architecture/plans/table-selection-gestures.md
new file mode 100644
index 00000000..d3fe73e8
--- /dev/null
+++ b/architecture/plans/table-selection-gestures.md
@@ -0,0 +1,224 @@
+---
+title: Kontrakt výběru a klávesových zkratek tabulky (myš + klávesnice + přístupnost)
+date: 2026-07-25
+scope: packages/table (selection runtime, record actions, table view), packages/core (nic — gesta jsou table-only)
+status: draft — k editaci, neschváleno
+parent: architecture/plans/v2-master-plan.md
+related: docs/table/record-actions.md, packages/table/resources/js/record-actions.js
+implementation: architecture/plans/table-selection-gestures-implementation.md
+rollout: architecture/plans/table-selection-gestures-rollout.md
+---
+
+# Kontrakt výběru a klávesových zkratek tabulky
+
+Návrh k editaci. Sloupec **Rozhodnutí** je to jediné závazné — sloupce s Excelem,
+Explorerem/Finderem a APG jsou jen podklad, ať je vidět, kde se odchylujeme
+vědomě a kde bychom si vymýšleli.
+
+Legenda stavu: ✅ hotovo · ➕ doplnit · ✏️ změnit oproti dnešku · ❌ nedělat
+
+---
+
+## 1. Základní princip
+
+**Fokus ≠ výběr.** Aktivní řádek (kam míří klávesnice) je něco jiného než
+zaškrtnuté řádky (co půjde do hromadné akce).
+
+Excel to má opačně — kurzor *je* výběr, takže tam šipka výběr přepíše. To u nás
+nejde: máme checkboxy a bulk akce, a šipka, která smaže zaškrtané řádky, je
+ztráta dat z pohledu uživatele. Naše tabulka se proto chová jako **Excel trvale
+v režimu `Shift`+`F8` (add-mód)** — což je zároveň chování Exploreru s `Ctrl`.
+
+Z toho plyne zbytek: `F8` i `Shift`+`F8` nemají co přidat a vypadávají.
+
+| Pojem | Význam | Vizuál |
+|---|---|---|
+| Aktivní řádek | kam míří klávesnice; kotva rozsahu | `activeRowClass()`, výchozí `bg-primary-100` |
+| Kotva (anchor) | odkud roste `Shift` rozsah | bez vizuálu — žije jen po dobu držení `Shift`u, viz §5 |
+| Výběr | co půjde do bulk akce | checkbox + `bg-primary-50` |
+
+---
+
+## 2. Klávesnice
+
+| Zkratka | Excel | Explorer / Finder | WAI-ARIA APG | **Rozhodnutí** | Stav |
+|-------------------------------|---|---|---|-|-|
+| `↑` / `↓` | posun kurzoru, výběr = ta buňka | posun **výběru** | posun fokusu | **posun aktivního řádku, výběr netknutý** | ✅ |
+| `Shift` + `↑`/`↓` | roztažení bloku od kotvy (i zpětné zmenšení) | rozsah od kotvy | rozšíření výběru o řádek | **blok od kotvy = `base ∪ rozsah`** | ✏️ |
+| `mod` + `Shift` + `↑`/`↓` | roztažení bloku na okraj | Win: aditivní rozsah | `mod`+`Shift`+`Home`/`End` | **rozsah od kotvy na první / poslední řádek stránky** | ➕ |
+| `Space` | (píše mezeru) | toggle v checkbox listech | **toggle fokusované položky** | **toggle aktivního řádku** | ✅ |
+| `Shift` + `Space` | vybrat celý řádek | — | rozsah od poslední vybrané k fokusované | **nic** — viz §5, zamítnuto | ❌ |
+| `mod` + `A` | vybrat vše | vybrat vše | vybrat vše | **vybrat celou stránku** | ✅ |
+| `Home` / `End` | A1 / konec dat | první / poslední | první / poslední | **aktivní řádek na první / poslední řádek stránky, výběr netknutý** | ➕ |
+| `Shift` + `Home`/`End` | rozsah k okraji | rozsah k okraji | rozsah k okraji | **rozsah od kotvy k prvnímu / poslednímu** (totéž co `mod`+`Shift`+šipka) | ➕ |
+| `PageUp` / `PageDown` | stránka | stránka | autorem definováno | **posun aktivního řádku o viewport řádků, výběr netknutý** | ➕ |
+| `Shift` + `PageUp`/`PageDown` | rozsah o stránku | rozsah o stránku | — | **roztažení rozsahu od kotvy o tentýž skok** | ➕ |
+| `Enter` | potvrdit / dolů | otevřít položku | — | **primární record action** | ✅ |
+| `Delete` | smazat obsah | smazat | — | **`onKey('Delete')` akce** | ✅ |
+| `Backspace` | smazat obsah | — | — | **alias `Delete` (na Macu je `⌫` jediný Delete)** | ➕ |
+| Menu klávesa / `Shift` + `F10` | kontextové menu | kontextové menu | kontextové menu | **kontextové menu řádku** (`Shift`+`F10` je alias — Menu klávesu většina notebooků nemá) | ✅ / ➕ |
+| `Escape` | zrušit režim | — | — | **zavřít modal** (dnešní chování) | ✅ |
+| `?` | — | — | — | **otevřít nápovědu zkratek** (viz §4) | ➕ |
+
+`mod` = `Ctrl` na Windows / `⌘` na macOS. **Doslovný `Ctrl` v téhle sadě nepoužíváme
+nikde**, a to ze dvou nezávislých důvodů: `Ctrl`+klik je na Macu pravý klik, a
+`Ctrl`+`↑`/`↓` je tam Mission Control / App Exposé — systémová zkratka, kterou
+stránka vůbec nedostane, takže se nedá ani `preventDefault`nout.
+
+Rozsah roste vždy od kotvy a vždy se **drženým** `Shift`em — u šipek, u
+`Home`/`End`, u `PageUp`/`PageDown` i u kliku. `mod` sám o sobě znamená
+„jednotlivec / bez rozsahu", `mod`+`Shift` znamená „až na okraj". Že se `Shift`
+drží po celou dobu, není detail: díky tomu kotva nikdy nemusí přežít víc než
+jeden stisk (§5).
+
+**Rozsah gest je vždy jedna stránka.** „První" a „poslední" řádek znamená první a
+poslední řádek aktuální stránky, ne datové sady — stejně jako `mod`+`A` vybírá
+stránku a ne všechno, co filtr matchne. Přes hranici stránky vede jediná cesta,
+a tou je „Vybrat všech N odpovídajících" v bulk baru (`mode: 'all'`).
+
+Skok `PageUp`/`PageDown` je počet celých viditelných řádků mínus jeden (obvyklý
+překryv o řádek), minimálně 1 — ne pevná konstanta, aby skok odpovídal tomu, co
+uživatel právě vidí.
+
+### Nesouvislý výběr (1–4 a 8–15) čistě klávesnicí
+
+| Krok | Zkratky |
+|---|---|
+| vybrat 1–4 | `Space` na řádku 1, pak `Shift`+`↓` ×3 |
+| přejít na 8 bez ztráty výběru | `↓` ×4 (u nás plná šipka; v Exceli/Exploreru by to chtělo add-mód) |
+| přidat 8–15 | `Space` na řádku 8, pak `Shift`+`↓` ×7 |
+
+U delších bloků se nemusí krokovat: `Shift`+`PageDown` skočí o viewport a dá se
+přestřelit — `Shift`+`↑` blok zase zmenší, protože kotva se roztahováním nehýbe.
+
+---
+
+## 3. Myš
+
+| Gesto | Excel | Explorer / Finder | **Rozhodnutí** | Stav |
+|---|---|---|---|---|
+| klik na checkbox | — | — | **toggle řádku + kotva** | ✅ |
+| `Shift` + klik | blok od kotvy | blok od kotvy | **blok od kotvy (`base ∪ rozsah`)** | ➕ |
+| `mod` + klik | přidat/odebrat jednotlivce | přidat/odebrat jednotlivce | **toggle řádku + kotva, i mimo checkbox** | ➕ |
+| `mod` + `Shift` + klik | přidat další blok | přidat další blok | **blok čistě přidaný k výběru** | ➕ |
+| tažení | výběr rozsahu tahem | rámeček | **sweep jen přes checkboxový sloupec, jen přidává** | ➕ |
+| `mod` + tažení | přidat další rozsah | — | **sweep aditivně** | ➕ |
+| tažení mimo checkboxový sloupec | — | rámeček | **nic** — patří označování textu v buňkách | ❌ |
+| klik na řádek | — | vybere řádek | **jen označí (aktivní řádek), nevybírá** | ✅ |
+| dvojklik na řádek | — | otevře | **primární record action** | ✅ |
+| pravý klik / `Ctrl`+klik (Mac) | kontextové menu | kontextové menu | **kontextové menu řádku** | ✅ |
+
+---
+
+## 4. Přístupnost
+
+Tahle sekce je záměrně samostatná — zkratky bez ní nepomůžou nikomu, kdo tabulku
+neovládá myší a očima.
+
+| Téma | Stav dnes | **Rozhodnutí** |
+|---|---|---|
+| `role="grid"` + `role="row"` | ✅ (jen když jsou record actions) | ponechat |
+| `aria-multiselectable="true"` | ❌ chybí | **doplnit na tabulku, když je `selectable()`** |
+| `aria-selected` na řádku | ❌ chybí — výběr je čitelný jen z checkboxu | **doplnit** |
+| `aria-rowcount` / `aria-rowindex` | ❌ chybí — čtečka hlásí „3 z 10" u stránkované tabulky | **doplnit u paginace** |
+| Oznámení změny výběru | ❌ chybí | **`aria-live="polite"` region: „vybráno 5 z 240"** |
+| Viditelný fokus | ✅ `focus-visible` ring | ponechat |
+| Označení aktivního řádku | ⚠️ jen barva pozadí | **přidat nebarevný signál** (levý pruh / ring) — barva sama nesmí nést význam (WCAG 1.4.1) |
+| Kontrast označení | ⚠️ neověřeno | **ověřit ≥ 3:1 vůči sousednímu řádku** (WCAG 1.4.11) |
+| Nápověda zkratek | ⚠️ jen v preview legendě | **klávesa `?` otevře přehled zkratek** (objevitelnost) |
+| Dotyk / mobilní karty | sweep i klávesnice mimo | **ponechat mimo, ale checkboxy a bulk bar musí stačit samy** |
+| `prefers-reduced-motion` | ⚠️ neověřeno u sweepu | **žádná animace při sweepu** |
+| Cíl kliknutí u checkboxu | 16×16 px | **zvětšit klikatelnou plochu na celou buňku (≥ 24×24)** — WCAG 2.5.8 |
+
+---
+
+## 5. Rozhodnuté otázky
+
+Žádná otevřená nezůstala.
+
+**Kotva je jednorázová a bez vizuálu.** Plná šipka ji zahodí, jak to kód dělá dnes
+(`record-actions.js:206`). Všechna rozsahová gesta drží `Shift` po celou dobu, takže
+kotva nikdy nežije déle než jeden stisk a není co zobrazovat. Kdyby někdy přibylo
+gesto, které kotvu potřebuje napříč navigací, vrací se tím i povinnost dát jí
+nebarevný marker a sladit ho se signálem aktivního řádku z §4 — je to jeden balík,
+ne dvě nezávislá rozhodnutí.
+
+**Rozsahová gesta se zapisují jako sjednocení do `selected`, ne jako „vyber rozsah".**
+V `keys` módu tím rozsah přidává, v `all` módu (kde `selected` drží výjimky) tím
+vyřazuje. Stejný kód, žádný `if` na mód, a symetrie s `toggle()`, který se takhle
+chová už dneska. Varianta „přepnout na `keys`" je vyloučená: uživateli s vybranými
+240 záznamy napříč stránkami by `Shift`+klik na 15 řádků tiše shodil výběr na 15.
+
+**Sada zkratek nebude mít vypínač.** Bez `selectable()` se výběrová gesta nenaváží
+a klávesy chytá jen řádek s fokusem (`record-actions.js:141`), takže inputy, inline
+edit ani filtry v tabulce nekolidují. Na přemapování konkrétní klávesy je `onKey()`.
+Cíleně se dá vypínač doplnit později; odebrat ho kvůli BC už ne.
+
+**Sweep** jen přes checkboxový sloupec. Přes celý řádek rozbije označování textu
+v buňkách a koliduje se sortable handlem.
+
+**`PageUp`/`PageDown`** = počet celých viditelných řádků − 1, min. 1. Pevná
+konstanta by na krátké i na dlouhé tabulce byla jinak špatně.
+
+**`Home`/`End` a „okraj"** = hranice aktuální stránky, ne datové sady.
+
+### Zvážené a zamítnuté
+
+**`Shift`+`Space`** (rozsah od kotvy k aktivnímu řádku, dle APG listboxu) —
+zamítnuto. Uzavřel by blok bez držení `Shift`u během navigace, ale totéž svede
+`Shift`+`End`, `Shift`+`PageDown` i `Shift`+šipka: rozsah je nedestruktivní, dá se
+přestřelit a vrátit se, kotva se nehne. Jako jediné gesto by přitom vyžadoval
+kotvu přeživší běžnou navigaci, a tím i její vizuál — jedna zkratka pro pohodlí za
+tři položky práce. Excelí význam téže klávesy („vybrat celý řádek") je pro nás
+prázdný, protože řádek je u nás nejmenší jednotka.
+
+---
+
+## 5b. Nápověda zkratek (`?`)
+
+| Otázka | **Rozhodnutí** |
+|---|---|
+| Modal, nebo popover? | **Modal** — obsah je tabulka o dvou sloupcích a musí jít projít klávesnicí; popover se zavírá na blur, což je u nápovědy ke klávesnici sebevražda. Přes `Modals\*` jako Htmlable objekt, dle konvence repa. |
+| Kdo klávesu vlastní? | **`wireRecordSelection`**, ne `wireRecordActions`. Dnešní `onKeydown` visí na ` ` record actions, takže na tabulce bez record actions by `?` nefungovalo. Selection root existuje vždy, když je `selectable()`. |
+| Odkud obsah? | **Generovaný z PHP** (`getSelectionConfig()` + zkratky record actions), ne natvrdo napsaný seznam. Jinak nebude obsahovat `onKey()` akce, které si definuje aplikace, a rozejde se s realitou při první změně. |
+| Tabulka bez výběru i bez record actions | žádné zkratky → **žádná nápověda**, klávesa se neváže |
+
+---
+
+## 6. Dopad na implementaci
+
+| Soubor | Co |
+|---|---|
+| `packages/table/resources/js/selection.js` (nový) | `wireRecordSelection` — jediný vlastník výběru a všech gest (dnes ~55 řádků inline `x-data` v Blade, což porušuje Rendering pravidlo 1 a 4) |
+| `packages/table/resources/js/record-actions.js` | navigace a akce zůstávají, výběr deleguje; nové klávesy |
+| `views/tables/partials/selection-assets.blade.php` (nový) | `@assets` bundle, zrcadlí `record-actions-assets` |
+| `views/tables/index.blade.php` | `x-data="wireRecordSelection({…})"`, `data-select-cell`, sweep listenery na rootu, ARIA atributy, `aria-live` region |
+| `packages/table/src/Table.php` | `getSelectionConfig()` (PHP vlastní sémantiku, JS ji konzumuje) + fluent vypínače |
+| `packages/table/src/Support/ShortcutLegend.php` (nový) | složí seznam zkratek pro nápovědu z `getSelectionConfig()` + zkratek record actions — jeden zdroj pravdy pro `?` modal i pro docs |
+| `views/tables/partials/shortcut-help.blade.php` (nový) | obsah `?` modalu, konzumuje `ShortcutLegend` |
+| `package.json` | `build:table-assets` bundluje oba moduly |
+| `architecture/decisions/0024-table-selection-gestures.md` | ADR: `mod` a nikdy doslovný `Ctrl`, pravidlo `base ∪ rozsah` zapsané jako sjednocení do `selected` (a tím `all` mód zdarma), kotva jednorázová a bez vizuálu, rozsah gest = stránka, sweep jen aditivní a jen v checkboxovém sloupci |
+
+### Etapy (jde zastavit po každé)
+
+1. **Extrakce** `wireRecordSelection` beze změny chování, API 1:1 (konzumují ho i mobilní karty a bulk bar).
+2. **Kotva a rozsahy** — `base ∪ rozsah`, `Shift`/`mod`/`mod`+`Shift` klik. Součástí je oprava `selectRange()` (`record-actions.js:261`) a `selectPage()` (`:270`), které dnes natvrdo nastavují `mode = 'keys'` a tím v `all` módu shodí výběr z celé filtrované sady na stránku.
+3. **Klávesnice** — `Shift`+šipky na `base ∪ rozsah`, `mod`+`Shift`+šipky, `Home`/`End` + `Shift` varianta, `PageUp`/`PageDown` + `Shift` varianta, `Backspace`, `Shift`+`F10`.
+4. **Sweep** přes checkboxový sloupec.
+5. **Nápověda `?`** — `ShortcutLegend`, modal, klávesa na selection rootu.
+6. **Přístupnost** — ARIA, `aria-live`, nebarevné označení, kontrast, klikatelná plocha checkboxu.
+7. **Docs** EN+CZ + boost guidelines + `boost:sync-docs`.
+
+### Rizika
+
+- `index.blade.php` je hot file; selection komponentu konzumují **tři** místa (desktop řádky, mobilní karty, bulk bar) → extrakce musí být API-kompatibilní.
+- Kdo má publikovaný `wire-table::tables.index`, nová gesta nedostane, dokud view nepřepublikuje → do docs i CHANGELOGu.
+- `wire-sortable` přeřazuje řádky SortableJS instancí na ` `; má `handle: '.wire-sortable-handle'`, takže sweep z checkboxové buňky nekoliduje — **ověřit CDP testem nad sortable preview**.
+- `$wire.entangle` musí přežít přesun do modulu (deferred commit, `queueCommit`).
+- Sweep koliduje s označováním textu a s `click` po tažení → `preventDefault` + zahození následného kliku.
+
+### Verifikace
+
+- Pest: markup (komponenta, `data-select-cell`, ARIA), žádná regrese stávajících selection testů.
+- CDP (`workbench/scripts/verify-selection-gestures.mjs`): `Shift`/`mod`/`mod`+`Shift` klik, sweep, 1–4 + 8–15 klávesnicí, zmenšení bloku po `mod`+`A`, `Home`/`End`/`PageUp`/`PageDown`, mobilní karty, bulk bar, sortable koexistence.
+- `composer test:table` → integrační sada → `composer analyse` → `composer lint` → coverage gate.
diff --git a/architecture/table.md b/architecture/table.md
index f440d970..b8fdb743 100644
--- a/architecture/table.md
+++ b/architecture/table.md
@@ -41,6 +41,24 @@ That means state shape and hydration changes can have Livewire-wide effects for
Top-level fluent config object. Start here when the task is about table API shape.
+### `Concerns/HasGestures.php` + `Support/TableGestures.php`
+
+Canonical owner of "which desktop gestures does this table offer" — keyboard
+grid navigation, range selection, the drag sweep, the right-click menu, the `?`
+help, the fill handle. Every consumer asks here, never a local flag:
+`usesGridSemantics()`, `mountsRecordActionController()`, `usesDragSelect()`,
+`usesRangeSelection()`, `usesShortcutHelp()`, `usesActiveRowMarker()`,
+`hasRowContextMenu()`, `isFillHandleEnabled()`, and the client via
+`getGestureConfig()` / `getRecordActionKeyboardConfig()`.
+
+The layer is **opt-in**: `TableGestures::defaults()` leaves keyboard navigation
+and the drag sweep off, and `Table::gestures()` is what turns them on. Two more
+rules hold the design together: a capability is a **permission, not a trigger**
+(allowing the sweep does not make a table selectable), and an **explicitly
+declared record action is outside the layer** (`onClick()` survives
+`gestures(false)`; only `onKey()` needs the keyboard). Project-wide default in
+`config('wire-table.defaults.gestures')`.
+
### `Concerns/WithTable.php`
Primary Livewire trait and one of the highest-risk files in the repo.
diff --git a/docs-site/assets/previews/core-toasts.png b/docs-site/assets/previews/core-toasts.png
index b7bbe7e5..b7259a6f 100644
Binary files a/docs-site/assets/previews/core-toasts.png and b/docs-site/assets/previews/core-toasts.png differ
diff --git a/docs-site/assets/previews/field-date-time-picker.png b/docs-site/assets/previews/field-date-time-picker.png
index dbcef91e..a8083283 100644
Binary files a/docs-site/assets/previews/field-date-time-picker.png and b/docs-site/assets/previews/field-date-time-picker.png differ
diff --git a/docs-site/assets/previews/field-file-upload.png b/docs-site/assets/previews/field-file-upload.png
index 02146558..79a2c288 100644
Binary files a/docs-site/assets/previews/field-file-upload.png and b/docs-site/assets/previews/field-file-upload.png differ
diff --git a/docs-site/assets/previews/forms-repeater.png b/docs-site/assets/previews/forms-repeater.png
index 6adc63fa..f0386086 100644
Binary files a/docs-site/assets/previews/forms-repeater.png and b/docs-site/assets/previews/forms-repeater.png differ
diff --git a/docs-site/assets/previews/sortable-detail.png b/docs-site/assets/previews/sortable-detail.png
index a8a2f780..a597873b 100644
Binary files a/docs-site/assets/previews/sortable-detail.png and b/docs-site/assets/previews/sortable-detail.png differ
diff --git a/docs-site/assets/previews/sortable-overview.png b/docs-site/assets/previews/sortable-overview.png
index 5bf4f29e..3f636bac 100644
Binary files a/docs-site/assets/previews/sortable-overview.png and b/docs-site/assets/previews/sortable-overview.png differ
diff --git a/docs-site/assets/previews/table-actions-quiet.png b/docs-site/assets/previews/table-actions-quiet.png
index 25abc551..247a3967 100644
Binary files a/docs-site/assets/previews/table-actions-quiet.png and b/docs-site/assets/previews/table-actions-quiet.png differ
diff --git a/docs-site/assets/previews/table-image-gallery.png b/docs-site/assets/previews/table-image-gallery.png
index 697aef2d..2f2d6c84 100644
Binary files a/docs-site/assets/previews/table-image-gallery.png and b/docs-site/assets/previews/table-image-gallery.png differ
diff --git a/docs-site/assets/previews/table-overview.png b/docs-site/assets/previews/table-overview.png
index c9793060..5a878951 100644
Binary files a/docs-site/assets/previews/table-overview.png and b/docs-site/assets/previews/table-overview.png differ
diff --git a/docs-site/assets/previews/table-selection.png b/docs-site/assets/previews/table-selection.png
index 74dfabc1..948e1c82 100644
Binary files a/docs-site/assets/previews/table-selection.png and b/docs-site/assets/previews/table-selection.png differ
diff --git a/docs-site/assets/previews/table-subrows-filter.png b/docs-site/assets/previews/table-subrows-filter.png
index de9fbd98..793528f6 100644
Binary files a/docs-site/assets/previews/table-subrows-filter.png and b/docs-site/assets/previews/table-subrows-filter.png differ
diff --git a/docs-site/assets/previews/table-subrows-flatten.png b/docs-site/assets/previews/table-subrows-flatten.png
index 32ae4e2f..35faae83 100644
Binary files a/docs-site/assets/previews/table-subrows-flatten.png and b/docs-site/assets/previews/table-subrows-flatten.png differ
diff --git a/docs-site/assets/previews/table-subrows-limit.png b/docs-site/assets/previews/table-subrows-limit.png
index 3edd8661..50b3fa5c 100644
Binary files a/docs-site/assets/previews/table-subrows-limit.png and b/docs-site/assets/previews/table-subrows-limit.png differ
diff --git a/docs-site/assets/previews/table-subrows.png b/docs-site/assets/previews/table-subrows.png
index c6582da8..e8d90c88 100644
Binary files a/docs-site/assets/previews/table-subrows.png and b/docs-site/assets/previews/table-subrows.png differ
diff --git a/docs/cs/forms/fields/date-time-picker.md b/docs/cs/forms/fields/date-time-picker.md
index 13a15aee..ae634a81 100644
--- a/docs/cs/forms/fields/date-time-picker.md
+++ b/docs/cs/forms/fields/date-time-picker.md
@@ -40,6 +40,29 @@ DateTimePicker::make('start')
->closeOnDateSelection()
```
+Meze přijmou cokoli, co jde přečíst jako datum — `Carbon`/`DateTimeInterface`,
+nebo řetězec jako `'2026-07-10'`, `'10.07.2026'`, `'today'` či `'+1 week'` — a na
+pozadí se převedou do tvaru, kterému widget rozumí. Mez, kterou přečíst nelze,
+vyhodí výjimku, místo aby ji prohlížeč tiše zahodil.
+
+```php
+DateTimePicker::make('start')
+ ->minDate(now()) // žádná data v minulosti
+ ->maxDate(now()->addYear())
+```
+
+U režimu `datetime` může mez nést i čas — ten pak omezí hodiny jen v ten hraniční
+den:
+
+```php
+DateTimePicker::make('slot')
+ ->minDate('2026-07-10 08:30') // 10. července nejdřív od 08:30
+ ->maxDate('2026-07-20 17:00') // 20. července nejpozději do 17:00
+```
+
+Horní mez zadaná na celý den pokrývá celý den: `->maxDate('2026-07-20')`
+nechá 20. července volitelné až do 23:59.
+
## Volby času
```php
@@ -92,8 +115,8 @@ Jedinou výjimkou je [`asMonth()`](#rezimy), který je vždy nativní.
| `asDateTime()` | — | Alias pro `mode('datetime')` |
| `format(string)` | string | Formát uložení (Carbon kompatibilní) |
| `displayFormat(string)` | string | Formát zobrazení ukázaný uživateli |
-| `minDate(string\|Closure)` | string | Minimální volitelné datum |
-| `maxDate(string\|Closure)` | string | Maximální volitelné datum |
+| `minDate(string\|DateTimeInterface\|Closure)` | string | Nejdřívější volitelné datum; u `datetime` může nést i čas |
+| `maxDate(string\|DateTimeInterface\|Closure)` | string | Nejpozdější volitelné datum; mez na celý den pokrývá celý den |
| `disabledDates(array\|Closure)` | array | Data, která nelze vybrat |
| `firstDayOfWeek(int)` | int | 0=neděle, 1=pondělí |
| `closeOnDateSelection()` | bool | Zavřít picker po výběru data |
diff --git a/docs/cs/table/advanced.md b/docs/cs/table/advanced.md
index 9fc74ac3..c5212212 100644
--- a/docs/cs/table/advanced.md
+++ b/docs/cs/table/advanced.md
@@ -363,11 +363,26 @@ $table->lazy()
### Jak to funguje
-1. Stránka se vykreslí okamžitě s placeholder HTML
+1. Stránka se vykreslí okamžitě s placeholder HTML — a s Alpine bundly, které bude tabulka potřebovat
2. Livewire odešle async volání pro načtení obsahu tabulky
3. Placeholder je nahrazen plně vykreslenou tabulkou
4. Následné interakce (řazení, filtrování, stránkování) jsou normální Livewire volání
+Bod 1 není detail, který by se dal přejít. Bundly za dropdowny, výběrem řádků
+a record controllerem registrují své Alpine komponenty z listeneru na
+`alpine:init` — a ten proběhne přesně jednou, když Alpine nabootuje. Bundle,
+který by dorazil až s odloženým markupem, přijde po něm, přihlásí se k eventu,
+jenž už nikdy nenastane, a nezaregistruje nic; tabulka by pak naběhla s mrtvými
+dropdowny a s backdropy sheetů zaseknutými přes celou stránku. Proto je posílá
+už render **placeholderu** a markup, který ho nahradí, se inicializuje normálně.
+
+Které bundly se načtou, se řídí konfigurací tabulky: dropdown bundle vždy
+(toolbar je z dropdownů), selection bundle při `selectable()` a record
+controller vždy, když si ho tabulka vůbec montuje — pointer bindingy record
+akcí, kontextové menu řádku, gridová klávesová sémantika, drag-select nebo
+výběr rozsahu Shiftem. Vlastní `lazyPlaceholder()` mění jen viditelný
+skeleton — na to, co se načte, nemá vliv.
+
### Kdy použít
- Dashboardové stránky s více tabulkami — načtěte každou lazy
diff --git a/docs/cs/table/columns/editing.md b/docs/cs/table/columns/editing.md
index dbc60f40..fd0c8fc0 100644
--- a/docs/cs/table/columns/editing.md
+++ b/docs/cs/table/columns/editing.md
@@ -68,8 +68,8 @@ engine, chipy a query-string persistenci viz [Filtry na úrovni sloupce](../filt
->filterAsSelect(array|string $options, ?string $placeholder = null) // jedna hodnota; searchable combobox
->filterAsMultiSelect(array|string $options, ?string $placeholder = null) // více hodnot (whereIn); searchable combobox
->filterSearchable(bool $condition = true) // přepnutí vyhledávání (defaultně zapnuté)
-->filterAsDate(?string $minDate = null, ?string $maxDate = null)
-->filterAsDateRange(?string $minDate = null, ?string $maxDate = null)
+->filterAsDate(string|DateTimeInterface|null $minDate = null, string|DateTimeInterface|null $maxDate = null)
+->filterAsDateRange(string|DateTimeInterface|null $minDate = null, string|DateTimeInterface|null $maxDate = null)
->filterAsNumberRange(?float $min = null, ?float $max = null, ?float $step = null)
->filterAsBoolean(?string $trueLabel = null, ?string $falseLabel = null)
->filterOperator(string $operator) // '=', '!=', '>', '<', '>=', '<=', 'like' (výchozí, částečná shoda), 'starts_with', 'ends_with'
diff --git a/docs/cs/table/columns/fill-handle.md b/docs/cs/table/columns/fill-handle.md
index 67cc097c..aae5cef9 100644
--- a/docs/cs/table/columns/fill-handle.md
+++ b/docs/cs/table/columns/fill-handle.md
@@ -132,3 +132,5 @@ volání; nepoužívejte znovu ty, se kterými jste začali.
- [Editace a filtry na úrovni sloupce](editing.md) — jak funguje jedno inline uložení
- [TextInputColumn](text-input.md) · [SelectColumn](select.md) · [ToggleColumn](toggle.md)
+- [Vrstva gest](../gestures.md) — handle je jedna z jejích schopností;
+ `gestures(false)` ho zavře i s endpointem
diff --git a/docs/cs/table/filters/date.md b/docs/cs/table/filters/date.md
index f2a6b435..2f286e61 100644
--- a/docs/cs/table/filters/date.md
+++ b/docs/cs/table/filters/date.md
@@ -66,7 +66,7 @@ měsíce jejich dětských záznamů.
```php
DateFilter::make('birth_date')
->minDate('1900-01-01')
- ->maxDate(now()->format('Y-m-d'))
+ ->maxDate(now())
```
## API DateFilter
@@ -76,8 +76,8 @@ DateFilter::make('birth_date')
->month(bool $month = true) // výběr měsíce, shoduje se s celým měsícem
->fromLabel(string $label) // placeholder "from" (výchozí: 'From')
->toLabel(string $label) // placeholder "to" (výchozí: 'To')
-->minDate(string $date) // min volitelné datum
-->maxDate(string $date) // max volitelné datum
+->minDate(string|DateTimeInterface|null $date) // min volitelné datum
+->maxDate(string|DateTimeInterface|null $date) // max volitelné datum
```
## Chování rozsahu
diff --git a/docs/cs/table/gestures.md b/docs/cs/table/gestures.md
new file mode 100644
index 00000000..4bcd6110
--- /dev/null
+++ b/docs/cs/table/gestures.md
@@ -0,0 +1,393 @@
+---
+order: 48
+---
+
+# Vrstva gest
+
+Tabulka wire-table se umí chovat jako desktopová aplikace: šipky procházejí
+řádky, `Shift` roztahuje rozsah, myš označuje tažením přes checkboxový sloupec,
+pravý klik otevře menu řádku, `?` vysvětlí zkratky a fill handle roztáhne jednu
+hodnotu přes mnoho buněk.
+
+Pro back office je to přesně ono. Pro veřejný výpis je to obvykle špatně —
+zvýrazněný řádek a zabavený pravý klik tam v lepším případě jen ruší.
+
+Je to proto jeden vypínač a tabulka začíná na té tiché straně:
+
+```php
+->gestures()
+```
+
+Tohle je ta desktopová tabulka. Bez toho dostanete obyčejnou webovou — tu, kterou
+většina stránek chce.
+
+## Co tabulka dostane, když si neřekne
+
+**Každý způsob ovládání řádku je vypnutý, dokud si o něj neřeknete.** Tři
+schopnosti mění chování tabulky vůči návštěvníkovi, který ji ovládat nezamýšlel,
+a všechny tři čekají:
+
+- **Klávesová navigace** dá řádky do pořadí tabulátoru, označí aktivní řádek
+ a začne odpovídat na šipky a `mod`+klávesu.
+- **Označování tažením** promění stisk v checkboxovém sloupci na blokový výběr —
+ gesto, které lidé najdou omylem dřív než schválně.
+- **Rozsahový výběr** přepisuje význam modifikovaného kliku: `Shift`+klik přestane
+ být klikem a stane se z něj „všechno mezi tímhle a posledním". Ve správci
+ souborů správně, v seznamu článků překvapivě.
+
+Tabulka se `selectable()` proto začíná jako zaškrtávátka a nic víc a delegovaný
+Alpine controller se ani nevykreslí. Co zůstává povolené, stejně potřebuje
+vlastní pozvánku: kontextové menu potřebuje navázané akce, fill handle potřebuje
+`->fillHandle()` a nápověda `?` potřebuje klávesovou vrstvu, kterou tenhle default
+nechává vypnutou.
+
+```php
+// Obyčejný výpis. Checkboxy fungují a nic jiného:
+// žádné šipky, žádné označování tažením, žádný modifikovaný klik s jiným významem.
+Table::make()->selectable()
+
+// Ta samá tabulka jako aplikace.
+Table::make()->gestures()->selectable()
+```
+
+Když chcete jít opačným směrem — žádné kontextové menu, žádné rozsahy, žádný fill
+handle, vůbec nic — řekněte si o to:
+
+```php
+->gestures(false)
+```
+
+## Co se počítá jako gesto
+
+Šest schopností, každá zvlášť přepínatelná. „Výchozí" je to, co dostane tabulka,
+která `gestures()` nikdy nezavolá:
+
+| Schopnost | Výchozí | Co pokrývá |
+|-----------|---------|------------|
+| `keyboard` | **vyp** | Navigaci v mřížce: putovní `tabindex`, šipky, `Home`/`End`, `PageUp`/`PageDown`, `Enter` / `Shift`+`Enter` pro primární a sekundární record action, `Space` pro přepnutí výběru a každou vlastní `keyboardShortcut()` / `onKey()` proti aktivnímu řádku. Zároveň je to to, co z tabulky dělá ARIA `grid`. |
+| `rangeSelection` | **vyp** | `Shift`+klik, `mod`+klik a `mod`+`Shift`+klik na řádek, plus `Shift`+šipka, `Shift`+`Home` a `Shift`+`End` z klávesnice. |
+| `dragSelect` | **vyp** | Označování tažením: stisknout v checkboxovém sloupci a táhnout přes blok řádků. |
+| `contextMenu` | zap | Kontextové menu řádku pod pravým tlačítkem — jak `rowContextMenu()`, tak libovolnou `onContextMenu()` record action. |
+| `shortcutHelp` | zap¹ | Nápovědu zkratek pod `?`. |
+| `fillHandle` | zap² | Fill handle nad editovatelnými buňkami ve stylu Excelu. |
+
+`mod` je `Ctrl` na Windows a `⌘` na macOS.
+
+¹ Povolená, ale čte klávesovou vrstvu — s výchozím nastavením se tedy neotevře.
+² Povolený, ale tabulka si o něj pořád musí říct přes `->fillHandle()`.
+
+S výchozím nastavením tedy tabulka nabízí jen gesta, která sama deklarovala:
+kontextové menu, pokud je na něj navázaná akce, a fill handle, pokud si o něj
+řekla.
+
+## Kombinování
+
+Předejte closure. Dostane gesta téhle tabulky a nastaví je na místě — návratová
+hodnota se ignoruje, takže funguje jak fluent řetězec, tak víceřádkové tělo.
+
+```php
+->gestures(fn (TableGestures $g) => $g
+ ->keyboard() // šipky, Enter, zkratky …
+ ->dragSelect(false)) // … ale pořád žádné označování tažením
+```
+
+Každý setter bere `bool`, takže `->contextMenu(false)` se čte stejně dobře jako
+`->contextMenu()`.
+
+Můžete taky předat hotovou sadu, což se hodí, když má víc tabulek sdílet jeden
+domácí styl:
+
+```php
+use NyonCode\WireTable\Support\TableGestures;
+
+$readOnly = TableGestures::none()->contextMenu();
+
+// …a pak v každé tabulce:
+->gestures($readOnly)
+```
+
+`TableGestures::defaults()`, `TableGestures::all()` a `TableGestures::none()` jsou
+tři výchozí body: dodávaný default, všechno, nic.
+
+## Povolení není zapnutí
+
+Každá schopnost je **povolení**, nikdy spouštěč. Zapnout ji neznamená vyrobit to,
+co řídí:
+
+- `dragSelect` a `rangeSelection` pořád potřebují `->selectable()` (nebo
+ `->bulkActions()`, které ho implikují) — rozsah musí mít v čem růst.
+- `fillHandle` pořád potřebuje `->fillHandle()` na tabulce a editovatelné sloupce.
+- `shortcutHelp` pořád potřebuje klávesovou vrstvu, protože právě ta na klávesu
+ poslouchá.
+
+Takže `->gestures(fn ($g) => $g->dragSelect())` na tabulce bez `selectable()`
+nezmění nic. Je to záměr: vrstva gest rozhoduje, co tabulka *smí*, a zbytek API
+tabulky rozhoduje, co *má*.
+
+## `keyboard()` má tři stavy
+
+Ostatních pět schopností jsou prosté booleany. `keyboard` je třístavová, protože
+„zapnuto" tu musí znamenat dvě různé věci:
+
+| Hodnota | Význam |
+|---------|--------|
+| `false` (výchozí) | Vypnuto |
+| `null` | Rozhoduje tabulka — zapnuto pro tabulku s record actions nebo pro selectable. Tohle nastaví `gestures()` |
+| `true` | Zapnout natvrdo, i pro tabulku, která nemá ani jedno |
+
+`gestures()` nechává klávesnici na `null` místo aby ji zapínalo natvrdo: tabulka
+bez record actions a bez výběru nemá pro šipky co dělat a putovní tabindex nad
+netečnými řádky je horší než žádný:
+
+```php
+Table::make()->gestures() // není grid
+Table::make()->gestures()->selectable() // je grid
+Table::make()->gestures(fn (TableGestures $g) => $g->keyboard(true)) // grid tak jako tak
+```
+
+## Co vrstva neřídí
+
+**Výslovně deklarovaná record action funguje dál.** Vazba jako
+
+```php
+->recordAction(RecordAction::make(Action::make('view'))->onClick())
+```
+
+je vědomé rozhodnutí o téhle tabulce, ne implicitní afordance, kterou si tabulka
+zapnula sama — takže `gestures(false)` přežije. Vrstva gest řídí jen to, co by si
+tabulka jinak zapnula sama od sebe.
+
+Jedinou výjimkou je `->onKey()`, které potřebuje klávesovou vrstvu, aby měla čím
+poslouchat. S vypnutou `keyboard` nemá vazba `onKey()` odkud vystřelit.
+
+Samotný výběr zůstává taky nedotčený. I s vypnutými gesty fungují checkboxy, oba
+ovladače „vybrat vše" i bulk bar přesně jako dřív — přijdete o zkratky k nim, ne
+o funkci. Buňka výběru pak na modifikovaný klik reaguje přepnutím, protože
+s vypnutými rozsahy by na něj nereagoval nikdo jiný.
+
+## Označení aktivního řádku
+
+Řádky nesou marker aktivního řádku tehdy, když nějaké gesto potřebuje odkud růst
+— tedy když tabulka používá grid semantiku, rozsahový výběr nebo označování
+tažením.
+
+Tabulka, které zbyla jen deklarovaná klik akce, neoznačuje nic. Klik tam otevře
+záznam a jde se dál; zvýrazněný řádek, který po něm zůstane, by byl aplikační
+afordance na stránce, která o žádnou nežádala.
+
+## Výchozí nastavení pro celý projekt
+
+Nastavte jednou pro všechny tabulky:
+
+```php
+// config/wire-table.php
+'defaults' => [
+ 'gestures' => true,
+],
+```
+
+`null` (nebo chybějící klíč) ponechá dodávaný default popsaný výše, `true` povolí
+všechno všem tabulkám — back office si vrstvu zapne jednou tady místo u každé
+tabulky — `false` nepovolí nic a mapa kombinuje:
+
+```php
+'gestures' => ['keyboard' => true, 'drag_select' => false],
+```
+
+Klíče schopností se párují volně — `drag_select`, `drag-select`, `dragSelect`
+i `dragselect` jsou tentýž klíč. **Neznámý** klíč vyhodí
+`TableConfigurationException`, místo aby tiše nedělal nic: překlep v povolení je
+přesně ten druh chyby, která se projeví až za půl roku jako „proč tohle
+nefunguje".
+
+Per-table `->gestures(...)` vždy přebije výchozí hodnotu z configu.
+
+## Vypnuto je i na serveru
+
+Vypnutí schopnosti není věc toho, že klient ignoruje eventy. Jde s tím i markup
+a endpointy:
+
+- Delegované Alpine controllery se nevykreslí. Tabulka, které zbylo jen vypnutí
+ gest, nevykreslí controller vůbec a její asset bundly se nepožadují.
+- Tabulka přestane být ARIA `grid`: žádné `role="grid"`, `role="row"`, žádný
+ putovní `tabindex`.
+- Řádky nejsou fokusovatelné, takže klik nikomu nesebere fokus.
+- Fill endpoint odmítá. Vypnutý `fillHandle` zavře `fillTableCells` na serveru,
+ ne jen úchyt v UI.
+- Legenda zkratek zahodí řádky, které už neplatí — s vypnutými rozsahy se
+ `Shift`+šipka v nápovědě `?` neobjeví, protože nefunguje.
+
+To poslední je obecné pravidlo: legenda se generuje z toho, co tabulka opravdu
+dělá, takže se nemůže rozejít se skutečností.
+
+## Na telefonu jsou z gest tlačítka
+
+Tabulka řízená gesty je desktopová myšlenka. Na telefonu není dvojklik, není
+pravý klik a není hover, kterým by se jeden nebo druhý dal objevit — takže
+record action, která je na desktopu jen chováním, by na skládané mobilní kartě
+byla **nedosažitelná**.
+
+Vykreslí se tam proto jako obyčejné tlačítko, a jen tam:
+
+```php
+->recordAction(RecordAction::make(Action::make('open'))->onDoubleClick())
+```
+
+| Povrch | Co uživatel dostane |
+|--------|---------------------|
+| Desktop | Gesto dvojkliku. Žádný sloupec, žádné tlačítko. |
+| Mobilní karta | Tlačítko `Open`. |
+
+Fallback si dává pozor, aby nic nezdvojil:
+
+- Akce už přítomné v `->actions()` si drží pořadí a record actions se přidají za ně.
+- `recordAction('edit')`, které jen *odkazuje* na akci deklarovanou v
+ `->actions()`, ukáže jedno tlačítko — ne totéž dvakrát.
+- Akce povýšená do sloupce přes `->alsoInRowActions()` už tlačítkem je, takže se
+ nechá být.
+- Fallbacková tlačítka se počítají do `->collapseActionsOnMobile()`, takže karta
+ tiše nepřeroste práh, který jste nastavili.
+
+Když má karta zůstat čistá, vypněte to:
+
+```php
+->recordActionButtonsOnMobile(false)
+```
+
+## Přehled API
+
+Všechno, co vrstva nabízí, na jednom místě.
+
+### Na tabulce
+
+| Volání | Co dělá |
+|--------|---------|
+| `->gestures()` | Povolí všechny schopnosti. Klávesnice zůstane na „rozhodne tabulka" |
+| `->gestures(false)` | Nepovolí vůbec nic |
+| `->gestures(fn (TableGestures $g) => …)` | Nastaví schopnosti téhle tabulky na místě |
+| `->gestures(TableGestures $set)` | Převezme hotovou sadu |
+| `->recordActionButtonsOnMobile(bool)` | Jestli se behavior-only record actions renderují na kartě jako tlačítka (výchozí `true`) |
+
+Čtecí metody, hodí se ve vlastní view nebo v testu:
+
+| Volání | Odpovídá na |
+|--------|-------------|
+| `getGestures(): TableGestures` | Syrová povolení, ještě bez předpokladů |
+| `usesGridSemantics(): bool` | Je tohle ARIA grid? Jediný vlastník toho rozhodnutí |
+| `keyboardNavEnabled(): bool` | Alias předchozího, čte ho view |
+| `usesRangeSelection(): bool` | Fungují `Shift`/`mod` kliky a `Shift`+šipky jako rozsah? |
+| `usesDragSelect(): bool` | Označuje tažení po checkboxovém sloupci? |
+| `usesShortcutHelp(): bool` | Otevře `?` legendu? |
+| `usesActiveRowMarker(): bool` | Nesou řádky marker aktivního řádku? |
+| `mountsRecordActionController(): bool` | Vykresluje se vůbec delegovaný Alpine controller? |
+| `getGestureConfig(): array` | `['sweep' => bool, 'ranges' => bool]` — co konzumuje klientský controller |
+| `getTableRole(): ?string` | `'grid'`, nebo `null` |
+| `hasRowContextMenu(): bool` | Je tu kontextové menu (včetně povolení)? |
+| `isFillHandleEnabled(): bool` | Nabízí se fill handle (včetně povolení)? |
+
+### Na `TableGestures`
+
+```php
+use NyonCode\WireTable\Support\TableGestures;
+```
+
+| Volání | Význam |
+|--------|--------|
+| `TableGestures::defaults()` | Dodávaný default: klávesnice a tažení vypnuté, zbytek povolený |
+| `TableGestures::all()` | Všechno povolené; klávesnice zůstává na `null` |
+| `TableGestures::none()` | Nepovolené nic |
+| `TableGestures::fromConfig($value)` | Sestavení z config hodnoty (`null` / `bool` / mapa) |
+| `->keyboard(?bool)`, `->rangeSelection(bool)`, `->dragSelect(bool)`, `->contextMenu(bool)`, `->shortcutHelp(bool)`, `->fillHandle(bool)` | Settery; každý vrací `$this` |
+| `->allowsKeyboard(): ?bool` a `->allows*(): bool` | Povolení *před* předpoklady konkrétní tabulky |
+| `->toArray(): array` | Všech šest jako data |
+
+Povolení a výsledek jsou dvě různé otázky: `allowsDragSelect()` říká, že tabulka
+označovat tažením smí, `usesDragSelect()` říká, že to opravdu dělá (k tomu
+potřebuje ještě `selectable()`).
+
+## Recepty
+
+**Veřejný výpis.** Nedělejte nic:
+
+```php
+$table->model(Post::class)->columns([...]);
+```
+
+**Back-office grid.** Jedno volání, nebo `'gestures' => true` v configu pro celý
+projekt:
+
+```php
+$table->gestures()->selectable()->bulkActions([DeleteBulkAction::make()]);
+```
+
+**Klik na řádek ho otevře, jinak tichá tabulka.** Deklarovaná record action stojí
+mimo vrstvu, takže tady `gestures()` netřeba:
+
+```php
+$table->recordAction(RecordAction::make(Action::make('view'))->onClick());
+```
+
+**Klávesnici ano, tažení ne.** Pro dlouhé seznamy, kde by nechtěné tažení vybralo
+sto řádků:
+
+```php
+$table->gestures(fn (TableGestures $g) => $g->keyboard())->selectable();
+```
+
+**Domácí styl napříč tabulkami.** Sadu postavte jednou a předávejte ji:
+
+```php
+// app/Tables/Gestures.php
+public static function backOffice(): TableGestures
+{
+ return TableGestures::all()->dragSelect(false);
+}
+
+// v každé tabulce
+$table->gestures(Gestures::backOffice());
+```
+
+**Tabulka ve stránce s vlastní obsluhou klávesnice.** Nechte si myší půlku:
+
+```php
+$table->gestures(fn (TableGestures $g) => $g->rangeSelection()->dragSelect())->selectable();
+```
+
+**Rozsahy ano, tažení ne.** `Shift`+klik pro blok, bez tažení, které vybere
+omylem:
+
+```php
+$table->gestures(fn (TableGestures $g) => $g->rangeSelection())->selectable();
+```
+
+## Když něco nefunguje
+
+| Projev | Proč | Náprava |
+|--------|------|---------|
+| Šipky nic nedělají, řádky nejdou zaostřit | Klávesová vrstva je opt-in | `->gestures()` |
+| `->gestures()` je tam a grid pořád ne | Šipky nemají co ovládat — žádné record actions, žádný výběr | Přidat `->selectable()`, nebo vynutit `->gestures(fn ($g) => $g->keyboard(true))` |
+| `?` nic neotevře | Čte klávesovou vrstvu a prázdná legenda nevykreslí modal vůbec | Zapnout klávesnici; zkontrolovat `shortcutLegend()->isEmpty()` |
+| Tažení po checkboxovém sloupci nic nevybere | `dragSelect` je defaultně vypnutý | `->gestures()`, nebo `->gestures(fn ($g) => $g->dragSelect())` |
+| `Shift`+klik přepne jeden řádek místo rozsahu | `rangeSelection` je defaultně vypnutý | `->gestures()`, nebo `->gestures(fn ($g) => $g->rangeSelection())` |
+| Pravý klik ukáže menu prohlížeče | Není na něj navázaná akce, nebo je `contextMenu` vypnuté | Navázat přes `->onContextMenu()`; zkontrolovat `hasRowContextMenu()` |
+| Fill handle se neobjeví | Potřebuje `->fillHandle()` **a** povolení **a** editovatelný fillable sloupec | Zkontrolovat `isFillHandleEnabled()` a `Column::fillable()` |
+| Vazba `->onKey()` nikdy nevystřelí | Klávesy čte klávesová vrstva | `->gestures()` |
+| Record action je na telefonu nedosažitelná | Fallback byl vypnutý | `->recordActionButtonsOnMobile()` |
+
+## Jak vybrat
+
+| Tabulka | Doporučení |
+|---------|------------|
+| Veřejný výpis, marketingová stránka | Nic dělat nemusíte — to je výchozí stav |
+| Back-office výpis, operátoři na klávesnici | `->gestures()`, nebo `'gestures' => true` v configu |
+| Veřejná stránka, kde nesmí ani pravý klik | `->gestures(false)` |
+| Read-only report, pravý klik se pořád hodí | `TableGestures::none()->contextMenu()` |
+| Dlouhý seznam, klávesnice se hodí, tažení je riskantní | `->gestures(fn ($g) => $g->keyboard())` |
+| Vložená do stránky s vlastní obsluhou klávesnice | `->gestures(fn ($g) => $g->rangeSelection()->dragSelect())` |
+| Výběr je důležitý, náhodné tažení ne | `->gestures(fn ($g) => $g->rangeSelection())` |
+
+## Viz také
+
+- [Výběr řádků](selection.md) — co dělá které výběrové gesto
+- [Record actions](record-actions.md) — navázání akce na gesto řádku
+- [Pokročilé](advanced.md) — fill handle
diff --git a/docs/cs/table/overview.md b/docs/cs/table/overview.md
index a695c157..6e6c20b9 100644
--- a/docs/cs/table/overview.md
+++ b/docs/cs/table/overview.md
@@ -686,4 +686,7 @@ class UserTable extends Component
| [Importy](imports.md) | Importy CSV — mapování hlaviček, přetypování, validace po řádcích, updateExisting |
| [Správci relací](relation-managers.md) | Tabulky zúžené na relaci jako samostatné Livewire komponenty |
| [Pokročilé](advanced.md) | Podřádky, souhrnná patička, polling, lazy loading, cachování, debug, responzivita |
+| [Výběr řádků](selection.md) | Zaškrtávátka, „vybrat vše odpovídající" a výběrová gesta |
+| [Akce nad záznamem](record-actions.md) | Vazby na klik, dvojklik, pravý klik a klávesy celého řádku |
+| [Vrstva gest](gestures.md) | `gestures()` — opt-in klávesová/tažecí vrstva a fallback na tlačítka na mobilu |
| [Akce](../core/actions.md) | Kompletní systém akcí — modály, formuláře, wizard kroky, životní cyklus |
diff --git a/docs/cs/table/record-actions.md b/docs/cs/table/record-actions.md
index 960317a0..5f7f4a37 100644
--- a/docs/cs/table/record-actions.md
+++ b/docs/cs/table/record-actions.md
@@ -91,38 +91,80 @@ Action::make('edit')->onDoubleClick()->alsoInRowActions()
## Ovládání klávesnicí
-Jakmile má tabulka libovolnou record action, klávesová navigace se zapne
-automaticky a tabulka se ohlásí jako grid:
+Klávesová navigace je opt-in přes `->gestures()`. Jakmile si o ni tabulka řekne,
+platí pro každou tabulku, kterou klávesnice umí ovládat řádek po řádku — pro tu
+s akcemi nad záznamem stejně jako pro tu, která je `->selectable()` nebo má
+hromadné akce — a taková tabulka se ohlásí jako ARIA grid:
+
+```php
+->gestures()
+->recordAction(Action::make('open')->onDoubleClick())
+```
| Klávesa | Akce |
|---------|------|
| `↑` / `↓` | Posun aktivního řádku |
+| `Home` / `End`, `PageUp` / `PageDown` | Skok na okraj, nebo posun o obrazovku |
| `Enter` | Primární record action (binding dvojkliku, jinak kliku) |
| `Shift` + `Enter` | Sekundární record action (druhý pointer binding) |
| `Space` | Přepnout výběr aktivního řádku (a nastavit kotvu) při selectable, jinak primární akce |
-| `Shift` + `↑` / `↓` | Rozšířit souvislý výběr od kotvy (desktopový range-select) |
+| `Shift` + `↑` / `↓` | Rozšířit výběr od kotvy |
| `mod` + `A` | Vybrat všechny řádky na stránce |
-| Menu klávesa | Otevřít kontextové menu řádku |
+| Menu klávesa, `Shift` + `F10` | Otevřít kontextové menu řádku |
+| `?` | Zobrazit zkratky, na které tabulka reaguje |
| `Delete`, `mod+d`, … | Vlastní `->onKey()` / `->keyboardShortcut()` akce |
-Klávesnicový výběr řídí **stejný** stav výběru jako checkboxy a bulk bar — šipkou
-na řádek, `Space` pro výběr, `Shift`+šipka pro rozšíření bloku — pak spusť
-hromadnou akci z baru.
+Vazba `->onKey('Delete')` reaguje i na `Backspace` — na klávesnici Macu je to
+tatáž klávesa pod jiným jménem.
+
+Výběrová gesta — `Space`, rozsahy, `mod`+`A` — popisuje
+[Výběr řádků](selection.md), včetně gest myší a toho, co rozsah znamená, když je
+vybráno „vše odpovídající".
+
+Myš i klávesnice sdílejí jeden aktivní řádek: **klik na řádek ho označí** a šipky
+pokračují odtud, takže se tabulka nikdy neovládá ze dvou míst zároveň. Označení
+zůstane vidět i pod kurzorem, přežije roundtrip vyvolaný akcí a drží se svého
+záznamu i po přeřazení (když záznam ze stránky zmizí, tabstop se vrátí na první
+řádek).
-Vynuť vypnutí (či zapnutí), pokud potřebuješ:
+Klávesy dosáhnou na grid jen tehdy, když má fokus **samotný řádek**: stisk uvnitř
+tlačítka akce, inline editovatelné buňky nebo dropdownu patří tomu prvku. Dokud
+je otevřený modal akce, grid je inertní — žádná šipka neposune označení za
+dialogem a žádná zkratka nespustí druhou akci — a po zavření modalu se fokus
+vrátí na aktivní řádek, takže šipky dál fungují.
+
+Vynuť vypnutí (či zapnutí), pokud potřebuješ — klávesnice je jedna ze schopností
+[vrstvy gest](gestures.md):
```php
-->recordActionKeyboard(false)
+->gestures(fn (TableGestures $g) => $g->keyboard(false))
```
Protože Enter vždy dosáhne na primární akci, každá record action zůstává
dostupná klávesnicí — behavior-only akce nikdy není past jen pro myš.
+### Klávesy, které si grid vyhrazuje
+
+Klávesy, kterými grid naviguje, nejde navázat na akci — vazba by nikdy
+nevystřelila. Místo tichého zahození proto `->onKey()` vyhodí výjimku už při
+konfiguraci:
+
+```text
+Enter Space ArrowUp ArrowDown Home End PageUp PageDown ContextMenu F10 ?
+```
+
+`keyboardShortcut()` nastavený přímo na akci se jen přeskočí a nikdy není
+fatální — taková akce může legitimně sloužit i toolbaru nebo paletě.
+
## Kombinace s výběrem a hromadnými akcemi
-Když je tabulka `->selectable()`, jednoklik dál vybírá řádek, takže výchozí
-trigger record akce se stává **dvojklik** — nekolidují spolu. Klik na checkbox
-jen přepne výběr a hromadné akce zůstávají nedotčené:
+Když je tabulka `->selectable()`, výchozím triggerem record akce se stává
+**dvojklik**, takže jednoklik zůstává volný pro práci s výběrem — jen označí
+řádek, na který dopadne (aktivní řádek pro klávesnici a kotva dalšího
+`Shift`+rozsahu). Prostý klik zaškrtávátko nikdy nezaškrtne; ty s modifikátorem
+záměrně ano, protože přesně to `Shift` a `mod` znamenají všude jinde (viz
+[Výběr řádků](selection.md)). Klik s modifikátorem je výběrové gesto a nikdy
+nespustí navázanou akci nad záznamem. Hromadné akce zůstávají nedotčené:
```php
->selectable()
@@ -139,7 +181,20 @@ obarvi pro silnější náznak „tento řádek je klikatelný":
```php
->recordActionHover('primary') // obarvený hover místo neutrální šedé
-->activeRowClass('bg-amber-100') // přepis zvýraznění klávesnicově aktivního řádku
+->activeRowClass('bg-amber-100') // přepis označení aktivního řádku (klik i klávesnice)
+```
+
+Aktivní řádek po dobu označení shazuje svůj hover tint, takže označení nikdy
+nepřebije `hover:bg-*`, když na něm spočine kurzor.
+
+Ve výchozím stavu jsou označením dva signály, ne jeden: podbarvení a pruh u
+náběžné hrany řádku. Samotné podbarvení má vůči prostému řádku kontrast asi
+1,1:1 — pod hranicí 3:1 a neviditelné pro každého, kdo ty dva odstíny nerozliší.
+`activeRowClass()` nahrazuje **obě** poloviny, takže přepis si ručí za vlastní
+kontrast:
+
+```php
+->activeRowClass('bg-amber-100 [&>td:first-of-type]:before:bg-amber-600')
```
## Doporučené UX
@@ -178,3 +233,10 @@ tlačítka:
Action::make('delete')->onContextMenu(),
])
```
+
+## Související dokumentace
+
+- [Výběr řádků](selection.md) — výběrová gesta, se kterými akce nad záznamem
+ sdílejí řádek
+- [Akce](actions.md) — řádkové, hromadné a hlavičkové akce
+- [Vrstva gest](gestures.md) — vypnutí gest a tlačítkový fallback na mobilu
diff --git a/docs/cs/table/selection.md b/docs/cs/table/selection.md
new file mode 100644
index 00000000..da07f376
--- /dev/null
+++ b/docs/cs/table/selection.md
@@ -0,0 +1,193 @@
+---
+order: 47
+---
+
+# Výběr řádků
+
+Z výběru může být sada gest, ne jen sloupec zaškrtávátek. Tabulka se
+`->selectable()` — nebo taková, která má jen `->bulkActions()`, což výběr
+implikuje — dává zaškrtávátka, ovladače „vybrat vše" a bulk bar:
+
+```php
+->selectable()
+->bulkActions([DeleteBulkAction::make()])
+```
+
+Přidejte `->gestures()` a chová se navíc jako seznam v desktopovém správci
+souborů: `Shift`+klik vezme rozsah, `mod`+klik přidá jeden řádek, tažení po
+sloupci se zaškrtávátky nabere celý blok a z klávesnice šipky procházejí řádky,
+`Space` přepíná, `Shift`+šipky roztahují a `mod`+`A` vezme stránku.
+
+```php
+->gestures()
+->selectable()
+```
+
+To rozdělení je záměrné: klávesová navigace, rozsahový výběr i označování tažením
+mění chování tabulky vůči někomu, kdo ji ovládat nezamýšlel, takže čekají, až si
+o ně řeknete (viz [Vrstva gest](gestures.md)). U všeho níže je uvedeno, co
+potřebuje.
+
+## Co umí myš
+
+| Gesto | Výsledek | Potřebuje |
+|-------|----------|-----------|
+| Klik do buňky výběru | Přepne řádek a nastaví kotvu rozsahu | — |
+| `Shift` + klik | Vybere rozsah mezi kotvou a tímto řádkem | `gestures()` |
+| `mod` + klik | Přepne tento jeden řádek (kdekoli na něm) a zakotví zde | `gestures()` |
+| `mod` + `Shift` + klik | Přidá celý blok k tomu, co už je vybrané | `gestures()` |
+| Tažení po sloupci se zaškrtávátky | Přejede a vybere souvislý úsek řádků | `gestures()` |
+| Klik na samotný řádek | Označí řádek (viz níže) — zaškrtávátko nezaškrtne | — |
+
+Cílem je **celá buňka výběru**, ne jen šestnáctipixelové políčko uvnitř: samotné
+políčko je pod každým doporučením pro velikost dotykového cíle a zbytek buňky
+zůstává mrtvý. Klik v buňce se nikdy nedostane k akci navázané na řádek.
+
+Prostý klik na *tělo* řádku řádek označí — stane se aktivním řádkem pro
+klávesnici a kotvou pro další rozsah — ale nevybere ho. Výběr zůstává záměrný.
+Výjimkou je `mod`+klik, a přesně od toho ten modifikátor je.
+
+## Co umí klávesnice
+
+Všechno v téhle sekci potřebuje `->gestures()` — viz
+[Vrstva gest](gestures.md).
+
+| Klávesa | Výsledek |
+|---------|----------|
+| `↑` / `↓` | Posun aktivního řádku |
+| `Home` / `End` | Skok na první / poslední řádek stránky |
+| `PageUp` / `PageDown` | Posun o jednu obrazovku |
+| `Space` | Přepne aktivní řádek a zakotví zde |
+| `Shift` + `↑` / `↓` | Zvětší nebo zmenší rozsah od kotvy |
+| `Shift` + `Home` / `End` | Rozšíří rozsah k prvnímu / poslednímu řádku |
+| `mod` + `Shift` + `↑` / `↓` | Totéž co `Shift`+`Home` / `End` |
+| `mod` + `A` | Vybere všechny řádky na stránce |
+| `?` | Zobrazí zkratky, na které tabulka reaguje |
+
+Klávesnicový výběr řídí **stejný** stav jako zaškrtávátka a lišta hromadných
+akcí: šipkou na řádek, `Space` pro výběr, `Shift`+šipka pro rozšíření, pak
+hromadná akce.
+
+Klávesy se k tabulce dostanou jen tehdy, když má fokus **samotný řádek**. Stisk
+uvnitř řádku — tlačítko akce, editovatelná buňka, dropdown — patří tomu prvku,
+takže `Space` napsaný do buňky zůstane mezerou a `?` napsaný do vyhledávání
+nápovědu neotevře.
+
+## Jak se chovají rozsahy
+
+Každý rozsah roste od **kotvy**: řádku, který jste naposledy vybrali klávesou
+`Space`, zaškrtávátkem nebo `mod`+klikem. Kotva je neviditelná a jednorázová —
+prostý posun šipkou ji zruší.
+
+Rozsah zapisuje **to, co už bylo vybrané, plus rozsah** — ne jen rozsah samotný.
+Vyberte řádky 2–6, zakotvěte na řádku 8, `Shift`+šipkou dolů na 12 a máte
+vybráno 2–6 a 8–12. Zmenšení rozsahu vrátí jen ty řádky, které rozsah sám
+přidal.
+
+Když výběr žádnou vlastní kotvu nemá — vznikl přes `mod`+`A` nebo přes pruh
+„vybrat vše" — první `Shift`+šipka roste od vzdálenější hrany souvislého bloku,
+ve kterém stojíte. Díky tomu *zmenší nebo zvětší blok, který vidíte*, místo aby
+zahodila zbytek výběru.
+
+Jednotlivé řádky z výběru vyřadíte tak, že na ně přejdete šipkami a stisknete
+`Space`, nebo je odkliknete `mod`+klikem.
+
+## Výběr přes hranici stránky
+
+Jakmile je vybraná celá stránka, nabídne lišta **Vybrat všech N** a výběr přejde
+z výčtu klíčů na „vše, co odpovídá aktuálnímu filtru" (co ten tvar znamená a proč
+existuje, popisují [Hromadné akce](actions.md#hromadne-akce)).
+
+Gesta fungují i v tomto režimu a čtou se tak, jak se od „všechno kromě…" čeká:
+
+- `Shift`+šipka přes rozsah ho **odznačí**, protože uložený seznam je seznam
+ výjimek.
+- `mod`+`A` stojí stranou. Vybráno je už všechno, není co přidat.
+- Zaškrtávátko v hlavičce edituje výjimky a nikdy vás tiše nevrátí zpět k
+ výčtovému výběru.
+
+## Tažení po sloupci
+
+Stiskněte tlačítko ve sloupci se zaškrtávátky a táhněte: každý řádek, přes který
+projedete, se vybere, a tabulka u okraje sama odroluje. Gesto je záměrně úzce
+vymezené.
+
+- **Jen přidává.** Couvnutí zpět nic neodznačí. Přejezd může vždy jen přidávat.
+- **Jen myš.** Prst tažený po sloupci roluje stránku, jak má.
+- **Jen ve sloupci se zaškrtávátky.** Tažení jinde označuje text jako vždy.
+- Startuje až prvním pohybem, který změní řádek, takže prostý klik zůstane
+ prostým klikem.
+
+## Nápověda ke zkratkám
+
+Stiskněte `?` s fokusem na řádku a tabulka ukáže přesně to, na co reaguje —
+včetně vašich vlastních vazeb přes `->onKey()` a jejich popisků. Seznam vzniká z
+konfigurace té konkrétní tabulky, takže tabulka bez akcí nad záznamem si žádné
+nevymyslí.
+
+Tentýž seznam je k dispozici jako data, pokud si ho chcete vykreslit sami:
+
+```php
+$sections = $table->shortcutLegend()->sections();
+```
+
+Každá sekce má přeložený `heading` a seznam hodnotových objektů `ShortcutHint`
+(`->keys`, `->description`, `->labels(mac: true)`).
+
+## Přístupnost
+
+Výběr není funkce jen pro myš a tabulka to dává najevo:
+
+- Tabulka je ARIA **grid**: `aria-rowcount`, `aria-multiselectable` a
+ `aria-rowindex` na každém řádku, počítaný přes celou sadu výsledků — takže
+ první řádek druhé stránky se ohlásí jako řádek 12, ne znovu jako řádek 1.
+- Každý řádek hlásí `aria-selected`, drženo v souladu s živým výběrem, ne s
+ poslední odpovědí serveru.
+- Změny výběru se ohlašují v „polite" live regionu: *„3 z 40 vybráno"*,
+ *„Vybráno vše (40)"*, *„Výběr zrušen"*.
+- Aktivní řádek je označen podbarvením **a** pruhem u náběžné hrany. Samotná
+ barva by selhala u každého, kdo ty dva odstíny nerozliší, a samotné podbarvení
+ má kontrast asi 1,1:1 — pod hranicí 3:1. Pruh ji splňuje ve světlém i tmavém
+ režimu.
+
+Pokud se marker bije s vaším designem, přepište ho — přepis nahrazuje obě
+poloviny, takže si ručí za vlastní kontrast:
+
+```php
+->activeRowClass('bg-amber-100 [&>td:first-of-type]:before:bg-amber-600')
+```
+
+## Klávesy, které si tabulka vyhrazuje
+
+Grid vlastní klávesy, kterými naviguje, takže navázat na ně akci nad záznamem by
+znamenalo mrtvý kód. `->onKey()` proto odmítne už při konfiguraci, místo aby
+vazbu tiše zahodil:
+
+```text
+Enter Space ArrowUp ArrowDown Home End PageUp PageDown ContextMenu F10 ?
+```
+
+`Backspace` **vyhrazený není**: funguje jako platformní alias klávesy `Delete`,
+takže vazba `->onKey('Delete')` reaguje na obojí a explicitní
+`->onKey('Backspace')` zůstává platný.
+
+## Jak to zase vypnout
+
+Tabulka, která si o vrstvu gest řekla, může vracet schopnosti po jedné — nebo
+všechny najednou:
+
+```php
+->gestures(fn (TableGestures $g) => $g->dragSelect(false)) // klávesnici nech, tažení pryč
+->gestures(fn (TableGestures $g) => $g->keyboard(false)) // a naopak
+->gestures(false) // všechna gesta včetně rozsahů
+```
+
+Zaškrtávátka fungují v každé z těch variant dál — viz
+[Vrstva gest](gestures.md).
+
+## Související dokumentace
+
+- [Akce nad záznamem](record-actions.md) — vazby na klik, dvojklik a kontextové
+ menu celého řádku
+- [Hromadné akce](actions.md#hromadne-akce) — práce s výběrem
+- [Vrstva gest](gestures.md) — jak tahle gesta vypnout, celá nebo po částech
diff --git a/docs/cs/upgrade.md b/docs/cs/upgrade.md
index 60fdbd8c..e09300a6 100644
--- a/docs/cs/upgrade.md
+++ b/docs/cs/upgrade.md
@@ -78,6 +78,84 @@ Před upgradem ověřte, že je vaše aplikace splňuje.
---
+## Výběr a klávesová gesta
+
+Z výběru v tabulce se stala plnohodnotná sada gest, ne jen sloupec zaškrtávátek
+(viz [Výběr řádků](table/selection.md)). Při upgradu zkontrolujte čtyři věci.
+
+**1. Všechna gesta nad řádkem jsou opt-in — `->gestures()`.** Z výběru se stala
+plnohodnotná sada gest: `Shift`/`mod` kliky pro rozsahy, tažení po sloupci se
+zaškrtávátky, které nabere celý blok, a z klávesnice šipky, `Space`, `Shift`+šipky
+a `mod`+`A`. Nic z toho není zapnuté, dokud si o to tabulka neřekne — každé z nich
+totiž mění chování tabulky vůči návštěvníkovi, který ji ovládat nezamýšlel: řádky
+jdou do pořadí tabulátoru, označuje se aktivní řádek, tažení začne vybírat
+a modifikovaný klik přestane být klikem.
+
+Tabulkám, které to chtějí, přidejte jedno volání:
+
+```php
+->gestures()
+->selectable()
+```
+
+nebo, pokud je celý projekt back office:
+
+```php
+// config/wire-table.php
+'defaults' => ['gestures' => true],
+```
+
+Co změna *neovlivní*: zaškrtávátka, oba ovladače „vybrat vše" i bulk bar fungují
+beze změny a tabulka, která si o gesta neřekla, nemontuje delegovaný controller
+vůbec. Stejně tak kontextové menu pod pravým tlačítkem a fill handle — o oboje
+jste si stejně museli říct sami.
+Šest schopností a jak je kombinovat najdete ve [Vrstvě gest](table/gestures.md).
+
+**2. `->onKey()` na navigační klávese nově vyhodí výjimku.** Dřív se tiše
+zahodila, takže akce prostě nikdy nevystřelila. Pokud takovou vazbu máte, byla
+to už dřív mrtvá větev — přemapujte ji na volnou klávesu:
+
+```text
+Enter Space ArrowUp ArrowDown Home End PageUp PageDown ContextMenu F10 ?
+```
+
+`Backspace` zůstává k dispozici a nově funguje i jako alias klávesy `Delete`.
+
+**3. Rozsahová gesta už neopouštějí režim „vše odpovídající".** Když je vybráno
+„vše, co odpovídá filtru", je uložený seznam seznamem *výjimek* — takže rozsah
+přes `Shift`+šipku ho nově **odznačí**, místo aby celý výběr zúžil na jednu
+stránku. Pokud výběr čtete přímo, počítejte s tím, že `getSelectedRecordKeys()`
+v tomto režimu záměrně vrací `[]`; použijte `selectedRecordsQuery()` nebo
+`eachSelectedRecord()`.
+
+**4. Přepublikujte view tabulky, pokud jste ho přepsali.** Gesta potřebují
+markup, který zkompilovaný JavaScript hledá, a publikovaná kopie
+`resources/views/vendor/wire-table/tables/index.blade.php` ho mít nebude. View
+nese kontraktní značku, takže zastaralá kopie spadne hlasitě v konzoli prohlížeče
+místo toho, aby tiše vybírala špatné řádky:
+
+```bash
+php artisan vendor:publish --tag=wire-table::views --force
+```
+
+Své úpravy pak naneste znovu na nový soubor. Pokud jste view přepsali jen kvůli
+vzhledu, bývá [Theming](theming.md) menší cesta.
+
+**5. Akce nad záznamem, které byly jen chováním, se na mobilní kartě nově
+vykreslí jako tlačítko.** Telefon nemá dvojklik, pravý klik ani hover, kterým by
+se jeden nebo druhý dal objevit — akce navázaná jen na gesto tak byla po složení
+tabulky nedosažitelná. Nově se na kartě vykreslí jako obyčejné tlačítko, a jen
+tam; desktopová tabulka se nemění. Nic se nezdvojí: akce už přítomná
+v `->actions()` i akce povýšená přes `->alsoInRowActions()` dá právě jedno
+tlačítko a fallbacková tlačítka se počítají do `->collapseActionsOnMobile()`.
+Vypnout lze pro konkrétní tabulku:
+
+```php
+->recordActionButtonsOnMobile(false)
+```
+
+---
+
## Hledání breaking changes
`CHANGELOG.md` je zdroj pravdy. Breaking changes jsou vyznačeny pod nadpisem
diff --git a/docs/forms/fields/date-time-picker.md b/docs/forms/fields/date-time-picker.md
index 54c9dca4..7485f0a8 100644
--- a/docs/forms/fields/date-time-picker.md
+++ b/docs/forms/fields/date-time-picker.md
@@ -40,6 +40,29 @@ DateTimePicker::make('start')
->closeOnDateSelection()
```
+Bounds take anything readable as a date — a `Carbon`/`DateTimeInterface`, or a
+string such as `'2026-07-10'`, `'10.07.2026'`, `'today'` or `'+1 week'` — and are
+reshaped for the widget behind the scenes. A bound that cannot be read at all
+throws, rather than being silently dropped by the browser.
+
+```php
+DateTimePicker::make('start')
+ ->minDate(now()) // no past dates
+ ->maxDate(now()->addYear())
+```
+
+On a `datetime` picker a bound may also carry a time, which then limits the
+clock on that boundary day only:
+
+```php
+DateTimePicker::make('slot')
+ ->minDate('2026-07-10 08:30') // 10 July cannot start before 08:30
+ ->maxDate('2026-07-20 17:00') // 20 July cannot run past 17:00
+```
+
+A day-granular upper bound covers the whole day: `->maxDate('2026-07-20')`
+leaves 20 July selectable up to 23:59.
+
## Time Options
```php
@@ -92,8 +115,8 @@ The only exception is [`asMonth()`](#modes), which is always native.
| `asDateTime()` | — | Alias for `mode('datetime')` |
| `format(string)` | string | Storage format (Carbon compatible) |
| `displayFormat(string)` | string | Display format shown to the user |
-| `minDate(string\|Closure)` | string | Minimum selectable date |
-| `maxDate(string\|Closure)` | string | Maximum selectable date |
+| `minDate(string\|DateTimeInterface\|Closure)` | string | Earliest selectable date; may carry a time on a `datetime` picker |
+| `maxDate(string\|DateTimeInterface\|Closure)` | string | Latest selectable date; a day-granular bound covers the whole day |
| `disabledDates(array\|Closure)` | array | Dates that cannot be selected |
| `firstDayOfWeek(int)` | int | 0=Sunday, 1=Monday |
| `closeOnDateSelection()` | bool | Close picker after a date is selected |
diff --git a/docs/table/advanced.md b/docs/table/advanced.md
index 15be3c69..6591b539 100644
--- a/docs/table/advanced.md
+++ b/docs/table/advanced.md
@@ -360,11 +360,27 @@ $table->lazy()
### How It Works
-1. Page renders immediately with the placeholder HTML
+1. Page renders immediately with the placeholder HTML — and with the Alpine bundles the table will need
2. Livewire dispatches an async call to load table content
3. Placeholder is replaced with the fully rendered table
4. Subsequent interactions (sort, filter, paginate) are normal Livewire calls
+Step 1 is not a detail you can skip past. The bundles behind dropdowns, row
+selection and the record controller register their Alpine components from an
+`alpine:init` listener, and that event fires exactly once — when Alpine boots.
+A bundle arriving with the deferred markup would land after it, subscribe to an
+event that never fires again, and register nothing; the table would then come
+up with every dropdown dead and each sheet backdrop stuck over the page. So the
+**placeholder** render ships them, and the markup that replaces it initialises
+normally.
+
+Which bundles load follows the table's own configuration: the dropdown bundle
+always (the toolbar is built from dropdowns), the selection bundle with
+`selectable()`, and the record controller whenever the table mounts it at all —
+record-action pointer bindings, a row context menu, grid keyboard semantics,
+drag-select or Shift-range selection. A custom `lazyPlaceholder()` replaces the
+visible skeleton only — it never changes what loads.
+
### When to Use
- Dashboard pages with multiple tables — load each lazily
diff --git a/docs/table/columns/editing.md b/docs/table/columns/editing.md
index eda69f5d..533b066e 100644
--- a/docs/table/columns/editing.md
+++ b/docs/table/columns/editing.md
@@ -68,8 +68,8 @@ and query-string persistence.
->filterAsSelect(array|string $options, ?string $placeholder = null) // single value; searchable combobox
->filterAsMultiSelect(array|string $options, ?string $placeholder = null) // several values (whereIn); searchable combobox
->filterSearchable(bool $condition = true) // toggle the in-panel search (on by default)
-->filterAsDate(?string $minDate = null, ?string $maxDate = null)
-->filterAsDateRange(?string $minDate = null, ?string $maxDate = null)
+->filterAsDate(string|DateTimeInterface|null $minDate = null, string|DateTimeInterface|null $maxDate = null)
+->filterAsDateRange(string|DateTimeInterface|null $minDate = null, string|DateTimeInterface|null $maxDate = null)
->filterAsNumberRange(?float $min = null, ?float $max = null, ?float $step = null)
->filterAsBoolean(?string $trueLabel = null, ?string $falseLabel = null)
->filterOperator(string $operator) // '=', '!=', '>', '<', '>=', '<=', 'like' (default, partial match), 'starts_with', 'ends_with'
diff --git a/docs/table/columns/fill-handle.md b/docs/table/columns/fill-handle.md
index 238fbe93..7454a385 100644
--- a/docs/table/columns/fill-handle.md
+++ b/docs/table/columns/fill-handle.md
@@ -134,3 +134,5 @@ the versions the previous call returned; do not reuse the ones you started with.
- [Editing & Column-Level Filters](editing.md) — how a single inline save works
- [TextInputColumn](text-input.md) · [SelectColumn](select.md) · [ToggleColumn](toggle.md)
+- [The Gesture Layer](../gestures.md) — the handle is one of its capabilities;
+ `gestures(false)` closes it, endpoint included
diff --git a/docs/table/filters/date.md b/docs/table/filters/date.md
index 7f1e6208..595a7cac 100644
--- a/docs/table/filters/date.md
+++ b/docs/table/filters/date.md
@@ -66,7 +66,7 @@ the month of their child records.
```php
DateFilter::make('birth_date')
->minDate('1900-01-01')
- ->maxDate(now()->format('Y-m-d'))
+ ->maxDate(now())
```
## DateFilter API
@@ -76,8 +76,8 @@ DateFilter::make('birth_date')
->month(bool $month = true) // month picker, matches whole month
->fromLabel(string $label) // "from" placeholder (default: 'From')
->toLabel(string $label) // "to" placeholder (default: 'To')
-->minDate(string $date) // min selectable date
-->maxDate(string $date) // max selectable date
+->minDate(string|DateTimeInterface|null $date) // min selectable date
+->maxDate(string|DateTimeInterface|null $date) // max selectable date
```
## Range Behavior
diff --git a/docs/table/gestures.md b/docs/table/gestures.md
new file mode 100644
index 00000000..9263966c
--- /dev/null
+++ b/docs/table/gestures.md
@@ -0,0 +1,396 @@
+---
+order: 48
+---
+
+# The Gesture Layer
+
+A wire-table table can behave like a desktop application: arrow keys walk the
+rows, `Shift` works a range, the mouse sweeps down the checkbox column, right
+click opens a row menu, `?` explains itself, and a fill handle drags one value
+across many cells.
+
+That is exactly right for a back office. It is usually wrong for a public
+listing, where a highlighted row and a hijacked right click are noise at best.
+
+So it is one switch, and a table starts on the quiet side of it:
+
+```php
+->gestures()
+```
+
+That is the desktop-application table. Without it you get an ordinary web
+table — the one most pages want.
+
+## What a table gets without asking
+
+**Every way of operating a row is off until you ask.** Three capabilities change
+how the table answers a visitor who never intended to operate it, and all three
+wait:
+
+- **Keyboard navigation** puts the rows in the tab order, marks an active row
+ and starts answering arrows and `mod`+key.
+- **The drag sweep** turns a press in the checkbox column into a block
+ selection — a gesture people find by accident before they find it on purpose.
+- **Range selection** re-reads a modified click: `Shift`+click stops being a
+ click and becomes "everything between here and the last one". Right in a file
+ manager, startling in a list of blog posts.
+
+A selectable table therefore starts as checkboxes and nothing more, and the
+delegated Alpine controller is not even rendered. What stays allowed needs an
+invitation of its own anyway: a context menu needs actions bound to it, the fill
+handle needs `->fillHandle()`, and the `?` help needs the keyboard layer this
+default leaves off.
+
+```php
+// An ordinary listing. Checkboxes work and nothing else does:
+// no arrow keys, no drag selecting, no modified click meaning something else.
+Table::make()->selectable()
+
+// The same table as an application.
+Table::make()->gestures()->selectable()
+```
+
+To go further the other way — no right-click menu, no ranges, no fill handle,
+nothing at all — say so:
+
+```php
+->gestures(false)
+```
+
+## What counts as a gesture
+
+Six capabilities, each switchable on its own. "Default" is what a table that
+never calls `gestures()` gets:
+
+| Capability | Default | What it covers |
+|------------|---------|----------------|
+| `keyboard` | **off** | Grid navigation: roving `tabindex`, arrows, `Home`/`End`, `PageUp`/`PageDown`, `Enter` / `Shift`+`Enter` for the primary and secondary record action, `Space` to toggle the selection, and every action's own `keyboardShortcut()` / `onKey()` against the active row. Also what makes the table an ARIA `grid`. |
+| `rangeSelection` | **off** | `Shift`+click, `mod`+click and `mod`+`Shift`+click on a row, plus `Shift`+arrow, `Shift`+`Home` and `Shift`+`End` from the keyboard. |
+| `dragSelect` | **off** | The mouse sweep: press in the checkbox column and drag to select a block of rows. |
+| `contextMenu` | on | The right-click row menu — both `rowContextMenu()` and any `onContextMenu()` record action. |
+| `shortcutHelp` | on¹ | The `?` shortcut help. |
+| `fillHandle` | on² | The Excel-style fill handle on editable cells. |
+
+`mod` is `Ctrl` on Windows and `⌘` on macOS.
+
+¹ Allowed, but it reads the keyboard layer, so with the default it never opens.
+² Allowed, but the table still has to call `->fillHandle()`.
+
+With the default, then, the only gestures a table really offers are the ones it
+declared itself: a right-click menu if an action is bound to one, and the fill
+handle if it asked for one.
+
+## Mixing them
+
+Pass a closure. It receives this table's gestures and configures them in place —
+the return value is ignored, so a fluent chain and a multi-line body both work.
+
+```php
+->gestures(fn (TableGestures $g) => $g
+ ->keyboard() // arrows, Enter, shortcuts …
+ ->dragSelect(false)) // … but still no mouse sweep
+```
+
+Every setter takes a `bool`, so `->contextMenu(false)` reads as well as
+`->contextMenu()`.
+
+You can also hand over a prepared set, which is useful when several tables share
+one house style:
+
+```php
+use NyonCode\WireTable\Support\TableGestures;
+
+$readOnly = TableGestures::none()->contextMenu();
+
+// …then, in each table:
+->gestures($readOnly)
+```
+
+`TableGestures::defaults()`, `TableGestures::all()` and `TableGestures::none()`
+are the three starting points: the shipped default, everything, nothing.
+
+## A permission is not a switch-on
+
+Every capability is a **permission**, never a trigger. Turning one on does not
+conjure the thing it governs:
+
+- `dragSelect` and `rangeSelection` still need `->selectable()` (or
+ `->bulkActions()`, which implies it) — there has to be a selection for a range
+ to grow in. Both are also off in the default, so they need the permission
+ *and* the selection.
+- `fillHandle` still needs `->fillHandle()` on the table and editable columns.
+- `shortcutHelp` still needs the keyboard layer, because the keyboard layer is
+ what listens for the key.
+
+So `->gestures(fn ($g) => $g->dragSelect())` on a table without `selectable()`
+changes nothing. This is deliberate: the gesture layer decides what a table is
+*allowed* to do, and the rest of the table API decides what it *has*.
+
+## `keyboard()` has three states
+
+The other five capabilities are plain booleans. `keyboard` is three-state,
+because "on" has to mean two different things:
+
+| Value | Meaning |
+|-------|---------|
+| `false` (the default) | Off |
+| `null` | The table decides — on for a table with record actions or a selectable one. This is what `gestures()` sets |
+| `true` | Force it on, even for a table with neither |
+
+`gestures()` leaves the keyboard at `null` rather than forcing it, because a
+table with no record actions and no selection has nothing for the arrows to do,
+and a roving tabindex over inert rows is worse than none:
+
+```php
+Table::make()->gestures() // not a grid
+Table::make()->gestures()->selectable() // a grid
+Table::make()->gestures(fn (TableGestures $g) => $g->keyboard(true)) // a grid regardless
+```
+
+## What the layer does *not* govern
+
+**An explicitly declared record action keeps firing.** A binding like
+
+```php
+->recordAction(RecordAction::make(Action::make('view'))->onClick())
+```
+
+is a deliberate statement about this table, not an implicit affordance the table
+turned on for itself — so it survives `gestures(false)`. The gesture layer only
+governs the layer a table would otherwise switch on for itself.
+
+The one exception is `->onKey()`, which needs a keyboard layer to listen with.
+With `keyboard` off, an `onKey()` binding has nowhere to fire from.
+
+Selection itself is likewise untouched. With every gesture off, the checkboxes,
+both select-all controls and the bulk bar work exactly as they always did — you
+lose the shortcuts to them, not the feature. The selection cell then answers a
+modified click by toggling, since with ranges off nothing else would.
+
+## The active-row marker
+
+Rows carry the active-row marker when any gesture needs somewhere to grow from —
+that is, when the table uses grid semantics, range selection, or the sweep.
+
+A table left with nothing but a declared click action marks nothing. A click
+there opens the record and moves on; a highlighted row left behind would be an
+application affordance on a page that asked for none.
+
+## A project-wide default
+
+Set it once for every table:
+
+```php
+// config/wire-table.php
+'defaults' => [
+ 'gestures' => true,
+],
+```
+
+`null` (or a missing key) keeps the shipped default described above, `true`
+allows everything for every table — a back office turns the layer on once here
+instead of on every table — `false` allows nothing, and a map mixes:
+
+```php
+'gestures' => ['keyboard' => true, 'drag_select' => false],
+```
+
+Capability keys are matched loosely — `drag_select`, `drag-select`, `dragSelect`
+and `dragselect` are the same key. An **unknown** key throws
+`TableConfigurationException` rather than doing nothing quietly, because a typo
+in a permission is the kind of mistake that only shows up as "why doesn't this
+work" six months later.
+
+A per-table `->gestures(...)` always wins over the config default.
+
+## It is off on the server too
+
+Switching a capability off is not a matter of the client ignoring events. The
+markup and the endpoints go with it:
+
+- The delegated Alpine controllers are not rendered. A table with nothing but the
+ gestures off renders no controller at all, and its asset bundles are not
+ requested.
+- The table stops being an ARIA `grid`: no `role="grid"`, no `role="row"`, no
+ roving `tabindex`.
+- The rows are not focusable, so nothing steals focus on click.
+- The fill endpoint refuses. `fillHandle` off closes `fillTableCells` server-side,
+ not just the handle in the UI.
+- The shortcut legend drops the rows it no longer applies to — with ranges off,
+ `Shift`+arrow is not listed in the `?` help, because it does not work.
+
+That last point is the general rule: the legend is generated from what the table
+actually does, so it can never drift from reality.
+
+## Phones get buttons instead
+
+A gesture-driven table is a desktop idea. There is no double click on a phone,
+no right click, and no hover to discover either of them — so a record action
+that is behaviour-only on the desktop would be **unreachable** on a stacked
+mobile card.
+
+It is therefore rendered as an ordinary button there, and only there:
+
+```php
+->recordAction(RecordAction::make(Action::make('open'))->onDoubleClick())
+```
+
+| Surface | What the user gets |
+|---------|--------------------|
+| Desktop | A double-click gesture. No column, no button. |
+| Mobile card | An `Open` button. |
+
+The fallback is careful about not doubling anything:
+
+- Actions already in `->actions()` keep their order, and the record actions are
+ appended after them.
+- `recordAction('edit')`, which only *references* an action already declared in
+ `->actions()`, shows one button — not the same one twice.
+- An action promoted into the column with `->alsoInRowActions()` is already a
+ button, so it is left alone.
+- The fallback buttons count towards `->collapseActionsOnMobile()`, so a card
+ does not quietly grow past the threshold you set.
+
+Switch it off when a card is meant to stay clean:
+
+```php
+->recordActionButtonsOnMobile(false)
+```
+
+## API reference
+
+Everything the layer exposes, in one place.
+
+### On the table
+
+| Call | What it does |
+|------|--------------|
+| `->gestures()` | Allow every capability. The keyboard is left at "the table decides" |
+| `->gestures(false)` | Allow nothing at all |
+| `->gestures(fn (TableGestures $g) => …)` | Configure this table's capabilities in place |
+| `->gestures(TableGestures $set)` | Adopt a prepared set |
+| `->recordActionButtonsOnMobile(bool)` | Whether behaviour-only record actions render as buttons on a stacked card (default `true`) |
+
+Readers, useful in a custom view or a test:
+
+| Call | Answers |
+|------|---------|
+| `getGestures(): TableGestures` | The raw permissions, before any prerequisite |
+| `usesGridSemantics(): bool` | Is this an ARIA grid? The single owner of that decision |
+| `keyboardNavEnabled(): bool` | Alias of the above, read from the view |
+| `usesRangeSelection(): bool` | Do `Shift`/`mod` clicks and `Shift`+arrows work a range? |
+| `usesDragSelect(): bool` | Does a drag down the checkbox column sweep? |
+| `usesShortcutHelp(): bool` | Does `?` open the legend? |
+| `usesActiveRowMarker(): bool` | Do rows carry the active-row marker? |
+| `mountsRecordActionController(): bool` | Is the delegated Alpine controller rendered at all? |
+| `getGestureConfig(): array` | `['sweep' => bool, 'ranges' => bool]` — what the client controller consumes |
+| `getTableRole(): ?string` | `'grid'` or `null` |
+| `hasRowContextMenu(): bool` | Is there a right-click menu (permission included)? |
+| `isFillHandleEnabled(): bool` | Is the fill handle offered (permission included)? |
+
+### On `TableGestures`
+
+```php
+use NyonCode\WireTable\Support\TableGestures;
+```
+
+| Call | Meaning |
+|------|---------|
+| `TableGestures::defaults()` | The shipped default: keyboard and drag sweep off, the rest allowed |
+| `TableGestures::all()` | Everything allowed; keyboard left at `null` |
+| `TableGestures::none()` | Nothing allowed |
+| `TableGestures::fromConfig($value)` | Build from a config value (`null` / `bool` / map) |
+| `->keyboard(?bool)`, `->rangeSelection(bool)`, `->dragSelect(bool)`, `->contextMenu(bool)`, `->shortcutHelp(bool)`, `->fillHandle(bool)` | The setters; each returns `$this` |
+| `->allowsKeyboard(): ?bool` and `->allows*(): bool` | The permission, *before* the table's own prerequisites |
+| `->toArray(): array` | All six as data |
+
+A permission and an outcome are different questions: `allowsDragSelect()` says
+the table is allowed to sweep, `usesDragSelect()` says it actually does (which
+also needs `selectable()`).
+
+## Recipes
+
+**A public listing.** Nothing to do:
+
+```php
+$table->model(Post::class)->columns([...]);
+```
+
+**A back-office grid.** One call, or `'gestures' => true` in config for the whole
+project:
+
+```php
+$table->gestures()->selectable()->bulkActions([DeleteBulkAction::make()]);
+```
+
+**Click a row to open it, on an otherwise quiet table.** A declared record action
+is outside the layer, so this needs no `gestures()` at all:
+
+```php
+$table->recordAction(RecordAction::make(Action::make('view'))->onClick());
+```
+
+**Keyboard yes, sweeping no.** For long lists where an accidental drag would
+select a hundred rows:
+
+```php
+$table->gestures(fn (TableGestures $g) => $g->keyboard())->selectable();
+```
+
+**A house style across many tables.** Build the set once and hand it over:
+
+```php
+// app/Tables/Gestures.php
+public static function backOffice(): TableGestures
+{
+ return TableGestures::all()->dragSelect(false);
+}
+
+// in each table
+$table->gestures(Gestures::backOffice());
+```
+
+**A table inside a page with its own keyboard handling.** Keep the mouse half:
+
+```php
+$table->gestures(fn (TableGestures $g) => $g->rangeSelection()->dragSelect())->selectable();
+```
+
+**Ranges but no sweep.** `Shift`+click for a block, without a drag that selects
+by accident:
+
+```php
+$table->gestures(fn (TableGestures $g) => $g->rangeSelection())->selectable();
+```
+
+## Troubleshooting
+
+| Symptom | Why | Fix |
+|---------|-----|-----|
+| Arrow keys do nothing, rows are not focusable | The keyboard layer is opt-in | `->gestures()` |
+| `->gestures()` is there and it still is not a grid | Nothing for the arrows to drive — no record actions, no selection | Add `->selectable()`, or force it with `->gestures(fn ($g) => $g->keyboard(true))` |
+| `?` opens nothing | It reads the keyboard layer, and an empty legend renders no modal at all | Turn the keyboard on; check `shortcutLegend()->isEmpty()` |
+| A drag down the checkbox column selects nothing | `dragSelect` is off by default | `->gestures()`, or `->gestures(fn ($g) => $g->dragSelect())` |
+| `Shift`+click toggles one row instead of a range | `rangeSelection` is off by default | `->gestures()`, or `->gestures(fn ($g) => $g->rangeSelection())` |
+| Right-click shows the browser menu | No action is bound to it, or `contextMenu` is off | Bind one with `->onContextMenu()`; check `hasRowContextMenu()` |
+| The fill handle does not appear | It needs `->fillHandle()` **and** the permission **and** a fillable editable column | Check `isFillHandleEnabled()` and `Column::fillable()` |
+| An `->onKey()` binding never fires | Keys are read by the keyboard layer | `->gestures()` |
+| A record action is unreachable on a phone | The fallback was switched off | `->recordActionButtonsOnMobile()` |
+
+## Choosing
+
+| Table | Suggested |
+|-------|-----------|
+| Public listing, marketing page | Nothing to do — the default |
+| Back-office list, keyboard-heavy operators | `->gestures()`, or `'gestures' => true` in config |
+| Public page that must not even right-click | `->gestures(false)` |
+| Read-only report, right click still useful | `TableGestures::none()->contextMenu()` |
+| Long list, keyboard useful, sweeping risky | `->gestures(fn ($g) => $g->keyboard())` |
+| Embedded in a page with its own keyboard handling | `->gestures(fn ($g) => $g->rangeSelection()->dragSelect())` |
+| Selection matters, a stray drag does not | `->gestures(fn ($g) => $g->rangeSelection())` |
+
+## See Also
+
+- [Selecting Rows](selection.md) — what each selection gesture does
+- [Record Actions](record-actions.md) — binding an action to a row gesture
+- [Advanced](advanced.md) — the fill handle
diff --git a/docs/table/overview.md b/docs/table/overview.md
index 4863c70f..35eb5412 100644
--- a/docs/table/overview.md
+++ b/docs/table/overview.md
@@ -686,4 +686,7 @@ class UserTable extends Component
| [Imports](imports.md) | CSV imports — header mapping, casting, per-row validation, updateExisting |
| [Relation Managers](relation-managers.md) | Relationship-scoped tables as standalone Livewire components |
| [Advanced](advanced.md) | Sub-rows, summary footer, polling, lazy loading, caching, debug, responsive |
+| [Selecting Rows](selection.md) | Checkboxes, select-all-matching, and the selection gestures |
+| [Record Actions](record-actions.md) | Whole-row click, double-click, right-click and key bindings |
+| [The Gesture Layer](gestures.md) | `gestures()` — the opt-in keyboard/drag layer, and the mobile button fallback |
| [Actions](../core/actions.md) | Full Action system — modals, forms, wizard steps, lifecycle |
diff --git a/docs/table/record-actions.md b/docs/table/record-actions.md
index 82531bc5..2bba18ea 100644
--- a/docs/table/record-actions.md
+++ b/docs/table/record-actions.md
@@ -91,38 +91,81 @@ Action::make('edit')->onDoubleClick()->alsoInRowActions()
## Keyboard navigation
-When a table has any record action, keyboard navigation turns on automatically
-and the table announces itself as a grid:
+Keyboard navigation is opt-in, with `->gestures()`. Once a table has asked, it
+applies to any table the keyboard can drive row by row — one with record actions,
+and equally one that is `->selectable()` or has bulk actions — and such a table
+announces itself as an ARIA grid:
+
+```php
+->gestures()
+->recordAction(Action::make('open')->onDoubleClick())
+```
| Key | Action |
|-----|--------|
| `↑` / `↓` | Move the active row |
+| `Home` / `End`, `PageUp` / `PageDown` | Jump to an edge, or move by a screenful |
| `Enter` | Primary record action (double-click binding, else click) |
| `Shift` + `Enter` | Secondary record action (the other pointer binding) |
| `Space` | Toggle selection of the active row (and set the range anchor) when selectable, else the primary action |
-| `Shift` + `↑` / `↓` | Extend a contiguous selection range from the anchor (desktop range-select) |
+| `Shift` + `↑` / `↓` | Extend a selection range from the anchor |
| `mod` + `A` | Select every row on the page |
-| Menu key | Open the row context menu |
+| Menu key, `Shift` + `F10` | Open the row context menu |
+| `?` | Show the shortcuts this table answers to |
| `Delete`, `mod+d`, … | Any record action's own `->onKey()` / `->keyboardShortcut()` |
-Keyboard selection drives the **same** selection state as the checkboxes and the
-bulk-action bar — arrow to a row, `Space` to select it, `Shift`+arrow to extend a
-block — then run the bulk action from the bar.
+A `->onKey('Delete')` binding also answers to `Backspace`, which is the same key
+under a different name on a Mac keyboard.
+
+The selection gestures — `Space`, the ranges, `mod`+`A` — are covered in
+[Selecting Rows](selection.md), along with the mouse ones and what a range means
+when "all matching" is selected.
+
+Pointer and keyboard share one active row: **clicking a row marks it** and the
+arrows continue from there, so a table is never navigated from two places at
+once. The marker stays visible while the pointer hovers the row it marks, it
+survives the roundtrip an action triggers, and it follows its record through a
+re-sort (when the record leaves the page entirely, the tabstop falls back to the
+first row).
-Force it off (or on) if you need to:
+Keys only reach the grid when a **row itself** has the focus: a keystroke inside
+a row action button, an inline-editable cell or a dropdown belongs to that
+element. While an action modal is open the grid is inert — no arrow moves the
+marker behind the dialog and no shortcut fires a second action — and closing the
+modal hands the focus back to the active row, so the arrows keep working.
+
+Force it off (or on) if you need to — the keyboard is one capability of the
+[gesture layer](gestures.md):
```php
-->recordActionKeyboard(false)
+->gestures(fn (TableGestures $g) => $g->keyboard(false))
```
Because Enter always reaches the primary action, every record action stays
keyboard-accessible — a behaviour-only action is never a mouse-only trap.
+### Keys the grid reserves
+
+The keys the grid navigates with cannot be bound to an action — the binding
+would never fire. Rather than dropping it silently, `->onKey()` throws at
+configuration time:
+
+```text
+Enter Space ArrowUp ArrowDown Home End PageUp PageDown ContextMenu F10 ?
+```
+
+A `keyboardShortcut()` stamped on the action itself is only skipped, never fatal,
+since that action may legitimately serve a toolbar or a palette as well.
+
## Combining with selection and bulk actions
-When the table is `->selectable()`, a single click still selects the row, so the
-default record-action trigger becomes **double-click** — the two never fight.
-Clicking a checkbox only toggles selection, and bulk actions are untouched:
+When the table is `->selectable()`, the default record-action trigger becomes
+**double-click**, so a single click stays free for working with the selection —
+it only marks the row it lands on (the active row for the keyboard and the anchor
+of the next `Shift`+range). A plain click never ticks the checkbox; the modified
+ones deliberately do, because that is what `Shift` and `mod` mean everywhere else
+(see [Selecting Rows](selection.md)). A modified click is a selection gesture and
+never runs a bound record action. Bulk actions are untouched:
```php
->selectable()
@@ -139,7 +182,20 @@ tint it for a stronger "this row is clickable" hint:
```php
->recordActionHover('primary') // colored hover instead of neutral gray
-->activeRowClass('bg-amber-100') // override the keyboard-active row highlight
+->activeRowClass('bg-amber-100') // override the active-row marker (click + keyboard)
+```
+
+The active row drops its hover tint while it is marked, so the marker is never
+painted over by `hover:bg-*` when the pointer rests on it.
+
+By default the marker is two signals, not one: a background tint and a stripe
+down the row's leading edge. The tint on its own measures about 1.1:1 against a
+plain row — below the 3:1 contrast floor, and invisible to a reader who cannot
+separate the two hues. `activeRowClass()` replaces **both** halves, so an
+override owns its own contrast:
+
+```php
+->activeRowClass('bg-amber-100 [&>td:first-of-type]:before:bg-amber-600')
```
## Recommended UX
@@ -178,3 +234,11 @@ instead:
Action::make('delete')->onContextMenu(),
])
```
+
+## Related docs
+
+- [Selecting Rows](selection.md) — the selection gestures record actions share
+ the row with
+- [Actions](actions.md) — row, bulk and header actions
+- [The Gesture Layer](gestures.md) — switching the gestures off, and the mobile
+ button fallback
diff --git a/docs/table/selection.md b/docs/table/selection.md
new file mode 100644
index 00000000..cc5678bb
--- /dev/null
+++ b/docs/table/selection.md
@@ -0,0 +1,198 @@
+---
+order: 47
+---
+
+# Selecting Rows
+
+Selection can be a gesture surface, not just a column of checkboxes. A table that
+is `->selectable()` — or that merely has `->bulkActions()`, which implies it —
+gives you the checkboxes, the select-all controls and the bulk bar:
+
+```php
+->selectable()
+->bulkActions([DeleteBulkAction::make()])
+```
+
+Add `->gestures()` and it behaves like a list in a desktop file manager as well:
+`Shift`+click takes a range, `mod`+click adds one row, a drag down the checkbox
+column sweeps a block in, and from the keyboard the arrows walk the rows,
+`Space` toggles, `Shift`+arrows extend and `mod`+`A` takes the page.
+
+```php
+->gestures()
+->selectable()
+```
+
+That split is deliberate: keyboard navigation, range selection and the drag
+sweep all change how the table answers someone who never meant to operate it, so
+they wait to be asked (see [The Gesture Layer](gestures.md)). Everything below is
+marked with what it needs.
+
+## What the mouse does
+
+| Gesture | Result | Needs |
+|---------|--------|-------|
+| Click the selection cell | Toggle that row, and set the range anchor | — |
+| `Shift` + click | Select the range between the anchor and this row | `gestures()` |
+| `mod` + click | Toggle this one row, anywhere on it, and anchor here | `gestures()` |
+| `mod` + `Shift` + click | Add the whole block to what is already selected | `gestures()` |
+| Drag down the checkbox column | Sweep a run of rows into the selection | `gestures()` |
+| Click the row itself | Marks the row (see below) — it never ticks the checkbox | — |
+
+The **whole selection cell** is the target, not just the 16-pixel box inside it:
+the box alone is under every touch-target guideline and leaves most of the cell
+dead. A click in the cell can never reach a record action bound to the row.
+
+A plain click on the row *body* marks the row — it becomes the active row for
+the keyboard, and the anchor for the next range — but never selects it.
+Selection stays deliberate. `mod`+click is the exception, and that is what the
+modifier is for.
+
+## What the keyboard does
+
+Everything in this section needs `->gestures()` — see
+[The Gesture Layer](gestures.md).
+
+| Key | Result |
+|-----|--------|
+| `↑` / `↓` | Move the active row |
+| `Home` / `End` | Jump to the first / last row on the page |
+| `PageUp` / `PageDown` | Move by one screenful |
+| `Space` | Toggle the active row, and anchor here |
+| `Shift` + `↑` / `↓` | Grow or shrink the range from the anchor |
+| `Shift` + `Home` / `End` | Extend the range to the first / last row |
+| `mod` + `Shift` + `↑` / `↓` | The same as `Shift`+`Home` / `End` |
+| `mod` + `A` | Select every row on the page |
+| `?` | Show the shortcuts this table answers to |
+
+Keyboard selection drives the **same** state as the checkboxes and the bulk bar:
+arrow to a row, `Space` to select, `Shift`+arrow to extend, then run the bulk
+action.
+
+Keys only reach the table when a **row itself** has the focus. A keystroke inside
+a row — an action button, an inline-editable cell, a dropdown — belongs to that
+element, so `Space` typed into a cell stays a space and `?` typed into the search
+box does not open the help.
+
+## How ranges behave
+
+Every range grows from an **anchor**: the row you last picked with `Space`, with
+a checkbox, or with `mod`+click. The anchor is invisible and one-shot — a plain
+arrow move clears it.
+
+A range writes **what was already selected, plus the range** — not the range
+alone. Select rows 2–6, anchor on row 8, `Shift`+arrow down to 12, and you have
+2–6 and 8–12. Shrinking the range back gives up only the rows the range itself
+added.
+
+When the selection carries no anchor of its own — it came from `mod`+`A`, or from
+the select-all strip — the first `Shift`+arrow grows from the far edge of the
+contiguous block you are standing in. That way it *shrinks or grows the block you
+can see* instead of throwing the rest of the selection away.
+
+To drop individual rows out of a selection, arrow to each one and press `Space`,
+or `mod`+click them.
+
+## Selecting beyond the page
+
+The bulk bar offers **Select all N** once a page is selected, which switches the
+selection from an explicit list of keys to "everything the current filter
+matches" (see [Bulk Actions](actions.md#bulk-actions) for what that
+shape means and why it exists).
+
+Inside that mode the gestures still work, and they read the way you would expect
+of "everything except…":
+
+- `Shift`+arrow over a range **deselects** it, because the stored list is the set
+ of exclusions.
+- `mod`+`A` stands down. Everything is already selected; there is nothing for it
+ to add.
+- The header checkbox edits the exclusions and never silently drops you back to
+ an explicit selection.
+
+## Dragging down the column
+
+Press in the checkbox column and drag: every row you pass is selected, and the
+table scrolls when you reach its edge. The gesture is deliberately narrow.
+
+- **Additive only.** Backing up does not deselect. A sweep can only ever add.
+- **Mouse only.** A finger dragging the checkbox column scrolls the page, as it
+ should.
+- **Only in the checkbox column.** Dragging anywhere else selects text, as it
+ always did.
+- It starts on the first movement that changes rows, so a plain click stays a
+ plain click.
+
+## The shortcut help
+
+Press `?` with a row focused and the table shows exactly what it answers to —
+including any `->onKey()` binding of your own, and its label. The list is built
+from the table's own configuration, so a table without record actions does not
+claim to have any.
+
+The same list is available as data if you want to render it yourself:
+
+```php
+$sections = $table->shortcutLegend()->sections();
+```
+
+Each section has a translated `heading` and a list of `ShortcutHint` value
+objects (`->keys`, `->description`, `->labels(mac: true)`).
+
+## Accessibility
+
+Selection is not a mouse-only feature, and the table says so:
+
+- The table is an ARIA **grid**: `aria-rowcount`, `aria-multiselectable`, and an
+ `aria-rowindex` on every row counted through the whole result set — so row 1 of
+ page 2 announces as row 12, not row 1 again.
+- Every row reports `aria-selected`, kept in step with the live selection rather
+ than the last server response.
+- Selection changes are announced in a polite live region: *"3 of 40 selected"*,
+ *"All 40 selected"*, *"Selection cleared"*.
+- The active row is marked by a background tint **and** a stripe down its leading
+ edge. Colour alone would fail anyone who cannot separate the two hues, and the
+ tint alone measures about 1.1:1 — under the 3:1 contrast floor. The stripe
+ clears it in both light and dark.
+
+Override the marker if it clashes with your design — an override replaces both
+halves, so it owns its own contrast:
+
+```php
+->activeRowClass('bg-amber-100 [&>td:first-of-type]:before:bg-amber-600')
+```
+
+## Keys the table reserves
+
+The grid owns the keys it navigates with, so binding a record action to one of
+them would be dead code. `->onKey()` refuses at configuration time rather than
+silently dropping the binding:
+
+```text
+Enter Space ArrowUp ArrowDown Home End PageUp PageDown ContextMenu F10 ?
+```
+
+`Backspace` is deliberately **not** reserved: it acts as a platform alias of
+`Delete`, so a `->onKey('Delete')` binding answers to both, and an explicit
+`->onKey('Backspace')` stays valid.
+
+## Turning it back off
+
+A table that asked for the gesture layer can hand back one capability at a time,
+or the lot:
+
+```php
+->gestures(fn (TableGestures $g) => $g->dragSelect(false)) // keep the keyboard, drop the sweep
+->gestures(fn (TableGestures $g) => $g->keyboard(false)) // the other way round
+->gestures(false) // every gesture, ranges included
+```
+
+The checkboxes keep working in every one of those — see
+[The Gesture Layer](gestures.md).
+
+## Related docs
+
+- [Record Actions](record-actions.md) — whole-row click, double-click and
+ context-menu bindings
+- [Bulk Actions](actions.md#bulk-actions) — acting on a selection
+- [The Gesture Layer](gestures.md) — switching these gestures off, whole or in part
diff --git a/docs/upgrade.md b/docs/upgrade.md
index e803523a..44dd4330 100644
--- a/docs/upgrade.md
+++ b/docs/upgrade.md
@@ -78,6 +78,84 @@ Confirm your app meets these before upgrading.
---
+## Selection and keyboard gestures
+
+A table's selection grew from a column of checkboxes into a full gesture surface
+(see [Selecting Rows](table/selection.md)). Four things to check on the way up.
+
+**1. Every row gesture is opt-in — `->gestures()`.** The selection grew a full
+gesture surface: `Shift`/`mod` clicks for ranges, a drag down the checkbox column
+that sweeps a block in, and from the keyboard the arrows, `Space`,
+`Shift`+arrows and `mod`+`A`. None of it is on unless a table asks, because each
+changes how the table answers a visitor who never meant to operate it — the rows
+go into the tab order, an active row is marked, a drag starts selecting, and a
+modified click stops meaning a click.
+
+Add one call to the tables that want it:
+
+```php
+->gestures()
+->selectable()
+```
+
+or, for a project where every table is a back-office table:
+
+```php
+// config/wire-table.php
+'defaults' => ['gestures' => true],
+```
+
+What is *not* affected: the checkboxes, both select-all controls and the bulk bar
+work with no change on your side, and a table that never asked mounts no
+delegated controller at all. So do the right-click row menu and the fill handle,
+each of which you already had to ask for. See [The Gesture Layer](table/gestures.md) for the six capabilities and how
+to mix them.
+
+**2. `->onKey()` on a navigation key now throws.** It used to be dropped
+silently, so the action simply never fired. If a table binds one of these, the
+binding was already dead code — rebind it to a free key:
+
+```text
+Enter Space ArrowUp ArrowDown Home End PageUp PageDown ContextMenu F10 ?
+```
+
+`Backspace` stays available, and now doubles as an alias of `Delete`.
+
+**3. Range gestures no longer leave "all matching" mode.** When a selection is
+"everything the filter matches", the stored list is the set of *exclusions* — so
+a `Shift`+arrow range over it now **deselects** that range instead of collapsing
+the whole selection down to one page. If your code reads the selection directly,
+note that `getSelectedRecordKeys()` returns `[]` in that mode by design; use
+`selectedRecordsQuery()` or `eachSelectedRecord()` instead.
+
+**4. Republish the table view if you have overridden it.** The gestures need
+markup the packaged JavaScript looks for, and a published copy of
+`resources/views/vendor/wire-table/tables/index.blade.php` will not have it. The
+view carries a contract marker so a stale copy fails loudly in the browser
+console rather than selecting the wrong rows in silence:
+
+```bash
+php artisan vendor:publish --tag=wire-table::views --force
+```
+
+Re-apply your customisations on top of the new file. If you overrode the view
+only to restyle it, [Theming](theming.md) is usually the smaller path.
+
+**5. Behaviour-only record actions now render as buttons on a mobile card.** A
+phone has no double click, no right click and no hover to discover either, so an
+action bound only to a gesture used to be unreachable once the table stacked.
+It is now rendered as an ordinary button on the card — and only there; the
+desktop table is unchanged. Nothing is doubled: an action already in
+`->actions()`, or one promoted with `->alsoInRowActions()`, still yields exactly
+one button, and the fallback buttons count towards
+`->collapseActionsOnMobile()`. Opt out per table:
+
+```php
+->recordActionButtonsOnMobile(false)
+```
+
+---
+
## Finding Breaking Changes
`CHANGELOG.md` is the source of truth. Breaking changes are called out under a
diff --git a/package-lock.json b/package-lock.json
index 42933778..75f6b3c0 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -7,26 +7,26 @@
"name": "wire-preview-workbench",
"hasInstallScript": true,
"dependencies": {
- "@floating-ui/dom": "^1.8.0",
- "@tiptap/core": "^2.0",
- "@tiptap/extension-character-count": "^2.0",
- "@tiptap/extension-highlight": "^2.0",
- "@tiptap/extension-image": "^2.0",
- "@tiptap/extension-link": "^2.0",
- "@tiptap/extension-placeholder": "^2.0",
- "@tiptap/extension-table": "^2.0",
- "@tiptap/extension-table-cell": "^2.0",
- "@tiptap/extension-table-header": "^2.0",
- "@tiptap/extension-table-row": "^2.0",
- "@tiptap/extension-text-align": "^2.0",
- "@tiptap/extension-underline": "^2.0",
- "@tiptap/starter-kit": "^2.0"
+ "@floating-ui/dom": "^1.8",
+ "@tiptap/core": "^3.28",
+ "@tiptap/extension-character-count": "^3.28",
+ "@tiptap/extension-highlight": "^3.28",
+ "@tiptap/extension-image": "^3.28",
+ "@tiptap/extension-link": "^3.28",
+ "@tiptap/extension-placeholder": "^3.28",
+ "@tiptap/extension-table": "^3.28",
+ "@tiptap/extension-table-cell": "^3.28",
+ "@tiptap/extension-table-header": "^3.28",
+ "@tiptap/extension-table-row": "^3.28",
+ "@tiptap/extension-text-align": "^3.28",
+ "@tiptap/extension-underline": "^3.28",
+ "@tiptap/starter-kit": "^3.28"
},
"devDependencies": {
"@tailwindcss/forms": "^0.5.10",
- "@tailwindcss/vite": "^4.3.3",
+ "@tailwindcss/vite": "^4.1.8",
"@torchlight-api/torchlight-cli": "0.1.7",
- "esbuild": "^0.28.1",
+ "esbuild": "^0.28",
"laravel-vite-plugin": "^3.1.3",
"patch-package": "^8.0.1",
"tailwindcss": "^4.1.8",
@@ -557,9 +557,9 @@
}
},
"node_modules/@inquirer/external-editor/node_modules/iconv-lite": {
- "version": "0.7.2",
- "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz",
- "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==",
+ "version": "0.7.3",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.3.tgz",
+ "integrity": "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -652,12 +652,6 @@
"url": "https://github.com/sponsors/Boshen"
}
},
- "node_modules/@remirror/core-constants": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/@remirror/core-constants/-/core-constants-3.0.0.tgz",
- "integrity": "sha512-42aWfPrimMfDKDi4YegyS7x+/0tlzaqwPQCULLanv3DMIlu96KTJR0fM5isWX2UViOqlGnX6YFgqWepcX+XMNg==",
- "license": "MIT"
- },
"node_modules/@rolldown/binding-android-arm64": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.5.tgz",
@@ -1188,72 +1182,6 @@
"node": ">=14.0.0"
}
},
- "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core": {
- "version": "1.11.1",
- "dev": true,
- "inBundle": true,
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "@emnapi/wasi-threads": "1.2.2",
- "tslib": "^2.4.0"
- }
- },
- "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime": {
- "version": "1.11.1",
- "dev": true,
- "inBundle": true,
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "tslib": "^2.4.0"
- }
- },
- "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads": {
- "version": "1.2.2",
- "dev": true,
- "inBundle": true,
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "tslib": "^2.4.0"
- }
- },
- "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": {
- "version": "1.1.4",
- "dev": true,
- "inBundle": true,
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "@tybys/wasm-util": "^0.10.1"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/Brooooooklyn"
- },
- "peerDependencies": {
- "@emnapi/core": "^1.7.1",
- "@emnapi/runtime": "^1.7.1"
- }
- },
- "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util": {
- "version": "0.10.2",
- "dev": true,
- "inBundle": true,
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "tslib": "^2.4.0"
- }
- },
- "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib": {
- "version": "2.8.1",
- "dev": true,
- "inBundle": true,
- "license": "0BSD",
- "optional": true
- },
"node_modules/@tailwindcss/oxide-win32-arm64-msvc": {
"version": "4.3.3",
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.3.tgz",
@@ -1304,75 +1232,75 @@
}
},
"node_modules/@tiptap/core": {
- "version": "2.27.2",
- "resolved": "https://registry.npmjs.org/@tiptap/core/-/core-2.27.2.tgz",
- "integrity": "sha512-ABL1N6eoxzDzC1bYvkMbvyexHacszsKdVPYqhl5GwHLOvpZcv9VE9QaKwDILTyz5voCA0lGcAAXZp+qnXOk5lQ==",
+ "version": "3.29.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/core/-/core-3.29.0.tgz",
+ "integrity": "sha512-A/lrhKpOYtl0V5pmPS00Zps8pgBe1qDOoD9fzsumDSZ3HP8W398C959Jgru75PNFokAya9COPD3iaKP3NWF25g==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
- "@tiptap/pm": "^2.7.0"
+ "@tiptap/pm": "3.29.0"
}
},
"node_modules/@tiptap/extension-blockquote": {
- "version": "2.27.2",
- "resolved": "https://registry.npmjs.org/@tiptap/extension-blockquote/-/extension-blockquote-2.27.2.tgz",
- "integrity": "sha512-oIGZgiAeA4tG3YxbTDfrmENL4/CIwGuP3THtHsNhwRqwsl9SfMk58Ucopi2GXTQSdYXpRJ0ahE6nPqB5D6j/Zw==",
+ "version": "3.29.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-blockquote/-/extension-blockquote-3.29.0.tgz",
+ "integrity": "sha512-k8NKHLEYOqre7guObZBeFM04CaTwmEceCmdIrIjd7H9KoHSbRODaBNved6j5G33x/FWMwIF/RQbE2BjWaFSgzA==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
- "@tiptap/core": "^2.7.0"
+ "@tiptap/core": "3.29.0",
+ "@tiptap/pm": "3.29.0"
}
},
"node_modules/@tiptap/extension-bold": {
- "version": "2.27.2",
- "resolved": "https://registry.npmjs.org/@tiptap/extension-bold/-/extension-bold-2.27.2.tgz",
- "integrity": "sha512-bR7J5IwjCGQ0s3CIxyMvOCnMFMzIvsc5OVZKscTN5UkXzFsaY6muUAIqtKxayBUucjtUskm5qZowJITCeCb1/A==",
+ "version": "3.29.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-bold/-/extension-bold-3.29.0.tgz",
+ "integrity": "sha512-BPUJvJ9sCsU3fxao5UJfDJrDuHhMn2hczZwZ9Qs4w1vkAeLGyOYbSejUTJdBYHLoexGiEwgnEeVeZN3C8GaI5g==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
- "@tiptap/core": "^2.7.0"
+ "@tiptap/core": "3.29.0"
}
},
"node_modules/@tiptap/extension-bullet-list": {
- "version": "2.27.2",
- "resolved": "https://registry.npmjs.org/@tiptap/extension-bullet-list/-/extension-bullet-list-2.27.2.tgz",
- "integrity": "sha512-gmFuKi97u5f8uFc/GQs+zmezjiulZmFiDYTh3trVoLRoc2SAHOjGEB7qxdx7dsqmMN7gwiAWAEVurLKIi1lnnw==",
+ "version": "3.29.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-bullet-list/-/extension-bullet-list-3.29.0.tgz",
+ "integrity": "sha512-1045sC5CRn7KD0wluxZeksBwpm2k3LKBUOM3kNwExHAPSV4in4gdarYJ2/WCmIKsmyj2FzUcF4rbpLLSB/dBSg==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
- "@tiptap/core": "^2.7.0"
+ "@tiptap/extension-list": "3.29.0"
}
},
"node_modules/@tiptap/extension-character-count": {
- "version": "2.27.2",
- "resolved": "https://registry.npmjs.org/@tiptap/extension-character-count/-/extension-character-count-2.27.2.tgz",
- "integrity": "sha512-EcQRIvbLbMDDzo7uFqXYgh1CfgedS9sYX4BllktY2OlXLPdNpwo9t8WMK/a7soESNv0Le3WZ5pNvnNhv7Z2YdA==",
+ "version": "3.29.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-character-count/-/extension-character-count-3.29.0.tgz",
+ "integrity": "sha512-mX29NypWoHlfYfVaaFzafaWFPgx3I7BSy3OCgk3VUXOwCmzgGjW5K6pPM4o/bHmonbsxibVALtSUdcRVuaj8QQ==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
- "@tiptap/core": "^2.7.0",
- "@tiptap/pm": "^2.7.0"
+ "@tiptap/extensions": "3.29.0"
}
},
"node_modules/@tiptap/extension-code": {
- "version": "2.27.2",
- "resolved": "https://registry.npmjs.org/@tiptap/extension-code/-/extension-code-2.27.2.tgz",
- "integrity": "sha512-7X9AgwqiIGXoZX7uvdHQsGsjILnN/JaEVtqfXZnPECzKGaWHeK/Ao4sYvIIIffsyZJA8k5DC7ny2/0sAgr2TuA==",
+ "version": "3.29.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-code/-/extension-code-3.29.0.tgz",
+ "integrity": "sha512-Um0BlyunRJ8Fal288Jn7I2n15FRwQcTy3NnKBBrPz3ikwx+yZZ0n9xTozpLMSxXrTq6cRlxLkASp/9FFx9sX9A==",
"license": "MIT",
"funding": {
"type": "github",
@@ -1392,356 +1320,362 @@
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
- "@tiptap/core": "^2.7.0",
- "@tiptap/pm": "^2.7.0"
+ "@tiptap/core": "3.29.0",
+ "@tiptap/pm": "3.29.0"
}
},
"node_modules/@tiptap/extension-document": {
- "version": "2.27.2",
- "resolved": "https://registry.npmjs.org/@tiptap/extension-document/-/extension-document-2.27.2.tgz",
- "integrity": "sha512-CFhAYsPnyYnosDC4639sCJnBUnYH4Cat9qH5NZWHVvdgtDwu8GZgZn2eSzaKSYXWH1vJ9DSlCK+7UyC3SNXIBA==",
+ "version": "3.29.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-document/-/extension-document-3.29.0.tgz",
+ "integrity": "sha512-folTlrwmUL+WSnojvfjZ951Gb25Pw8BxjXwhvXFuZXJ92qMfOcezA480kwFuu5nG8pPc/76ezQlz9a86Y5uQJw==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
- "@tiptap/core": "^2.7.0"
+ "@tiptap/core": "3.29.0"
}
},
"node_modules/@tiptap/extension-dropcursor": {
- "version": "2.27.2",
- "resolved": "https://registry.npmjs.org/@tiptap/extension-dropcursor/-/extension-dropcursor-2.27.2.tgz",
- "integrity": "sha512-oEu/OrktNoQXq1x29NnH/GOIzQZm8ieTQl3FK27nxfBPA89cNoH4mFEUmBL5/OFIENIjiYG3qWpg6voIqzswNw==",
+ "version": "3.29.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-dropcursor/-/extension-dropcursor-3.29.0.tgz",
+ "integrity": "sha512-IAY21q7KSXyCXN3u13kfpq0E8D2i3r9nSbsNYlLzkEv0Bpn+pWShZi05A0ubuothDNjdaMa96xaRY0f1y9gDZw==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
- "@tiptap/core": "^2.7.0",
- "@tiptap/pm": "^2.7.0"
+ "@tiptap/extensions": "3.29.0"
}
},
"node_modules/@tiptap/extension-gapcursor": {
- "version": "2.27.2",
- "resolved": "https://registry.npmjs.org/@tiptap/extension-gapcursor/-/extension-gapcursor-2.27.2.tgz",
- "integrity": "sha512-/c9VF1HBxj+AP54XGVgCmD9bEGYc5w5OofYCFQgM7l7PB1J00A4vOke0oPkHJnqnOOyPlFaxO/7N6l3XwFcnKA==",
+ "version": "3.29.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-gapcursor/-/extension-gapcursor-3.29.0.tgz",
+ "integrity": "sha512-Jz0zwriGxPMuRjKo6x7WhPa/7pZn0dovyJu/GLarN4T+mMseGNXWwEBsLEKkuumsJVPDEPPFjX+rzEeaFKInPQ==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
- "@tiptap/core": "^2.7.0",
- "@tiptap/pm": "^2.7.0"
+ "@tiptap/extensions": "3.29.0"
}
},
"node_modules/@tiptap/extension-hard-break": {
- "version": "2.27.2",
- "resolved": "https://registry.npmjs.org/@tiptap/extension-hard-break/-/extension-hard-break-2.27.2.tgz",
- "integrity": "sha512-kSRVGKlCYK6AGR0h8xRkk0WOFGXHIIndod3GKgWU49APuIGDiXd8sziXsSlniUsWmqgDmDXcNnSzPcV7AQ8YNg==",
+ "version": "3.29.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-hard-break/-/extension-hard-break-3.29.0.tgz",
+ "integrity": "sha512-8qKj0oeuU7IYoG73Lj3W4EC/JS/dFjN5iHmc4yodqppP6qs0o2zsF/2ipceGN/REZq5megPtxRgXu/nxQZ1NYg==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
- "@tiptap/core": "^2.7.0"
+ "@tiptap/core": "3.29.0"
}
},
"node_modules/@tiptap/extension-heading": {
- "version": "2.27.2",
- "resolved": "https://registry.npmjs.org/@tiptap/extension-heading/-/extension-heading-2.27.2.tgz",
- "integrity": "sha512-iM3yeRWuuQR/IRQ1djwNooJGfn9Jts9zF43qZIUf+U2NY8IlvdNsk2wTOdBgh6E0CamrStPxYGuln3ZS4fuglw==",
+ "version": "3.29.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-heading/-/extension-heading-3.29.0.tgz",
+ "integrity": "sha512-xWb3QEKo7cp9u6Vzi6oYKODra6jPwziHBv3fJAnxWpRldxtJqfyOVMikWJJFuDjmr+K0qyegzf206pMk+y0bDg==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
- "@tiptap/core": "^2.7.0"
+ "@tiptap/core": "3.29.0"
}
},
"node_modules/@tiptap/extension-highlight": {
- "version": "2.27.2",
- "resolved": "https://registry.npmjs.org/@tiptap/extension-highlight/-/extension-highlight-2.27.2.tgz",
- "integrity": "sha512-ZjlktDdMjruMJFAVz0TbQf0v92Jqkc7Ri1iZJqBXuLid+r+GxUzl2CVAV7qq5yagkGQgvAG+WGsMk880HgR3MA==",
+ "version": "3.29.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-highlight/-/extension-highlight-3.29.0.tgz",
+ "integrity": "sha512-4Gcqn8Sg8kAKe+cEHzdXijalV803WUjvloZvlqrTAJmcORFNOipc+r9ylSEmCLb6j0oLc1O7tJXIEX59khNX/A==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
- "@tiptap/core": "^2.7.0"
+ "@tiptap/core": "3.29.0"
}
},
- "node_modules/@tiptap/extension-history": {
- "version": "2.27.2",
- "resolved": "https://registry.npmjs.org/@tiptap/extension-history/-/extension-history-2.27.2.tgz",
- "integrity": "sha512-+hSyqERoFNTWPiZx4/FCyZ/0eFqB9fuMdTB4AC/q9iwu3RNWAQtlsJg5230bf/qmyO6bZxRUc0k8p4hrV6ybAw==",
+ "node_modules/@tiptap/extension-horizontal-rule": {
+ "version": "3.29.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-3.29.0.tgz",
+ "integrity": "sha512-u1OgncXkokIuUJQIh4jVQfYHL/6I8VnrokUcCud4eErWvgeLnCbNmVdih4kxIdfmvEyAxXjDwcqf4AB79TxuNg==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
- "@tiptap/core": "^2.7.0",
- "@tiptap/pm": "^2.7.0"
+ "@tiptap/core": "3.29.0",
+ "@tiptap/pm": "3.29.0"
}
},
- "node_modules/@tiptap/extension-horizontal-rule": {
- "version": "2.27.2",
- "resolved": "https://registry.npmjs.org/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-2.27.2.tgz",
- "integrity": "sha512-WGWUSgX+jCsbtf9Y9OCUUgRZYuwjVoieW5n6mAUohJ9/6gc6sGIOrUpBShf+HHo6WD+gtQjRd+PssmX3NPWMpg==",
+ "node_modules/@tiptap/extension-image": {
+ "version": "3.29.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-image/-/extension-image-3.29.0.tgz",
+ "integrity": "sha512-A+9Oxsobh4IRWpYj8+y4c0ujhZPY0z9PqvzyuV5GCqiUTRcTRnFI4a4PZDXO/GcCCQXfznfu8gbMtaEyEQW5+w==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
- "@tiptap/core": "^2.7.0",
- "@tiptap/pm": "^2.7.0"
+ "@tiptap/core": "3.29.0"
}
},
- "node_modules/@tiptap/extension-image": {
- "version": "2.27.2",
- "resolved": "https://registry.npmjs.org/@tiptap/extension-image/-/extension-image-2.27.2.tgz",
- "integrity": "sha512-5zL/BY41FIt72azVrCrv3n+2YJ/JyO8wxCcA4Dk1eXIobcgVyIdo4rG39gCqIOiqziAsqnqoj12QHTBtHsJ6mQ==",
+ "node_modules/@tiptap/extension-italic": {
+ "version": "3.29.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-italic/-/extension-italic-3.29.0.tgz",
+ "integrity": "sha512-c8gF+zM7yXB9iGxtVCioYecjQ+A2fLlNa1XYBYbTobNhbUo8a11akbTkGNcCIgttwnzwEoI4mB6/PK4yjaQvVg==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
- "@tiptap/core": "^2.7.0"
+ "@tiptap/core": "3.29.0"
}
},
- "node_modules/@tiptap/extension-italic": {
- "version": "2.27.2",
- "resolved": "https://registry.npmjs.org/@tiptap/extension-italic/-/extension-italic-2.27.2.tgz",
- "integrity": "sha512-1OFsw2SZqfaqx5Fa5v90iNlPRcqyt+lVSjBwTDzuPxTPFY4Q0mL89mKgkq2gVHYNCiaRkXvFLDxaSvBWbmthgg==",
+ "node_modules/@tiptap/extension-link": {
+ "version": "3.29.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-link/-/extension-link-3.29.0.tgz",
+ "integrity": "sha512-FuLDgE0k1dB4WCBTelw3re9BLmiurgXVSj3d3J4l3sunljyOs941RUU62b7pWP+ePNdGF7RgUMmMAB+58VZCIA==",
"license": "MIT",
+ "dependencies": {
+ "linkifyjs": "^4.3.3"
+ },
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
- "@tiptap/core": "^2.7.0"
+ "@tiptap/core": "3.29.0",
+ "@tiptap/pm": "3.29.0"
}
},
- "node_modules/@tiptap/extension-link": {
- "version": "2.27.2",
- "resolved": "https://registry.npmjs.org/@tiptap/extension-link/-/extension-link-2.27.2.tgz",
- "integrity": "sha512-bnP61qkr0Kj9Cgnop1hxn2zbOCBzNtmawxr92bVTOE31fJv6FhtCnQiD6tuPQVGMYhcmAj7eihtvuEMFfqEPcQ==",
+ "node_modules/@tiptap/extension-list": {
+ "version": "3.29.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-list/-/extension-list-3.29.0.tgz",
+ "integrity": "sha512-k0B/+nIkn4VvHSQ0kP+AzzAmgeOVxKMAdqG4a6qwxp/lR12aJGHlOP92KCjXV2RNPtuwDksJ1RIXrgxdf9WmJg==",
"license": "MIT",
- "dependencies": {
- "linkifyjs": "^4.3.2"
- },
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
- "@tiptap/core": "^2.7.0",
- "@tiptap/pm": "^2.7.0"
+ "@tiptap/core": "3.29.0",
+ "@tiptap/pm": "3.29.0"
}
},
"node_modules/@tiptap/extension-list-item": {
- "version": "2.27.2",
- "resolved": "https://registry.npmjs.org/@tiptap/extension-list-item/-/extension-list-item-2.27.2.tgz",
- "integrity": "sha512-eJNee7IEGXMnmygM5SdMGDC8m/lMWmwNGf9fPCK6xk0NxuQRgmZHL6uApKcdH6gyNcRPHCqvTTkhEP7pbny/fg==",
+ "version": "3.29.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-list-item/-/extension-list-item-3.29.0.tgz",
+ "integrity": "sha512-JSz27OIDHWDL7uw28E4W3eaN3H1u+NzJQKZkNXo5Qsvvsx8m/YIQWO9XlhiL3V4TZMjfvlc94lUp5tKW5KwETw==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
- "@tiptap/core": "^2.7.0"
+ "@tiptap/extension-list": "3.29.0"
+ }
+ },
+ "node_modules/@tiptap/extension-list-keymap": {
+ "version": "3.29.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-list-keymap/-/extension-list-keymap-3.29.0.tgz",
+ "integrity": "sha512-g14QLZR9fmfJIL+X2R/cVTvQS0Mp7CoenA9PO+8adTjK7hCeB8hA3UAakMB4ekkElOXNOtb16e0wqulDcoqANg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/extension-list": "3.29.0"
}
},
"node_modules/@tiptap/extension-ordered-list": {
- "version": "2.27.2",
- "resolved": "https://registry.npmjs.org/@tiptap/extension-ordered-list/-/extension-ordered-list-2.27.2.tgz",
- "integrity": "sha512-M7A4tLGJcLPYdLC4CI2Gwl8LOrENQW59u3cMVa+KkwG1hzSJyPsbDpa1DI6oXPC2WtYiTf22zrbq3gVvH+KA2w==",
+ "version": "3.29.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-ordered-list/-/extension-ordered-list-3.29.0.tgz",
+ "integrity": "sha512-9TXUDFagkGeu6Bo8L0b2SAVAYrY2Xzd28MXwiDxYvDKMjEGWkkCUpMiEI5Qav3GnfFWLmS3jI8PgUjxIw9wLLQ==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
- "@tiptap/core": "^2.7.0"
+ "@tiptap/extension-list": "3.29.0"
}
},
"node_modules/@tiptap/extension-paragraph": {
- "version": "2.27.2",
- "resolved": "https://registry.npmjs.org/@tiptap/extension-paragraph/-/extension-paragraph-2.27.2.tgz",
- "integrity": "sha512-elYVn2wHJJ+zB9LESENWOAfI4TNT0jqEN34sMA/hCtA4im1ZG2DdLHwkHIshj/c4H0dzQhmsS/YmNC5Vbqab/A==",
+ "version": "3.29.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-paragraph/-/extension-paragraph-3.29.0.tgz",
+ "integrity": "sha512-OtGrkwzqlW+ehW+d2qrQ7VVcP9PbDeQmuT4Ec4yFjf3vR4QO8OVdVfeOcUq52PtkT3kYRxz9fzEiEvjqRm72yA==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
- "@tiptap/core": "^2.7.0"
+ "@tiptap/core": "3.29.0"
}
},
"node_modules/@tiptap/extension-placeholder": {
- "version": "2.27.2",
- "resolved": "https://registry.npmjs.org/@tiptap/extension-placeholder/-/extension-placeholder-2.27.2.tgz",
- "integrity": "sha512-IjsgSVYJRjpAKmIoapU0E2R4E2FPY3kpvU7/1i7PUYisylqejSJxmtJPGYw0FOMQY9oxnEEvfZHMBA610tqKpg==",
+ "version": "3.29.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-placeholder/-/extension-placeholder-3.29.0.tgz",
+ "integrity": "sha512-V2jb0kL/k3rygAtKmSPYxoyBMCQJtJikPYnxiCYuY7FI1ohmCrmhyhkVqJv0b0EPMNW8BMIqf6DgpKJ/ODmXgA==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
- "@tiptap/core": "^2.7.0",
- "@tiptap/pm": "^2.7.0"
+ "@tiptap/extensions": "3.29.0"
}
},
"node_modules/@tiptap/extension-strike": {
- "version": "2.27.2",
- "resolved": "https://registry.npmjs.org/@tiptap/extension-strike/-/extension-strike-2.27.2.tgz",
- "integrity": "sha512-HHIjhafLhS2lHgfAsCwC1okqMsQzR4/mkGDm4M583Yftyjri1TNA7lzhzXWRFWiiMfJxKtdjHjUAQaHuteRTZw==",
+ "version": "3.29.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-strike/-/extension-strike-3.29.0.tgz",
+ "integrity": "sha512-VzasZkckrEXzmFQT9z3O95rUWy1b0RLFHAC6GUSQHxKk4TpP+Ujfn5+rqmPukMn0eScJyw1Y36jwfJc0VuNR+Q==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
- "@tiptap/core": "^2.7.0"
+ "@tiptap/core": "3.29.0"
}
},
"node_modules/@tiptap/extension-table": {
- "version": "2.27.2",
- "resolved": "https://registry.npmjs.org/@tiptap/extension-table/-/extension-table-2.27.2.tgz",
- "integrity": "sha512-pDbhOpT5phZkcsyPjGBQlXv0+0hmdrvqHJ+dJjkGcCtlfy2pHiEIhmIItOFagc7wXy8G9iUFZ9Jie4zvDf+brg==",
+ "version": "3.29.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-table/-/extension-table-3.29.0.tgz",
+ "integrity": "sha512-EwWZ5XDrAx1Us61eq9Gv5EblLmH14l8Al6X2VpFVI00TTu4qDwlW5wfnTsAkfAU0Ri0LyxLYb3lBfOvC8NV7uw==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
- "@tiptap/core": "^2.7.0",
- "@tiptap/pm": "^2.7.0"
+ "@tiptap/core": "3.29.0",
+ "@tiptap/pm": "3.29.0"
}
},
"node_modules/@tiptap/extension-table-cell": {
- "version": "2.27.2",
- "resolved": "https://registry.npmjs.org/@tiptap/extension-table-cell/-/extension-table-cell-2.27.2.tgz",
- "integrity": "sha512-9Lk46MjZMFzVZfOj9Kd7VgC6Odt6vmEhlCYVumErShUY7EkFqCw3b2IYoUtQkntfOEx/Afnhff/okNQwPsJeUA==",
+ "version": "3.29.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-table-cell/-/extension-table-cell-3.29.0.tgz",
+ "integrity": "sha512-rsp2DlD2LQYFH76h0Asu7ysMhsqaFt7y/kBWqZ1skvBvIzRLOMO0bBH7shQtKT91Mb9y5thiqCj7bmkol28aKw==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
- "@tiptap/core": "^2.7.0"
+ "@tiptap/extension-table": "3.29.0"
}
},
"node_modules/@tiptap/extension-table-header": {
- "version": "2.27.2",
- "resolved": "https://registry.npmjs.org/@tiptap/extension-table-header/-/extension-table-header-2.27.2.tgz",
- "integrity": "sha512-ZEb6lbG0NbbodWLV0b4BS/QrDIPlUbCcuOsUxzqVvlMUY1Vg6Fj6fKwLaBcsIUDHi8sxZDBEgYEDw3BR/zcO6A==",
+ "version": "3.29.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-table-header/-/extension-table-header-3.29.0.tgz",
+ "integrity": "sha512-6eN+zvuDXhc1OedNvejouLKslZSvteGmAMNyd/sRrJIx16Q2ENOGpI5uAintfSRKnrX88XVKMKRp2AGgYo2OJQ==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
- "@tiptap/core": "^2.7.0"
+ "@tiptap/extension-table": "3.29.0"
}
},
"node_modules/@tiptap/extension-table-row": {
- "version": "2.27.2",
- "resolved": "https://registry.npmjs.org/@tiptap/extension-table-row/-/extension-table-row-2.27.2.tgz",
- "integrity": "sha512-Nw9+tA56Y5HtLVP01NGCZSUuTQhJPtfK9OfmDgGgcxynn2cRVdEtj+9FNZqRhQ1iRVaAI+Rd4xRvX9qYePMOxw==",
+ "version": "3.29.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-table-row/-/extension-table-row-3.29.0.tgz",
+ "integrity": "sha512-+pIw0IEwmAeX53UqDFjSLLZBg6X7eKR6+yOKBfJgI7EsclVVjUFXFVrYs1WdqwaIgUgEx/TbgKq7krQc8lVtyw==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
- "@tiptap/core": "^2.7.0"
+ "@tiptap/extension-table": "3.29.0"
}
},
"node_modules/@tiptap/extension-text": {
- "version": "2.27.2",
- "resolved": "https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-2.27.2.tgz",
- "integrity": "sha512-Xk7nYcigljAY0GO9hAQpZ65ZCxqOqaAlTPDFcKerXmlkQZP/8ndx95OgUb1Xf63kmPOh3xypurGS2is3v0MXSA==",
+ "version": "3.29.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-3.29.0.tgz",
+ "integrity": "sha512-AMsf2V7IiGvzbl+LqLLfyWzufazZxHuid1gOnEt3KwolQ9AO7p8WJgHld0PZLxNyEh4SaSqK7b3d3SM9sqrlTw==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
- "@tiptap/core": "^2.7.0"
+ "@tiptap/core": "3.29.0"
}
},
"node_modules/@tiptap/extension-text-align": {
- "version": "2.27.2",
- "resolved": "https://registry.npmjs.org/@tiptap/extension-text-align/-/extension-text-align-2.27.2.tgz",
- "integrity": "sha512-0Pyks6Hu+Q/+9+5/osoSv0SP6jIerdWMYbi13aaZLsJoj3lBj5WNaE11JtAwSFN5sx0IbqhDSlp1zkvRnzgZ8g==",
+ "version": "3.29.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-text-align/-/extension-text-align-3.29.0.tgz",
+ "integrity": "sha512-/1htV3teqRJ7i7trAuORDg62LV0CQ1qskbMcP3SB7grsw6H1G54zwdaJVXeiIXmQKDF7FoZgY7u/v/TJckthEw==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
- "@tiptap/core": "^2.7.0"
+ "@tiptap/core": "3.29.0"
}
},
- "node_modules/@tiptap/extension-text-style": {
- "version": "2.27.2",
- "resolved": "https://registry.npmjs.org/@tiptap/extension-text-style/-/extension-text-style-2.27.2.tgz",
- "integrity": "sha512-Omk+uxjJLyEY69KStpCw5fA9asvV+MGcAX2HOxyISDFoLaL49TMrNjhGAuz09P1L1b0KGXo4ml7Q3v/Lfy4WPA==",
+ "node_modules/@tiptap/extension-underline": {
+ "version": "3.29.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-underline/-/extension-underline-3.29.0.tgz",
+ "integrity": "sha512-DSPTogdvxmoX/L0U5KgBC9jWffRvYkGiyh1tp28gn+QaARh4iJn79pAZpi1m1bEhal/0Mq5m4ukbiO7FWInwaA==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
- "@tiptap/core": "^2.7.0"
+ "@tiptap/core": "3.29.0"
}
},
- "node_modules/@tiptap/extension-underline": {
- "version": "2.27.2",
- "resolved": "https://registry.npmjs.org/@tiptap/extension-underline/-/extension-underline-2.27.2.tgz",
- "integrity": "sha512-gPOsbAcw1S07ezpAISwoO8f0RxpjcSH7VsHEFDVuXm4ODE32nhvSinvHQjv2icRLOXev+bnA7oIBu7Oy859gWQ==",
+ "node_modules/@tiptap/extensions": {
+ "version": "3.29.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/extensions/-/extensions-3.29.0.tgz",
+ "integrity": "sha512-ltlrm8dDHIgeNj3cOLEdLFMPPVy3TYvWA8ftrrJ44C/L01MBmgFB1f/vkPFYYnAasb2BYyVG6HxAGcTQHo5jHw==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
- "@tiptap/core": "^2.7.0"
+ "@tiptap/core": "3.29.0",
+ "@tiptap/pm": "3.29.0"
}
},
"node_modules/@tiptap/pm": {
- "version": "2.27.2",
- "resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-2.27.2.tgz",
- "integrity": "sha512-kaEg7BfiJPDQMKbjVIzEPO3wlcA+pZb2tlcK9gPrdDnEFaec2QTF1sXz2ak2IIb2curvnIrQ4yrfHgLlVA72wA==",
+ "version": "3.29.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-3.29.0.tgz",
+ "integrity": "sha512-4rr3HiZ8kbSNINuWXqKQJLv9fFMypCBN5gOwxoU+D4lEYVblkoM60fA1SlrI2BZVlZQmw2U38XKl5XMtUr55XQ==",
"license": "MIT",
"dependencies": {
- "prosemirror-changeset": "^2.3.0",
- "prosemirror-collab": "^1.3.1",
- "prosemirror-commands": "^1.6.2",
- "prosemirror-dropcursor": "^1.8.1",
- "prosemirror-gapcursor": "^1.3.2",
- "prosemirror-history": "^1.4.1",
- "prosemirror-inputrules": "^1.4.0",
- "prosemirror-keymap": "^1.2.2",
- "prosemirror-markdown": "^1.13.1",
- "prosemirror-menu": "^1.2.4",
- "prosemirror-model": "^1.23.0",
- "prosemirror-schema-basic": "^1.2.3",
- "prosemirror-schema-list": "^1.4.1",
- "prosemirror-state": "^1.4.3",
- "prosemirror-tables": "^1.6.4",
- "prosemirror-trailing-node": "^3.0.0",
- "prosemirror-transform": "^1.10.2",
- "prosemirror-view": "^1.37.0"
+ "prosemirror-changeset": "^2.4.1",
+ "prosemirror-commands": "^1.7.1",
+ "prosemirror-dropcursor": "^1.8.2",
+ "prosemirror-gapcursor": "^1.4.1",
+ "prosemirror-history": "^1.5.0",
+ "prosemirror-inputrules": "^1.5.1",
+ "prosemirror-keymap": "^1.2.3",
+ "prosemirror-model": "^1.25.11",
+ "prosemirror-schema-list": "^1.5.1",
+ "prosemirror-state": "^1.4.4",
+ "prosemirror-tables": "^1.8.5",
+ "prosemirror-transform": "^1.12.0",
+ "prosemirror-view": "^1.41.9"
},
"funding": {
"type": "github",
@@ -1749,32 +1683,35 @@
}
},
"node_modules/@tiptap/starter-kit": {
- "version": "2.27.2",
- "resolved": "https://registry.npmjs.org/@tiptap/starter-kit/-/starter-kit-2.27.2.tgz",
- "integrity": "sha512-bb0gJvPoDuyRUQ/iuN52j1//EtWWttw+RXAv1uJxfR0uKf8X7uAqzaOOgwjknoCIDC97+1YHwpGdnRjpDkOBxw==",
+ "version": "3.29.0",
+ "resolved": "https://registry.npmjs.org/@tiptap/starter-kit/-/starter-kit-3.29.0.tgz",
+ "integrity": "sha512-J3jTp3/WXnnL58TCtdMsfeQ2BeycFYrAib6Nbpjd9G0OLUBE7AILiH2HrobRQpvRbAUgD5SW+sBZYOonbWWqNg==",
"license": "MIT",
"dependencies": {
- "@tiptap/core": "^2.27.2",
- "@tiptap/extension-blockquote": "^2.27.2",
- "@tiptap/extension-bold": "^2.27.2",
- "@tiptap/extension-bullet-list": "^2.27.2",
- "@tiptap/extension-code": "^2.27.2",
- "@tiptap/extension-code-block": "^2.27.2",
- "@tiptap/extension-document": "^2.27.2",
- "@tiptap/extension-dropcursor": "^2.27.2",
- "@tiptap/extension-gapcursor": "^2.27.2",
- "@tiptap/extension-hard-break": "^2.27.2",
- "@tiptap/extension-heading": "^2.27.2",
- "@tiptap/extension-history": "^2.27.2",
- "@tiptap/extension-horizontal-rule": "^2.27.2",
- "@tiptap/extension-italic": "^2.27.2",
- "@tiptap/extension-list-item": "^2.27.2",
- "@tiptap/extension-ordered-list": "^2.27.2",
- "@tiptap/extension-paragraph": "^2.27.2",
- "@tiptap/extension-strike": "^2.27.2",
- "@tiptap/extension-text": "^2.27.2",
- "@tiptap/extension-text-style": "^2.27.2",
- "@tiptap/pm": "^2.27.2"
+ "@tiptap/core": "^3.29.0",
+ "@tiptap/extension-blockquote": "^3.29.0",
+ "@tiptap/extension-bold": "^3.29.0",
+ "@tiptap/extension-bullet-list": "^3.29.0",
+ "@tiptap/extension-code": "^3.29.0",
+ "@tiptap/extension-code-block": "^3.29.0",
+ "@tiptap/extension-document": "^3.29.0",
+ "@tiptap/extension-dropcursor": "^3.29.0",
+ "@tiptap/extension-gapcursor": "^3.29.0",
+ "@tiptap/extension-hard-break": "^3.29.0",
+ "@tiptap/extension-heading": "^3.29.0",
+ "@tiptap/extension-horizontal-rule": "^3.29.0",
+ "@tiptap/extension-italic": "^3.29.0",
+ "@tiptap/extension-link": "^3.29.0",
+ "@tiptap/extension-list": "^3.29.0",
+ "@tiptap/extension-list-item": "^3.29.0",
+ "@tiptap/extension-list-keymap": "^3.29.0",
+ "@tiptap/extension-ordered-list": "^3.29.0",
+ "@tiptap/extension-paragraph": "^3.29.0",
+ "@tiptap/extension-strike": "^3.29.0",
+ "@tiptap/extension-text": "^3.29.0",
+ "@tiptap/extension-underline": "^3.29.0",
+ "@tiptap/extensions": "^3.29.0",
+ "@tiptap/pm": "^3.29.0"
},
"funding": {
"type": "github",
@@ -1814,28 +1751,6 @@
"tslib": "^2.4.0"
}
},
- "node_modules/@types/linkify-it": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz",
- "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==",
- "license": "MIT"
- },
- "node_modules/@types/markdown-it": {
- "version": "14.1.2",
- "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz",
- "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==",
- "license": "MIT",
- "dependencies": {
- "@types/linkify-it": "^5",
- "@types/mdurl": "^2"
- }
- },
- "node_modules/@types/mdurl": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz",
- "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==",
- "license": "MIT"
- },
"node_modules/@yarnpkg/lockfile": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz",
@@ -1899,25 +1814,6 @@
"node": ">= 8"
}
},
- "node_modules/anymatch/node_modules/picomatch": {
- "version": "2.3.2",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
- "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8.6"
- },
- "funding": {
- "url": "https://github.com/sponsors/jonschlinkert"
- }
- },
- "node_modules/argparse": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
- "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
- "license": "Python-2.0"
- },
"node_modules/axios": {
"version": "0.21.4",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz",
@@ -2087,9 +1983,9 @@
}
},
"node_modules/chardet": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.1.1.tgz",
- "integrity": "sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==",
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.2.0.tgz",
+ "integrity": "sha512-rddelWYNPRrXq6PtNEN2S3f6t9ILzvqaN5pVgi4kqt9jHQaXIial9PznB5iSPVlQSLNaaH22ItWz3EJtQ10+OA==",
"dev": true,
"license": "MIT"
},
@@ -2264,12 +2160,6 @@
"node": ">= 12"
}
},
- "node_modules/crelt": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz",
- "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==",
- "license": "MIT"
- },
"node_modules/cross-spawn": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
@@ -2479,6 +2369,7 @@
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
+ "dev": true,
"license": "BSD-2-Clause",
"engines": {
"node": ">=0.12"
@@ -2563,33 +2454,13 @@
}
},
"node_modules/escape-string-regexp": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
- "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
- "license": "MIT",
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/fdir": {
- "version": "6.5.0",
- "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
- "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
"dev": true,
"license": "MIT",
"engines": {
- "node": ">=12.0.0"
- },
- "peerDependencies": {
- "picomatch": "^3 || ^4"
- },
- "peerDependenciesMeta": {
- "picomatch": {
- "optional": true
- }
+ "node": ">=0.8.0"
}
},
"node_modules/figures": {
@@ -2608,16 +2479,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/figures/node_modules/escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.8.0"
- }
- },
"node_modules/fill-range": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
@@ -3286,6 +3147,9 @@
"arm64"
],
"dev": true,
+ "libc": [
+ "glibc"
+ ],
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -3307,6 +3171,9 @@
"arm64"
],
"dev": true,
+ "libc": [
+ "musl"
+ ],
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -3328,6 +3195,9 @@
"x64"
],
"dev": true,
+ "libc": [
+ "glibc"
+ ],
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -3349,6 +3219,9 @@
"x64"
],
"dev": true,
+ "libc": [
+ "musl"
+ ],
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -3404,25 +3277,6 @@
"url": "https://opencollective.com/parcel"
}
},
- "node_modules/linkify-it": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.1.tgz",
- "integrity": "sha512-wVoTjP4Q6R0NW5hiZkVJaFZPWgtXfoGF+6LucL3/FtiNjmcHhYjEr5f1Kqjirc1nBW07J/ZuRFumqr2oqccEWg==",
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/puzrin"
- },
- {
- "type": "github",
- "url": "https://github.com/sponsors/markdown-it"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "uc.micro": "^2.0.0"
- }
- },
"node_modules/linkifyjs": {
"version": "4.3.3",
"resolved": "https://registry.npmjs.org/linkifyjs/-/linkifyjs-4.3.3.tgz",
@@ -3478,33 +3332,6 @@
"@jridgewell/sourcemap-codec": "^1.5.5"
}
},
- "node_modules/markdown-it": {
- "version": "14.2.0",
- "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.2.0.tgz",
- "integrity": "sha512-1TGiQiJVRQ3NPmZH6sx5Cfnmg6GQm9jvC1ch4TK511NjSJvjzKLzn5pPfZRNZkRPZP0HqCioSndqH8v2nRaWVQ==",
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/puzrin"
- },
- {
- "type": "github",
- "url": "https://github.com/sponsors/markdown-it"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "argparse": "^2.0.1",
- "entities": "^4.4.0",
- "linkify-it": "^5.0.1",
- "mdurl": "^2.0.0",
- "punycode.js": "^2.3.1",
- "uc.micro": "^2.1.0"
- },
- "bin": {
- "markdown-it": "bin/markdown-it.mjs"
- }
- },
"node_modules/math-intrinsics": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
@@ -3527,12 +3354,6 @@
"is-buffer": "~1.1.6"
}
},
- "node_modules/mdurl": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz",
- "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==",
- "license": "MIT"
- },
"node_modules/micromatch": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
@@ -3547,19 +3368,6 @@
"node": ">=8.6"
}
},
- "node_modules/micromatch/node_modules/picomatch": {
- "version": "2.3.2",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
- "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8.6"
- },
- "funding": {
- "url": "https://github.com/sponsors/jonschlinkert"
- }
- },
"node_modules/mimic-fn": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
@@ -3813,22 +3621,22 @@
"license": "ISC"
},
"node_modules/picomatch": {
- "version": "4.0.5",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz",
- "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==",
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
+ "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
"dev": true,
"license": "MIT",
"engines": {
- "node": ">=12"
+ "node": ">=8.6"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/postcss": {
- "version": "8.5.22",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.22.tgz",
- "integrity": "sha512-KBDEIpLrvpv16pp3K0Fw+UCoZfopFjjgeB+0tA/aaThfEE74kKDLrgg603YvOWJyg3+WYtyq3xYsQWsIyZlPqQ==",
+ "version": "8.5.23",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.23.tgz",
+ "integrity": "sha512-g50586zr4bZmwFiTlflMu8E0bDTb5I5gertgwAKmsdUlTQIhZtunzUlD1WSzwcVWPoAVpsrA6vlfCD7oXvRwgg==",
"dev": true,
"funding": [
{
@@ -3863,15 +3671,6 @@
"prosemirror-transform": "^1.0.0"
}
},
- "node_modules/prosemirror-collab": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/prosemirror-collab/-/prosemirror-collab-1.3.1.tgz",
- "integrity": "sha512-4SnynYR9TTYaQVXd/ieUvsVV4PDMBzrq2xPUWutHivDuOshZXqQ5rGbZM84HEaXKbLdItse7weMGOUdDVcLKEQ==",
- "license": "MIT",
- "dependencies": {
- "prosemirror-state": "^1.0.0"
- }
- },
"node_modules/prosemirror-commands": {
"version": "1.7.1",
"resolved": "https://registry.npmjs.org/prosemirror-commands/-/prosemirror-commands-1.7.1.tgz",
@@ -3884,9 +3683,9 @@
}
},
"node_modules/prosemirror-dropcursor": {
- "version": "1.8.2",
- "resolved": "https://registry.npmjs.org/prosemirror-dropcursor/-/prosemirror-dropcursor-1.8.2.tgz",
- "integrity": "sha512-CCk6Gyx9+Tt2sbYk5NK0nB1ukHi2ryaRgadV/LvyNuO3ena1payM2z6Cg0vO1ebK8cxbzo41ku2DE5Axj1Zuiw==",
+ "version": "1.8.3",
+ "resolved": "https://registry.npmjs.org/prosemirror-dropcursor/-/prosemirror-dropcursor-1.8.3.tgz",
+ "integrity": "sha512-FoYbsJR8gK+DGlqhNoE29Loa38eIZPzQRIb1VMaDNBoo4OLP6vVof/jR8qFY/6XvUd6Dhug8MDCHl2a/h8RTfQ==",
"license": "MIT",
"dependencies": {
"prosemirror-state": "^1.0.0",
@@ -3938,47 +3737,15 @@
"w3c-keyname": "^2.2.0"
}
},
- "node_modules/prosemirror-markdown": {
- "version": "1.13.4",
- "resolved": "https://registry.npmjs.org/prosemirror-markdown/-/prosemirror-markdown-1.13.4.tgz",
- "integrity": "sha512-D98dm4cQ3Hs6EmjK500TdAOew4Z03EV71ajEFiWra3Upr7diytJsjF4mPV2dW+eK5uNectiRj0xFxYI9NLXDbw==",
- "license": "MIT",
- "dependencies": {
- "@types/markdown-it": "^14.0.0",
- "markdown-it": "^14.0.0",
- "prosemirror-model": "^1.25.0"
- }
- },
- "node_modules/prosemirror-menu": {
- "version": "1.3.2",
- "resolved": "https://registry.npmjs.org/prosemirror-menu/-/prosemirror-menu-1.3.2.tgz",
- "integrity": "sha512-6VgUJTYod0nMBlCaYJGhXGLu7Gt4AvcwcOq0YfJCY/6Uh+3S7UsWhpy6rJFCBFOmonq1hD8KyWOtZhkppd4YPg==",
- "license": "MIT",
- "dependencies": {
- "crelt": "^1.0.0",
- "prosemirror-commands": "^1.0.0",
- "prosemirror-history": "^1.0.0",
- "prosemirror-state": "^1.0.0"
- }
- },
"node_modules/prosemirror-model": {
- "version": "1.25.7",
- "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.25.7.tgz",
- "integrity": "sha512-A79aN8QEFUwI6cax8Yq4Rpcx1TJZ3Kagn+ii7qLo4/V8H3mMiHrhFyhTyHHvpSnOgMPpWiDGSwM3etwrxE50ug==",
+ "version": "1.25.11",
+ "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.25.11.tgz",
+ "integrity": "sha512-QWg9RhnpLlogAmp3p96uEFrE5txQpFynd4vhBAELkwgOCWQs/X0yCzB3/hrHqiPwf91RG5KyWq6553zs9JqIOQ==",
"license": "MIT",
"dependencies": {
"orderedmap": "^2.0.0"
}
},
- "node_modules/prosemirror-schema-basic": {
- "version": "1.2.4",
- "resolved": "https://registry.npmjs.org/prosemirror-schema-basic/-/prosemirror-schema-basic-1.2.4.tgz",
- "integrity": "sha512-ELxP4TlX3yr2v5rM7Sb70SqStq5NvI15c0j9j/gjsrO5vaw+fnnpovCLEGIcpeGfifkuqJwl4fon6b+KdrODYQ==",
- "license": "MIT",
- "dependencies": {
- "prosemirror-model": "^1.25.0"
- }
- },
"node_modules/prosemirror-schema-list": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/prosemirror-schema-list/-/prosemirror-schema-list-1.5.1.tgz",
@@ -4014,21 +3781,6 @@
"prosemirror-view": "^1.41.4"
}
},
- "node_modules/prosemirror-trailing-node": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/prosemirror-trailing-node/-/prosemirror-trailing-node-3.0.0.tgz",
- "integrity": "sha512-xiun5/3q0w5eRnGYfNlW1uU9W6x5MoFKWwq/0TIRgt09lv7Hcser2QYV8t4muXbEr+Fwo0geYn79Xs4GKywrRQ==",
- "license": "MIT",
- "dependencies": {
- "@remirror/core-constants": "3.0.0",
- "escape-string-regexp": "^4.0.0"
- },
- "peerDependencies": {
- "prosemirror-model": "^1.22.1",
- "prosemirror-state": "^1.4.2",
- "prosemirror-view": "^1.33.8"
- }
- },
"node_modules/prosemirror-transform": {
"version": "1.12.0",
"resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.12.0.tgz",
@@ -4039,25 +3791,16 @@
}
},
"node_modules/prosemirror-view": {
- "version": "1.41.8",
- "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.41.8.tgz",
- "integrity": "sha512-TnKDdohEatgyZNGCDWIdccOHXhYloJwbwU+phw/a23KBvJIR9lWQWW7WHHK3vBdOLDNuF7TaX98GObUZOWkOnA==",
+ "version": "1.42.2",
+ "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.42.2.tgz",
+ "integrity": "sha512-Pdg0l5kXm8aLDquFAnQFTCITg0q44sLqBlHlpsVLD9segdOao8TOfQdAhCrCXyVgPSRr6UDDROOIWA3bIrN9YQ==",
"license": "MIT",
"dependencies": {
- "prosemirror-model": "^1.20.0",
+ "prosemirror-model": "^1.25.8",
"prosemirror-state": "^1.0.0",
"prosemirror-transform": "^1.1.0"
}
},
- "node_modules/punycode.js": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz",
- "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/readable-stream": {
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
@@ -4086,19 +3829,6 @@
"node": ">=8.10.0"
}
},
- "node_modules/readdirp/node_modules/picomatch": {
- "version": "2.3.2",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
- "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8.6"
- },
- "funding": {
- "url": "https://github.com/sponsors/jonschlinkert"
- }
- },
"node_modules/restore-cursor": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
@@ -4378,6 +4108,37 @@
"url": "https://github.com/sponsors/SuperchupuDev"
}
},
+ "node_modules/tinyglobby/node_modules/fdir": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
+ "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/tinyglobby/node_modules/picomatch": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz",
+ "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
"node_modules/tmp": {
"version": "0.2.7",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.7.tgz",
@@ -4421,16 +4182,10 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/uc.micro": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz",
- "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==",
- "license": "MIT"
- },
"node_modules/undici": {
- "version": "7.26.0",
- "resolved": "https://registry.npmjs.org/undici/-/undici-7.26.0.tgz",
- "integrity": "sha512-3O9Tf67pGhgOv9jM35AbhkXAKi13f3oy3aE4CSgr+TckGeY+/iu97ZXN+J7DpHPzLbVApFd1IFhcnBjREYXYcg==",
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/undici/-/undici-7.29.0.tgz",
+ "integrity": "sha512-IDxfleLmmbSskfWSUATiN1nfn2rDuvnMOqb5CWR92iIfojA0Ud+ulOAAEQ57LPr9rWmsreUyf5lwyao+7GNNVw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -4543,14 +4298,14 @@
"picomatch": "^2.3.1"
}
},
- "node_modules/vite-plugin-full-reload/node_modules/picomatch": {
- "version": "2.3.2",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
- "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
+ "node_modules/vite/node_modules/picomatch": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz",
+ "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==",
"dev": true,
"license": "MIT",
"engines": {
- "node": ">=8.6"
+ "node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
diff --git a/package.json b/package.json
index bc9abdc7..18f7c133 100644
--- a/package.json
+++ b/package.json
@@ -9,34 +9,35 @@
"docs:refresh": "bash scripts/refresh-docs-site.sh",
"docs:build": "php docs-site/build.php && npm run docs:highlight",
"docs:highlight": "node node_modules/@torchlight-api/torchlight-cli/dist/bin/torchlight.cjs.js -c torchlight.config.cjs -i docs-site/dist && node docs-site/scripts/strip-copy-source.mjs docs-site/dist && node docs-site/scripts/verify-no-leak.mjs docs-site/dist",
+ "verify:drivers": "bash scripts/verify-drivers.sh",
"docs:check": "node docs-site/scripts/verify-docs.mjs .",
"docs:changed": "bash scripts/docs-changed.sh",
"build:forms-assets": "rm -rf packages/forms/dist/tiptap && esbuild packages/forms/resources/js/tiptap-editor.js packages/forms/resources/js/tiptap-editor-addons.js --bundle --minify --format=esm --splitting --outdir=packages/forms/dist/tiptap --entry-names=[name] --chunk-names=chunk-[hash] --legal-comments=none && esbuild packages/forms/resources/js/image-processor.js --bundle --minify --format=iife --legal-comments=none --outfile=packages/forms/dist/wire-forms-image.js",
"build:core-assets": "esbuild packages/core/resources/js/dropdown.js --bundle --minify --format=iife --legal-comments=none --outfile=packages/core/dist/wire-core-dropdown.js",
- "build:table-assets": "esbuild packages/table/resources/js/record-actions.js --bundle --minify --format=iife --legal-comments=none --outfile=packages/table/dist/wire-table-records.js",
+ "build:table-assets": "esbuild packages/table/resources/js/record-actions.js --bundle --minify --format=iife --legal-comments=none --outfile=packages/table/dist/wire-table-records.js && esbuild packages/table/resources/js/record-selection.js --bundle --minify --format=iife --legal-comments=none --outfile=packages/table/dist/wire-table-selection.js",
"docs:api": "php docs-site/scripts/verify-api-docs.php ."
},
"dependencies": {
- "@floating-ui/dom": "^1.8.0",
- "@tiptap/core": "^2.0",
- "@tiptap/extension-character-count": "^2.0",
- "@tiptap/extension-highlight": "^2.0",
- "@tiptap/extension-image": "^2.0",
- "@tiptap/extension-link": "^2.0",
- "@tiptap/extension-placeholder": "^2.0",
- "@tiptap/extension-table": "^2.0",
- "@tiptap/extension-table-cell": "^2.0",
- "@tiptap/extension-table-header": "^2.0",
- "@tiptap/extension-table-row": "^2.0",
- "@tiptap/extension-text-align": "^2.0",
- "@tiptap/extension-underline": "^2.0",
- "@tiptap/starter-kit": "^2.0"
+ "@floating-ui/dom": "^1.8",
+ "@tiptap/core": "^3.28",
+ "@tiptap/extension-character-count": "^3.28",
+ "@tiptap/extension-highlight": "^3.28",
+ "@tiptap/extension-image": "^3.28",
+ "@tiptap/extension-link": "^3.28",
+ "@tiptap/extension-placeholder": "^3.28",
+ "@tiptap/extension-table": "^3.28",
+ "@tiptap/extension-table-cell": "^3.28",
+ "@tiptap/extension-table-header": "^3.28",
+ "@tiptap/extension-table-row": "^3.28",
+ "@tiptap/extension-text-align": "^3.28",
+ "@tiptap/extension-underline": "^3.28",
+ "@tiptap/starter-kit": "^3.28"
},
"devDependencies": {
"@tailwindcss/forms": "^0.5.10",
"@tailwindcss/vite": "^4.3.3",
"@torchlight-api/torchlight-cli": "0.1.7",
- "esbuild": "^0.28.1",
+ "esbuild": "^0.28",
"laravel-vite-plugin": "^3.1.3",
"patch-package": "^8.0.1",
"tailwindcss": "^4.1.8",
diff --git a/packages/boost/resources/boost/docs/forms/fields/date-time-picker.md b/packages/boost/resources/boost/docs/forms/fields/date-time-picker.md
index 54c9dca4..7485f0a8 100644
--- a/packages/boost/resources/boost/docs/forms/fields/date-time-picker.md
+++ b/packages/boost/resources/boost/docs/forms/fields/date-time-picker.md
@@ -40,6 +40,29 @@ DateTimePicker::make('start')
->closeOnDateSelection()
```
+Bounds take anything readable as a date — a `Carbon`/`DateTimeInterface`, or a
+string such as `'2026-07-10'`, `'10.07.2026'`, `'today'` or `'+1 week'` — and are
+reshaped for the widget behind the scenes. A bound that cannot be read at all
+throws, rather than being silently dropped by the browser.
+
+```php
+DateTimePicker::make('start')
+ ->minDate(now()) // no past dates
+ ->maxDate(now()->addYear())
+```
+
+On a `datetime` picker a bound may also carry a time, which then limits the
+clock on that boundary day only:
+
+```php
+DateTimePicker::make('slot')
+ ->minDate('2026-07-10 08:30') // 10 July cannot start before 08:30
+ ->maxDate('2026-07-20 17:00') // 20 July cannot run past 17:00
+```
+
+A day-granular upper bound covers the whole day: `->maxDate('2026-07-20')`
+leaves 20 July selectable up to 23:59.
+
## Time Options
```php
@@ -92,8 +115,8 @@ The only exception is [`asMonth()`](#modes), which is always native.
| `asDateTime()` | — | Alias for `mode('datetime')` |
| `format(string)` | string | Storage format (Carbon compatible) |
| `displayFormat(string)` | string | Display format shown to the user |
-| `minDate(string\|Closure)` | string | Minimum selectable date |
-| `maxDate(string\|Closure)` | string | Maximum selectable date |
+| `minDate(string\|DateTimeInterface\|Closure)` | string | Earliest selectable date; may carry a time on a `datetime` picker |
+| `maxDate(string\|DateTimeInterface\|Closure)` | string | Latest selectable date; a day-granular bound covers the whole day |
| `disabledDates(array\|Closure)` | array | Dates that cannot be selected |
| `firstDayOfWeek(int)` | int | 0=Sunday, 1=Monday |
| `closeOnDateSelection()` | bool | Close picker after a date is selected |
diff --git a/packages/boost/resources/boost/docs/table/advanced.md b/packages/boost/resources/boost/docs/table/advanced.md
index 15be3c69..6591b539 100644
--- a/packages/boost/resources/boost/docs/table/advanced.md
+++ b/packages/boost/resources/boost/docs/table/advanced.md
@@ -360,11 +360,27 @@ $table->lazy()
### How It Works
-1. Page renders immediately with the placeholder HTML
+1. Page renders immediately with the placeholder HTML — and with the Alpine bundles the table will need
2. Livewire dispatches an async call to load table content
3. Placeholder is replaced with the fully rendered table
4. Subsequent interactions (sort, filter, paginate) are normal Livewire calls
+Step 1 is not a detail you can skip past. The bundles behind dropdowns, row
+selection and the record controller register their Alpine components from an
+`alpine:init` listener, and that event fires exactly once — when Alpine boots.
+A bundle arriving with the deferred markup would land after it, subscribe to an
+event that never fires again, and register nothing; the table would then come
+up with every dropdown dead and each sheet backdrop stuck over the page. So the
+**placeholder** render ships them, and the markup that replaces it initialises
+normally.
+
+Which bundles load follows the table's own configuration: the dropdown bundle
+always (the toolbar is built from dropdowns), the selection bundle with
+`selectable()`, and the record controller whenever the table mounts it at all —
+record-action pointer bindings, a row context menu, grid keyboard semantics,
+drag-select or Shift-range selection. A custom `lazyPlaceholder()` replaces the
+visible skeleton only — it never changes what loads.
+
### When to Use
- Dashboard pages with multiple tables — load each lazily
diff --git a/packages/boost/resources/boost/docs/table/columns/editing.md b/packages/boost/resources/boost/docs/table/columns/editing.md
index eda69f5d..533b066e 100644
--- a/packages/boost/resources/boost/docs/table/columns/editing.md
+++ b/packages/boost/resources/boost/docs/table/columns/editing.md
@@ -68,8 +68,8 @@ and query-string persistence.
->filterAsSelect(array|string $options, ?string $placeholder = null) // single value; searchable combobox
->filterAsMultiSelect(array|string $options, ?string $placeholder = null) // several values (whereIn); searchable combobox
->filterSearchable(bool $condition = true) // toggle the in-panel search (on by default)
-->filterAsDate(?string $minDate = null, ?string $maxDate = null)
-->filterAsDateRange(?string $minDate = null, ?string $maxDate = null)
+->filterAsDate(string|DateTimeInterface|null $minDate = null, string|DateTimeInterface|null $maxDate = null)
+->filterAsDateRange(string|DateTimeInterface|null $minDate = null, string|DateTimeInterface|null $maxDate = null)
->filterAsNumberRange(?float $min = null, ?float $max = null, ?float $step = null)
->filterAsBoolean(?string $trueLabel = null, ?string $falseLabel = null)
->filterOperator(string $operator) // '=', '!=', '>', '<', '>=', '<=', 'like' (default, partial match), 'starts_with', 'ends_with'
diff --git a/packages/boost/resources/boost/docs/table/columns/fill-handle.md b/packages/boost/resources/boost/docs/table/columns/fill-handle.md
index 238fbe93..7454a385 100644
--- a/packages/boost/resources/boost/docs/table/columns/fill-handle.md
+++ b/packages/boost/resources/boost/docs/table/columns/fill-handle.md
@@ -134,3 +134,5 @@ the versions the previous call returned; do not reuse the ones you started with.
- [Editing & Column-Level Filters](editing.md) — how a single inline save works
- [TextInputColumn](text-input.md) · [SelectColumn](select.md) · [ToggleColumn](toggle.md)
+- [The Gesture Layer](../gestures.md) — the handle is one of its capabilities;
+ `gestures(false)` closes it, endpoint included
diff --git a/packages/boost/resources/boost/docs/table/filters/date.md b/packages/boost/resources/boost/docs/table/filters/date.md
index 7f1e6208..595a7cac 100644
--- a/packages/boost/resources/boost/docs/table/filters/date.md
+++ b/packages/boost/resources/boost/docs/table/filters/date.md
@@ -66,7 +66,7 @@ the month of their child records.
```php
DateFilter::make('birth_date')
->minDate('1900-01-01')
- ->maxDate(now()->format('Y-m-d'))
+ ->maxDate(now())
```
## DateFilter API
@@ -76,8 +76,8 @@ DateFilter::make('birth_date')
->month(bool $month = true) // month picker, matches whole month
->fromLabel(string $label) // "from" placeholder (default: 'From')
->toLabel(string $label) // "to" placeholder (default: 'To')
-->minDate(string $date) // min selectable date
-->maxDate(string $date) // max selectable date
+->minDate(string|DateTimeInterface|null $date) // min selectable date
+->maxDate(string|DateTimeInterface|null $date) // max selectable date
```
## Range Behavior
diff --git a/packages/boost/resources/boost/docs/table/gestures.md b/packages/boost/resources/boost/docs/table/gestures.md
new file mode 100644
index 00000000..9263966c
--- /dev/null
+++ b/packages/boost/resources/boost/docs/table/gestures.md
@@ -0,0 +1,396 @@
+---
+order: 48
+---
+
+# The Gesture Layer
+
+A wire-table table can behave like a desktop application: arrow keys walk the
+rows, `Shift` works a range, the mouse sweeps down the checkbox column, right
+click opens a row menu, `?` explains itself, and a fill handle drags one value
+across many cells.
+
+That is exactly right for a back office. It is usually wrong for a public
+listing, where a highlighted row and a hijacked right click are noise at best.
+
+So it is one switch, and a table starts on the quiet side of it:
+
+```php
+->gestures()
+```
+
+That is the desktop-application table. Without it you get an ordinary web
+table — the one most pages want.
+
+## What a table gets without asking
+
+**Every way of operating a row is off until you ask.** Three capabilities change
+how the table answers a visitor who never intended to operate it, and all three
+wait:
+
+- **Keyboard navigation** puts the rows in the tab order, marks an active row
+ and starts answering arrows and `mod`+key.
+- **The drag sweep** turns a press in the checkbox column into a block
+ selection — a gesture people find by accident before they find it on purpose.
+- **Range selection** re-reads a modified click: `Shift`+click stops being a
+ click and becomes "everything between here and the last one". Right in a file
+ manager, startling in a list of blog posts.
+
+A selectable table therefore starts as checkboxes and nothing more, and the
+delegated Alpine controller is not even rendered. What stays allowed needs an
+invitation of its own anyway: a context menu needs actions bound to it, the fill
+handle needs `->fillHandle()`, and the `?` help needs the keyboard layer this
+default leaves off.
+
+```php
+// An ordinary listing. Checkboxes work and nothing else does:
+// no arrow keys, no drag selecting, no modified click meaning something else.
+Table::make()->selectable()
+
+// The same table as an application.
+Table::make()->gestures()->selectable()
+```
+
+To go further the other way — no right-click menu, no ranges, no fill handle,
+nothing at all — say so:
+
+```php
+->gestures(false)
+```
+
+## What counts as a gesture
+
+Six capabilities, each switchable on its own. "Default" is what a table that
+never calls `gestures()` gets:
+
+| Capability | Default | What it covers |
+|------------|---------|----------------|
+| `keyboard` | **off** | Grid navigation: roving `tabindex`, arrows, `Home`/`End`, `PageUp`/`PageDown`, `Enter` / `Shift`+`Enter` for the primary and secondary record action, `Space` to toggle the selection, and every action's own `keyboardShortcut()` / `onKey()` against the active row. Also what makes the table an ARIA `grid`. |
+| `rangeSelection` | **off** | `Shift`+click, `mod`+click and `mod`+`Shift`+click on a row, plus `Shift`+arrow, `Shift`+`Home` and `Shift`+`End` from the keyboard. |
+| `dragSelect` | **off** | The mouse sweep: press in the checkbox column and drag to select a block of rows. |
+| `contextMenu` | on | The right-click row menu — both `rowContextMenu()` and any `onContextMenu()` record action. |
+| `shortcutHelp` | on¹ | The `?` shortcut help. |
+| `fillHandle` | on² | The Excel-style fill handle on editable cells. |
+
+`mod` is `Ctrl` on Windows and `⌘` on macOS.
+
+¹ Allowed, but it reads the keyboard layer, so with the default it never opens.
+² Allowed, but the table still has to call `->fillHandle()`.
+
+With the default, then, the only gestures a table really offers are the ones it
+declared itself: a right-click menu if an action is bound to one, and the fill
+handle if it asked for one.
+
+## Mixing them
+
+Pass a closure. It receives this table's gestures and configures them in place —
+the return value is ignored, so a fluent chain and a multi-line body both work.
+
+```php
+->gestures(fn (TableGestures $g) => $g
+ ->keyboard() // arrows, Enter, shortcuts …
+ ->dragSelect(false)) // … but still no mouse sweep
+```
+
+Every setter takes a `bool`, so `->contextMenu(false)` reads as well as
+`->contextMenu()`.
+
+You can also hand over a prepared set, which is useful when several tables share
+one house style:
+
+```php
+use NyonCode\WireTable\Support\TableGestures;
+
+$readOnly = TableGestures::none()->contextMenu();
+
+// …then, in each table:
+->gestures($readOnly)
+```
+
+`TableGestures::defaults()`, `TableGestures::all()` and `TableGestures::none()`
+are the three starting points: the shipped default, everything, nothing.
+
+## A permission is not a switch-on
+
+Every capability is a **permission**, never a trigger. Turning one on does not
+conjure the thing it governs:
+
+- `dragSelect` and `rangeSelection` still need `->selectable()` (or
+ `->bulkActions()`, which implies it) — there has to be a selection for a range
+ to grow in. Both are also off in the default, so they need the permission
+ *and* the selection.
+- `fillHandle` still needs `->fillHandle()` on the table and editable columns.
+- `shortcutHelp` still needs the keyboard layer, because the keyboard layer is
+ what listens for the key.
+
+So `->gestures(fn ($g) => $g->dragSelect())` on a table without `selectable()`
+changes nothing. This is deliberate: the gesture layer decides what a table is
+*allowed* to do, and the rest of the table API decides what it *has*.
+
+## `keyboard()` has three states
+
+The other five capabilities are plain booleans. `keyboard` is three-state,
+because "on" has to mean two different things:
+
+| Value | Meaning |
+|-------|---------|
+| `false` (the default) | Off |
+| `null` | The table decides — on for a table with record actions or a selectable one. This is what `gestures()` sets |
+| `true` | Force it on, even for a table with neither |
+
+`gestures()` leaves the keyboard at `null` rather than forcing it, because a
+table with no record actions and no selection has nothing for the arrows to do,
+and a roving tabindex over inert rows is worse than none:
+
+```php
+Table::make()->gestures() // not a grid
+Table::make()->gestures()->selectable() // a grid
+Table::make()->gestures(fn (TableGestures $g) => $g->keyboard(true)) // a grid regardless
+```
+
+## What the layer does *not* govern
+
+**An explicitly declared record action keeps firing.** A binding like
+
+```php
+->recordAction(RecordAction::make(Action::make('view'))->onClick())
+```
+
+is a deliberate statement about this table, not an implicit affordance the table
+turned on for itself — so it survives `gestures(false)`. The gesture layer only
+governs the layer a table would otherwise switch on for itself.
+
+The one exception is `->onKey()`, which needs a keyboard layer to listen with.
+With `keyboard` off, an `onKey()` binding has nowhere to fire from.
+
+Selection itself is likewise untouched. With every gesture off, the checkboxes,
+both select-all controls and the bulk bar work exactly as they always did — you
+lose the shortcuts to them, not the feature. The selection cell then answers a
+modified click by toggling, since with ranges off nothing else would.
+
+## The active-row marker
+
+Rows carry the active-row marker when any gesture needs somewhere to grow from —
+that is, when the table uses grid semantics, range selection, or the sweep.
+
+A table left with nothing but a declared click action marks nothing. A click
+there opens the record and moves on; a highlighted row left behind would be an
+application affordance on a page that asked for none.
+
+## A project-wide default
+
+Set it once for every table:
+
+```php
+// config/wire-table.php
+'defaults' => [
+ 'gestures' => true,
+],
+```
+
+`null` (or a missing key) keeps the shipped default described above, `true`
+allows everything for every table — a back office turns the layer on once here
+instead of on every table — `false` allows nothing, and a map mixes:
+
+```php
+'gestures' => ['keyboard' => true, 'drag_select' => false],
+```
+
+Capability keys are matched loosely — `drag_select`, `drag-select`, `dragSelect`
+and `dragselect` are the same key. An **unknown** key throws
+`TableConfigurationException` rather than doing nothing quietly, because a typo
+in a permission is the kind of mistake that only shows up as "why doesn't this
+work" six months later.
+
+A per-table `->gestures(...)` always wins over the config default.
+
+## It is off on the server too
+
+Switching a capability off is not a matter of the client ignoring events. The
+markup and the endpoints go with it:
+
+- The delegated Alpine controllers are not rendered. A table with nothing but the
+ gestures off renders no controller at all, and its asset bundles are not
+ requested.
+- The table stops being an ARIA `grid`: no `role="grid"`, no `role="row"`, no
+ roving `tabindex`.
+- The rows are not focusable, so nothing steals focus on click.
+- The fill endpoint refuses. `fillHandle` off closes `fillTableCells` server-side,
+ not just the handle in the UI.
+- The shortcut legend drops the rows it no longer applies to — with ranges off,
+ `Shift`+arrow is not listed in the `?` help, because it does not work.
+
+That last point is the general rule: the legend is generated from what the table
+actually does, so it can never drift from reality.
+
+## Phones get buttons instead
+
+A gesture-driven table is a desktop idea. There is no double click on a phone,
+no right click, and no hover to discover either of them — so a record action
+that is behaviour-only on the desktop would be **unreachable** on a stacked
+mobile card.
+
+It is therefore rendered as an ordinary button there, and only there:
+
+```php
+->recordAction(RecordAction::make(Action::make('open'))->onDoubleClick())
+```
+
+| Surface | What the user gets |
+|---------|--------------------|
+| Desktop | A double-click gesture. No column, no button. |
+| Mobile card | An `Open` button. |
+
+The fallback is careful about not doubling anything:
+
+- Actions already in `->actions()` keep their order, and the record actions are
+ appended after them.
+- `recordAction('edit')`, which only *references* an action already declared in
+ `->actions()`, shows one button — not the same one twice.
+- An action promoted into the column with `->alsoInRowActions()` is already a
+ button, so it is left alone.
+- The fallback buttons count towards `->collapseActionsOnMobile()`, so a card
+ does not quietly grow past the threshold you set.
+
+Switch it off when a card is meant to stay clean:
+
+```php
+->recordActionButtonsOnMobile(false)
+```
+
+## API reference
+
+Everything the layer exposes, in one place.
+
+### On the table
+
+| Call | What it does |
+|------|--------------|
+| `->gestures()` | Allow every capability. The keyboard is left at "the table decides" |
+| `->gestures(false)` | Allow nothing at all |
+| `->gestures(fn (TableGestures $g) => …)` | Configure this table's capabilities in place |
+| `->gestures(TableGestures $set)` | Adopt a prepared set |
+| `->recordActionButtonsOnMobile(bool)` | Whether behaviour-only record actions render as buttons on a stacked card (default `true`) |
+
+Readers, useful in a custom view or a test:
+
+| Call | Answers |
+|------|---------|
+| `getGestures(): TableGestures` | The raw permissions, before any prerequisite |
+| `usesGridSemantics(): bool` | Is this an ARIA grid? The single owner of that decision |
+| `keyboardNavEnabled(): bool` | Alias of the above, read from the view |
+| `usesRangeSelection(): bool` | Do `Shift`/`mod` clicks and `Shift`+arrows work a range? |
+| `usesDragSelect(): bool` | Does a drag down the checkbox column sweep? |
+| `usesShortcutHelp(): bool` | Does `?` open the legend? |
+| `usesActiveRowMarker(): bool` | Do rows carry the active-row marker? |
+| `mountsRecordActionController(): bool` | Is the delegated Alpine controller rendered at all? |
+| `getGestureConfig(): array` | `['sweep' => bool, 'ranges' => bool]` — what the client controller consumes |
+| `getTableRole(): ?string` | `'grid'` or `null` |
+| `hasRowContextMenu(): bool` | Is there a right-click menu (permission included)? |
+| `isFillHandleEnabled(): bool` | Is the fill handle offered (permission included)? |
+
+### On `TableGestures`
+
+```php
+use NyonCode\WireTable\Support\TableGestures;
+```
+
+| Call | Meaning |
+|------|---------|
+| `TableGestures::defaults()` | The shipped default: keyboard and drag sweep off, the rest allowed |
+| `TableGestures::all()` | Everything allowed; keyboard left at `null` |
+| `TableGestures::none()` | Nothing allowed |
+| `TableGestures::fromConfig($value)` | Build from a config value (`null` / `bool` / map) |
+| `->keyboard(?bool)`, `->rangeSelection(bool)`, `->dragSelect(bool)`, `->contextMenu(bool)`, `->shortcutHelp(bool)`, `->fillHandle(bool)` | The setters; each returns `$this` |
+| `->allowsKeyboard(): ?bool` and `->allows*(): bool` | The permission, *before* the table's own prerequisites |
+| `->toArray(): array` | All six as data |
+
+A permission and an outcome are different questions: `allowsDragSelect()` says
+the table is allowed to sweep, `usesDragSelect()` says it actually does (which
+also needs `selectable()`).
+
+## Recipes
+
+**A public listing.** Nothing to do:
+
+```php
+$table->model(Post::class)->columns([...]);
+```
+
+**A back-office grid.** One call, or `'gestures' => true` in config for the whole
+project:
+
+```php
+$table->gestures()->selectable()->bulkActions([DeleteBulkAction::make()]);
+```
+
+**Click a row to open it, on an otherwise quiet table.** A declared record action
+is outside the layer, so this needs no `gestures()` at all:
+
+```php
+$table->recordAction(RecordAction::make(Action::make('view'))->onClick());
+```
+
+**Keyboard yes, sweeping no.** For long lists where an accidental drag would
+select a hundred rows:
+
+```php
+$table->gestures(fn (TableGestures $g) => $g->keyboard())->selectable();
+```
+
+**A house style across many tables.** Build the set once and hand it over:
+
+```php
+// app/Tables/Gestures.php
+public static function backOffice(): TableGestures
+{
+ return TableGestures::all()->dragSelect(false);
+}
+
+// in each table
+$table->gestures(Gestures::backOffice());
+```
+
+**A table inside a page with its own keyboard handling.** Keep the mouse half:
+
+```php
+$table->gestures(fn (TableGestures $g) => $g->rangeSelection()->dragSelect())->selectable();
+```
+
+**Ranges but no sweep.** `Shift`+click for a block, without a drag that selects
+by accident:
+
+```php
+$table->gestures(fn (TableGestures $g) => $g->rangeSelection())->selectable();
+```
+
+## Troubleshooting
+
+| Symptom | Why | Fix |
+|---------|-----|-----|
+| Arrow keys do nothing, rows are not focusable | The keyboard layer is opt-in | `->gestures()` |
+| `->gestures()` is there and it still is not a grid | Nothing for the arrows to drive — no record actions, no selection | Add `->selectable()`, or force it with `->gestures(fn ($g) => $g->keyboard(true))` |
+| `?` opens nothing | It reads the keyboard layer, and an empty legend renders no modal at all | Turn the keyboard on; check `shortcutLegend()->isEmpty()` |
+| A drag down the checkbox column selects nothing | `dragSelect` is off by default | `->gestures()`, or `->gestures(fn ($g) => $g->dragSelect())` |
+| `Shift`+click toggles one row instead of a range | `rangeSelection` is off by default | `->gestures()`, or `->gestures(fn ($g) => $g->rangeSelection())` |
+| Right-click shows the browser menu | No action is bound to it, or `contextMenu` is off | Bind one with `->onContextMenu()`; check `hasRowContextMenu()` |
+| The fill handle does not appear | It needs `->fillHandle()` **and** the permission **and** a fillable editable column | Check `isFillHandleEnabled()` and `Column::fillable()` |
+| An `->onKey()` binding never fires | Keys are read by the keyboard layer | `->gestures()` |
+| A record action is unreachable on a phone | The fallback was switched off | `->recordActionButtonsOnMobile()` |
+
+## Choosing
+
+| Table | Suggested |
+|-------|-----------|
+| Public listing, marketing page | Nothing to do — the default |
+| Back-office list, keyboard-heavy operators | `->gestures()`, or `'gestures' => true` in config |
+| Public page that must not even right-click | `->gestures(false)` |
+| Read-only report, right click still useful | `TableGestures::none()->contextMenu()` |
+| Long list, keyboard useful, sweeping risky | `->gestures(fn ($g) => $g->keyboard())` |
+| Embedded in a page with its own keyboard handling | `->gestures(fn ($g) => $g->rangeSelection()->dragSelect())` |
+| Selection matters, a stray drag does not | `->gestures(fn ($g) => $g->rangeSelection())` |
+
+## See Also
+
+- [Selecting Rows](selection.md) — what each selection gesture does
+- [Record Actions](record-actions.md) — binding an action to a row gesture
+- [Advanced](advanced.md) — the fill handle
diff --git a/packages/boost/resources/boost/docs/table/overview.md b/packages/boost/resources/boost/docs/table/overview.md
index 4863c70f..35eb5412 100644
--- a/packages/boost/resources/boost/docs/table/overview.md
+++ b/packages/boost/resources/boost/docs/table/overview.md
@@ -686,4 +686,7 @@ class UserTable extends Component
| [Imports](imports.md) | CSV imports — header mapping, casting, per-row validation, updateExisting |
| [Relation Managers](relation-managers.md) | Relationship-scoped tables as standalone Livewire components |
| [Advanced](advanced.md) | Sub-rows, summary footer, polling, lazy loading, caching, debug, responsive |
+| [Selecting Rows](selection.md) | Checkboxes, select-all-matching, and the selection gestures |
+| [Record Actions](record-actions.md) | Whole-row click, double-click, right-click and key bindings |
+| [The Gesture Layer](gestures.md) | `gestures()` — the opt-in keyboard/drag layer, and the mobile button fallback |
| [Actions](../core/actions.md) | Full Action system — modals, forms, wizard steps, lifecycle |
diff --git a/packages/boost/resources/boost/docs/table/record-actions.md b/packages/boost/resources/boost/docs/table/record-actions.md
index 82531bc5..2bba18ea 100644
--- a/packages/boost/resources/boost/docs/table/record-actions.md
+++ b/packages/boost/resources/boost/docs/table/record-actions.md
@@ -91,38 +91,81 @@ Action::make('edit')->onDoubleClick()->alsoInRowActions()
## Keyboard navigation
-When a table has any record action, keyboard navigation turns on automatically
-and the table announces itself as a grid:
+Keyboard navigation is opt-in, with `->gestures()`. Once a table has asked, it
+applies to any table the keyboard can drive row by row — one with record actions,
+and equally one that is `->selectable()` or has bulk actions — and such a table
+announces itself as an ARIA grid:
+
+```php
+->gestures()
+->recordAction(Action::make('open')->onDoubleClick())
+```
| Key | Action |
|-----|--------|
| `↑` / `↓` | Move the active row |
+| `Home` / `End`, `PageUp` / `PageDown` | Jump to an edge, or move by a screenful |
| `Enter` | Primary record action (double-click binding, else click) |
| `Shift` + `Enter` | Secondary record action (the other pointer binding) |
| `Space` | Toggle selection of the active row (and set the range anchor) when selectable, else the primary action |
-| `Shift` + `↑` / `↓` | Extend a contiguous selection range from the anchor (desktop range-select) |
+| `Shift` + `↑` / `↓` | Extend a selection range from the anchor |
| `mod` + `A` | Select every row on the page |
-| Menu key | Open the row context menu |
+| Menu key, `Shift` + `F10` | Open the row context menu |
+| `?` | Show the shortcuts this table answers to |
| `Delete`, `mod+d`, … | Any record action's own `->onKey()` / `->keyboardShortcut()` |
-Keyboard selection drives the **same** selection state as the checkboxes and the
-bulk-action bar — arrow to a row, `Space` to select it, `Shift`+arrow to extend a
-block — then run the bulk action from the bar.
+A `->onKey('Delete')` binding also answers to `Backspace`, which is the same key
+under a different name on a Mac keyboard.
+
+The selection gestures — `Space`, the ranges, `mod`+`A` — are covered in
+[Selecting Rows](selection.md), along with the mouse ones and what a range means
+when "all matching" is selected.
+
+Pointer and keyboard share one active row: **clicking a row marks it** and the
+arrows continue from there, so a table is never navigated from two places at
+once. The marker stays visible while the pointer hovers the row it marks, it
+survives the roundtrip an action triggers, and it follows its record through a
+re-sort (when the record leaves the page entirely, the tabstop falls back to the
+first row).
-Force it off (or on) if you need to:
+Keys only reach the grid when a **row itself** has the focus: a keystroke inside
+a row action button, an inline-editable cell or a dropdown belongs to that
+element. While an action modal is open the grid is inert — no arrow moves the
+marker behind the dialog and no shortcut fires a second action — and closing the
+modal hands the focus back to the active row, so the arrows keep working.
+
+Force it off (or on) if you need to — the keyboard is one capability of the
+[gesture layer](gestures.md):
```php
-->recordActionKeyboard(false)
+->gestures(fn (TableGestures $g) => $g->keyboard(false))
```
Because Enter always reaches the primary action, every record action stays
keyboard-accessible — a behaviour-only action is never a mouse-only trap.
+### Keys the grid reserves
+
+The keys the grid navigates with cannot be bound to an action — the binding
+would never fire. Rather than dropping it silently, `->onKey()` throws at
+configuration time:
+
+```text
+Enter Space ArrowUp ArrowDown Home End PageUp PageDown ContextMenu F10 ?
+```
+
+A `keyboardShortcut()` stamped on the action itself is only skipped, never fatal,
+since that action may legitimately serve a toolbar or a palette as well.
+
## Combining with selection and bulk actions
-When the table is `->selectable()`, a single click still selects the row, so the
-default record-action trigger becomes **double-click** — the two never fight.
-Clicking a checkbox only toggles selection, and bulk actions are untouched:
+When the table is `->selectable()`, the default record-action trigger becomes
+**double-click**, so a single click stays free for working with the selection —
+it only marks the row it lands on (the active row for the keyboard and the anchor
+of the next `Shift`+range). A plain click never ticks the checkbox; the modified
+ones deliberately do, because that is what `Shift` and `mod` mean everywhere else
+(see [Selecting Rows](selection.md)). A modified click is a selection gesture and
+never runs a bound record action. Bulk actions are untouched:
```php
->selectable()
@@ -139,7 +182,20 @@ tint it for a stronger "this row is clickable" hint:
```php
->recordActionHover('primary') // colored hover instead of neutral gray
-->activeRowClass('bg-amber-100') // override the keyboard-active row highlight
+->activeRowClass('bg-amber-100') // override the active-row marker (click + keyboard)
+```
+
+The active row drops its hover tint while it is marked, so the marker is never
+painted over by `hover:bg-*` when the pointer rests on it.
+
+By default the marker is two signals, not one: a background tint and a stripe
+down the row's leading edge. The tint on its own measures about 1.1:1 against a
+plain row — below the 3:1 contrast floor, and invisible to a reader who cannot
+separate the two hues. `activeRowClass()` replaces **both** halves, so an
+override owns its own contrast:
+
+```php
+->activeRowClass('bg-amber-100 [&>td:first-of-type]:before:bg-amber-600')
```
## Recommended UX
@@ -178,3 +234,11 @@ instead:
Action::make('delete')->onContextMenu(),
])
```
+
+## Related docs
+
+- [Selecting Rows](selection.md) — the selection gestures record actions share
+ the row with
+- [Actions](actions.md) — row, bulk and header actions
+- [The Gesture Layer](gestures.md) — switching the gestures off, and the mobile
+ button fallback
diff --git a/packages/boost/resources/boost/docs/table/selection.md b/packages/boost/resources/boost/docs/table/selection.md
new file mode 100644
index 00000000..cc5678bb
--- /dev/null
+++ b/packages/boost/resources/boost/docs/table/selection.md
@@ -0,0 +1,198 @@
+---
+order: 47
+---
+
+# Selecting Rows
+
+Selection can be a gesture surface, not just a column of checkboxes. A table that
+is `->selectable()` — or that merely has `->bulkActions()`, which implies it —
+gives you the checkboxes, the select-all controls and the bulk bar:
+
+```php
+->selectable()
+->bulkActions([DeleteBulkAction::make()])
+```
+
+Add `->gestures()` and it behaves like a list in a desktop file manager as well:
+`Shift`+click takes a range, `mod`+click adds one row, a drag down the checkbox
+column sweeps a block in, and from the keyboard the arrows walk the rows,
+`Space` toggles, `Shift`+arrows extend and `mod`+`A` takes the page.
+
+```php
+->gestures()
+->selectable()
+```
+
+That split is deliberate: keyboard navigation, range selection and the drag
+sweep all change how the table answers someone who never meant to operate it, so
+they wait to be asked (see [The Gesture Layer](gestures.md)). Everything below is
+marked with what it needs.
+
+## What the mouse does
+
+| Gesture | Result | Needs |
+|---------|--------|-------|
+| Click the selection cell | Toggle that row, and set the range anchor | — |
+| `Shift` + click | Select the range between the anchor and this row | `gestures()` |
+| `mod` + click | Toggle this one row, anywhere on it, and anchor here | `gestures()` |
+| `mod` + `Shift` + click | Add the whole block to what is already selected | `gestures()` |
+| Drag down the checkbox column | Sweep a run of rows into the selection | `gestures()` |
+| Click the row itself | Marks the row (see below) — it never ticks the checkbox | — |
+
+The **whole selection cell** is the target, not just the 16-pixel box inside it:
+the box alone is under every touch-target guideline and leaves most of the cell
+dead. A click in the cell can never reach a record action bound to the row.
+
+A plain click on the row *body* marks the row — it becomes the active row for
+the keyboard, and the anchor for the next range — but never selects it.
+Selection stays deliberate. `mod`+click is the exception, and that is what the
+modifier is for.
+
+## What the keyboard does
+
+Everything in this section needs `->gestures()` — see
+[The Gesture Layer](gestures.md).
+
+| Key | Result |
+|-----|--------|
+| `↑` / `↓` | Move the active row |
+| `Home` / `End` | Jump to the first / last row on the page |
+| `PageUp` / `PageDown` | Move by one screenful |
+| `Space` | Toggle the active row, and anchor here |
+| `Shift` + `↑` / `↓` | Grow or shrink the range from the anchor |
+| `Shift` + `Home` / `End` | Extend the range to the first / last row |
+| `mod` + `Shift` + `↑` / `↓` | The same as `Shift`+`Home` / `End` |
+| `mod` + `A` | Select every row on the page |
+| `?` | Show the shortcuts this table answers to |
+
+Keyboard selection drives the **same** state as the checkboxes and the bulk bar:
+arrow to a row, `Space` to select, `Shift`+arrow to extend, then run the bulk
+action.
+
+Keys only reach the table when a **row itself** has the focus. A keystroke inside
+a row — an action button, an inline-editable cell, a dropdown — belongs to that
+element, so `Space` typed into a cell stays a space and `?` typed into the search
+box does not open the help.
+
+## How ranges behave
+
+Every range grows from an **anchor**: the row you last picked with `Space`, with
+a checkbox, or with `mod`+click. The anchor is invisible and one-shot — a plain
+arrow move clears it.
+
+A range writes **what was already selected, plus the range** — not the range
+alone. Select rows 2–6, anchor on row 8, `Shift`+arrow down to 12, and you have
+2–6 and 8–12. Shrinking the range back gives up only the rows the range itself
+added.
+
+When the selection carries no anchor of its own — it came from `mod`+`A`, or from
+the select-all strip — the first `Shift`+arrow grows from the far edge of the
+contiguous block you are standing in. That way it *shrinks or grows the block you
+can see* instead of throwing the rest of the selection away.
+
+To drop individual rows out of a selection, arrow to each one and press `Space`,
+or `mod`+click them.
+
+## Selecting beyond the page
+
+The bulk bar offers **Select all N** once a page is selected, which switches the
+selection from an explicit list of keys to "everything the current filter
+matches" (see [Bulk Actions](actions.md#bulk-actions) for what that
+shape means and why it exists).
+
+Inside that mode the gestures still work, and they read the way you would expect
+of "everything except…":
+
+- `Shift`+arrow over a range **deselects** it, because the stored list is the set
+ of exclusions.
+- `mod`+`A` stands down. Everything is already selected; there is nothing for it
+ to add.
+- The header checkbox edits the exclusions and never silently drops you back to
+ an explicit selection.
+
+## Dragging down the column
+
+Press in the checkbox column and drag: every row you pass is selected, and the
+table scrolls when you reach its edge. The gesture is deliberately narrow.
+
+- **Additive only.** Backing up does not deselect. A sweep can only ever add.
+- **Mouse only.** A finger dragging the checkbox column scrolls the page, as it
+ should.
+- **Only in the checkbox column.** Dragging anywhere else selects text, as it
+ always did.
+- It starts on the first movement that changes rows, so a plain click stays a
+ plain click.
+
+## The shortcut help
+
+Press `?` with a row focused and the table shows exactly what it answers to —
+including any `->onKey()` binding of your own, and its label. The list is built
+from the table's own configuration, so a table without record actions does not
+claim to have any.
+
+The same list is available as data if you want to render it yourself:
+
+```php
+$sections = $table->shortcutLegend()->sections();
+```
+
+Each section has a translated `heading` and a list of `ShortcutHint` value
+objects (`->keys`, `->description`, `->labels(mac: true)`).
+
+## Accessibility
+
+Selection is not a mouse-only feature, and the table says so:
+
+- The table is an ARIA **grid**: `aria-rowcount`, `aria-multiselectable`, and an
+ `aria-rowindex` on every row counted through the whole result set — so row 1 of
+ page 2 announces as row 12, not row 1 again.
+- Every row reports `aria-selected`, kept in step with the live selection rather
+ than the last server response.
+- Selection changes are announced in a polite live region: *"3 of 40 selected"*,
+ *"All 40 selected"*, *"Selection cleared"*.
+- The active row is marked by a background tint **and** a stripe down its leading
+ edge. Colour alone would fail anyone who cannot separate the two hues, and the
+ tint alone measures about 1.1:1 — under the 3:1 contrast floor. The stripe
+ clears it in both light and dark.
+
+Override the marker if it clashes with your design — an override replaces both
+halves, so it owns its own contrast:
+
+```php
+->activeRowClass('bg-amber-100 [&>td:first-of-type]:before:bg-amber-600')
+```
+
+## Keys the table reserves
+
+The grid owns the keys it navigates with, so binding a record action to one of
+them would be dead code. `->onKey()` refuses at configuration time rather than
+silently dropping the binding:
+
+```text
+Enter Space ArrowUp ArrowDown Home End PageUp PageDown ContextMenu F10 ?
+```
+
+`Backspace` is deliberately **not** reserved: it acts as a platform alias of
+`Delete`, so a `->onKey('Delete')` binding answers to both, and an explicit
+`->onKey('Backspace')` stays valid.
+
+## Turning it back off
+
+A table that asked for the gesture layer can hand back one capability at a time,
+or the lot:
+
+```php
+->gestures(fn (TableGestures $g) => $g->dragSelect(false)) // keep the keyboard, drop the sweep
+->gestures(fn (TableGestures $g) => $g->keyboard(false)) // the other way round
+->gestures(false) // every gesture, ranges included
+```
+
+The checkboxes keep working in every one of those — see
+[The Gesture Layer](gestures.md).
+
+## Related docs
+
+- [Record Actions](record-actions.md) — whole-row click, double-click and
+ context-menu bindings
+- [Bulk Actions](actions.md#bulk-actions) — acting on a selection
+- [The Gesture Layer](gestures.md) — switching these gestures off, whole or in part
diff --git a/packages/boost/resources/boost/docs/upgrade.md b/packages/boost/resources/boost/docs/upgrade.md
index e803523a..44dd4330 100644
--- a/packages/boost/resources/boost/docs/upgrade.md
+++ b/packages/boost/resources/boost/docs/upgrade.md
@@ -78,6 +78,84 @@ Confirm your app meets these before upgrading.
---
+## Selection and keyboard gestures
+
+A table's selection grew from a column of checkboxes into a full gesture surface
+(see [Selecting Rows](table/selection.md)). Four things to check on the way up.
+
+**1. Every row gesture is opt-in — `->gestures()`.** The selection grew a full
+gesture surface: `Shift`/`mod` clicks for ranges, a drag down the checkbox column
+that sweeps a block in, and from the keyboard the arrows, `Space`,
+`Shift`+arrows and `mod`+`A`. None of it is on unless a table asks, because each
+changes how the table answers a visitor who never meant to operate it — the rows
+go into the tab order, an active row is marked, a drag starts selecting, and a
+modified click stops meaning a click.
+
+Add one call to the tables that want it:
+
+```php
+->gestures()
+->selectable()
+```
+
+or, for a project where every table is a back-office table:
+
+```php
+// config/wire-table.php
+'defaults' => ['gestures' => true],
+```
+
+What is *not* affected: the checkboxes, both select-all controls and the bulk bar
+work with no change on your side, and a table that never asked mounts no
+delegated controller at all. So do the right-click row menu and the fill handle,
+each of which you already had to ask for. See [The Gesture Layer](table/gestures.md) for the six capabilities and how
+to mix them.
+
+**2. `->onKey()` on a navigation key now throws.** It used to be dropped
+silently, so the action simply never fired. If a table binds one of these, the
+binding was already dead code — rebind it to a free key:
+
+```text
+Enter Space ArrowUp ArrowDown Home End PageUp PageDown ContextMenu F10 ?
+```
+
+`Backspace` stays available, and now doubles as an alias of `Delete`.
+
+**3. Range gestures no longer leave "all matching" mode.** When a selection is
+"everything the filter matches", the stored list is the set of *exclusions* — so
+a `Shift`+arrow range over it now **deselects** that range instead of collapsing
+the whole selection down to one page. If your code reads the selection directly,
+note that `getSelectedRecordKeys()` returns `[]` in that mode by design; use
+`selectedRecordsQuery()` or `eachSelectedRecord()` instead.
+
+**4. Republish the table view if you have overridden it.** The gestures need
+markup the packaged JavaScript looks for, and a published copy of
+`resources/views/vendor/wire-table/tables/index.blade.php` will not have it. The
+view carries a contract marker so a stale copy fails loudly in the browser
+console rather than selecting the wrong rows in silence:
+
+```bash
+php artisan vendor:publish --tag=wire-table::views --force
+```
+
+Re-apply your customisations on top of the new file. If you overrode the view
+only to restyle it, [Theming](theming.md) is usually the smaller path.
+
+**5. Behaviour-only record actions now render as buttons on a mobile card.** A
+phone has no double click, no right click and no hover to discover either, so an
+action bound only to a gesture used to be unreachable once the table stacked.
+It is now rendered as an ordinary button on the card — and only there; the
+desktop table is unchanged. Nothing is doubled: an action already in
+`->actions()`, or one promoted with `->alsoInRowActions()`, still yields exactly
+one button, and the fallback buttons count towards
+`->collapseActionsOnMobile()`. Opt out per table:
+
+```php
+->recordActionButtonsOnMobile(false)
+```
+
+---
+
## Finding Breaking Changes
`CHANGELOG.md` is the source of truth. Breaking changes are called out under a
diff --git a/packages/boost/resources/boost/guidelines/wire-table.blade.php b/packages/boost/resources/boost/guidelines/wire-table.blade.php
index e278f0ba..4869a73c 100644
--- a/packages/boost/resources/boost/guidelines/wire-table.blade.php
+++ b/packages/boost/resources/boost/guidelines/wire-table.blade.php
@@ -88,6 +88,99 @@ class pins `query()` to the owner record's relationship, so a subclass cannot wi
`$this->attachRelated($id, [...pivot])` and `$this->detachRelated($id)` (belongs-to-many only, `null`
detaches all). Using one against an unsupported relationship type throws a clear `RuntimeException`.
+### Gesture layer
+
+The desktop behaviour — keyboard grid navigation, Shift/mod ranges, the drag sweep, the right-click row
+menu, the `?` help and the Excel fill handle — is one layer, owned by `Support\TableGestures` and
+configured with `Table::gestures()`. **It is opt-in.** Keyboard navigation, range selection and the drag
+sweep are OFF for a table that never calls `gestures()`, because each changes how the table answers a
+visitor who never meant to operate it (rows enter the tab order, an active row is marked, a press in the
+checkbox column starts selecting a block, a Shift+click stops meaning a click). A selectable table starts
+as checkboxes and nothing more and mounts no delegated controller at all. Right for a back office, wrong
+for a public listing:
+
+ ->gestures() // the desktop-app table
+ ->gestures(false) // not even the quiet capabilities
+ ->gestures(fn (TableGestures $g) => $g
+ ->keyboard() // arrows, Enter, shortcuts …
+ ->dragSelect(false)) // … but still no mouse sweep
+ ->gestures(TableGestures::none()->contextMenu()) // a prepared set, shared across tables
+
+Six capabilities, each its own setter and `allows*()` reader, with the default a table gets without asking:
+
+| Capability | Default | Covers |
+|---|---|---|
+| `keyboard` | **off** | roving tabindex, arrows, Home/End, PageUp/PageDown, Enter / Shift+Enter, Space, every `keyboardShortcut()`/`onKey()` against the active row — and what makes the table an ARIA `grid` |
+| `rangeSelection` | **off** | Shift / mod / mod+Shift click, Shift+arrow, Shift+Home/End |
+| `dragSelect` | **off** | the checkbox-column sweep |
+| `contextMenu` | on | `rowContextMenu()` and any `onContextMenu()` binding |
+| `shortcutHelp` | on¹ | `?` |
+| `fillHandle` | on² | the Excel-style handle on editable cells |
+
+¹ reads the keyboard layer, so with the default it never opens. ² still needs `Table::fillHandle()`.
+
+The three that stay allowed already need an invitation of their own — actions bound to the context menu,
+`fillHandle()`, and (for the `?` help) the keyboard layer this default leaves off — so with the shipped
+default a table offers only what it declared itself. The closure receives
+this table's gestures and configures them **in place**; its return value is ignored, so a fluent chain and
+a multi-line body both work. `TableGestures::defaults()` / `all()` / `none()` are the three starting
+points: shipped default, everything, nothing.
+
+Readers, all on `Table` and all consulted rather than re-derived: `usesGridSemantics()` (the single owner
+of "is this an ARIA grid"), `keyboardNavEnabled()`, `usesRangeSelection()`, `usesDragSelect()`,
+`usesShortcutHelp()`, `usesActiveRowMarker()`, `mountsRecordActionController()`, `getGestureConfig()`
+(the `{sweep, ranges}` the client controller consumes), `getRecordActionKeyboardConfig()`, `getTableRole()`,
+`hasRowContextMenu()`, `isFillHandleEnabled()`, `getGestures()` (the raw permissions).
+
+**A capability is a permission, never a trigger.** Switching one on never conjures the thing it governs:
+a sweep still needs `selectable()`, ranges still need a selection to grow in, `fillHandle` still needs
+`Table::fillHandle()` plus editable columns, and `shortcutHelp` cannot outlive the keyboard layer that
+listens for the key. `keyboard` is the one **three-state** switch (`false` = the shipped default; `null` = the table decides,
+which it takes for record actions or a selectable table — this is what `gestures()` sets, deliberately
+NOT `true`, since a table with neither has nothing for the arrows to do; `true` = force it on regardless);
+the other five are plain booleans.
+
+**What the layer does not govern: an explicitly declared record action.** `RecordAction::make('view')->onClick()`
+is a deliberate statement about that table, not an affordance the table turned on for itself, so it keeps
+firing with `gestures(false)`. The exception is `->onKey()`, which needs the keyboard layer to listen with.
+Selection is likewise untouched — checkboxes, both select-all controls and the bulk bar work with every
+gesture off; you lose the shortcuts to them, not the feature.
+
+**It is off on the server, not just ignored on the client.** The delegated Alpine controllers are not
+rendered (a table with nothing but the gestures off renders no controller at all and requests no bundle),
+`role="grid"`/`role="row"`/roving tabindex go away, rows stop being focusable so a click steals no focus,
+the `fillTableCells` endpoint **refuses**, and `shortcutLegend()` drops the rows that no longer apply —
+with ranges off, Shift+arrow is not listed in the `?` help because it does not work. The legend is
+generated from what the table actually does, so it cannot drift from reality.
+
+The project-wide default is `config('wire-table.defaults.gestures')` — `null`/absent keeps the shipped
+default above, `true` turns the whole layer on for **every** table (what a back-office project sets once),
+`false` allows nothing, and a map (`['keyboard' => true, 'drag_select' => false]`) mixes on top of the
+shipped default; keys match loosely (`drag_select` = `drag-select` = `dragSelect`), and an **unknown key
+throws** `TableConfigurationException` rather than quietly doing nothing. A per-table `gestures()` always
+wins. Consequence for fixtures and tests: anything asserting `role="grid"`, a roving tabindex, arrow
+navigation, a Shift+click range, a sweep or the `?` help must call `->gestures()` first.
+
+The active-row marker appears only when something needs an anchor to grow from — grid semantics, range
+selection or the sweep (`usesActiveRowMarker()`). A table left with nothing but a declared click action
+marks nothing: the click opens the record and moves on, and a highlight left behind would be an
+application affordance on a page that asked for none.
+
+**Phones get buttons instead of gestures.** There is no double click, no right click and no hover to
+discover either, so a behaviour-only record action would be *unreachable* on a stacked mobile card — it is
+therefore rendered as an ordinary button there, and only there (`getMobileRowActionsForDisplay()` vs
+`getRowActionsForDisplay()`). The fallback never doubles anything: row actions keep their order with the
+record actions appended after them, a `recordAction('edit')` that only *references* an action already in
+`->actions()` yields one button, an action promoted with `->alsoInRowActions()` is already a button and is
+left alone, and the fallback buttons count towards `->collapseActionsOnMobile()`. Turn it off with
+`->recordActionButtonsOnMobile(false)`.
+
+The card renders a **copy** with the keyboard shortcut stripped (`HasKeyboardShortcut::withoutKeyboardShortcut()`,
+new in wire-core). A rendered action button binds its `keyboardShortcut()` as a **window** listener
+(`x-on:keydown.{key}.window` in `wire-core::actions.button`) and the stacked cards are in the document at
+every width — so without this one `Delete` press ran an `onKey('Delete')` action once per card behind the
+desktop table. General rule: **never render the same shortcut-carrying action on two surfaces.**
+
### More
- Summaries: per-column `->summarize(...)` with footer scope toggles; grand totals computed in SQL.
@@ -98,9 +191,10 @@ class pins `query()` to the owner record's relationship, so a subclass cannot wi
- **Fill (Excel-style), server side.** `Table::fillHandle()` opts a table in to writing one value across many rows in **one** request (`fillTableCells`); `Column::fillable(false)` excludes a column that is otherwise editable (a unique code, an invoice number), and `Table::fillMaxRecords(int)` caps a single request (default 500). Each record still goes through the full per-record path — `canEdit()`, its own rules, its own optimistic-lock version — so a fill is deliberately **not** all-or-nothing: one row losing its race is reported as a per-record failure while the rest land. Records are resolved through the table's own query, so a key outside it is never written. The endpoint refuses outright unless `fillHandle()` is on. Per-cell `CellUpdating`/`CellUpdated` fire exactly as for a single edit — there is no separate bulk event. The payload is a **list** of `{column, value, records}` entries where `records` maps record key to the optimistic-lock version the client holds (a map, not a bare list of keys — PHP casts a numeric string array key to an int, so `{"15": "…"}` and `["…"]` would be indistinguishable). Driving `fillTableCells` repeatedly means sending the versions the previous call **returned**, never the ones you started with; the version is `updated_at` to the second, so two writes inside one second are indistinguishable and a stale version is not caught there.
- Conditional row styling: `Table::rowColor(string|Closure|null)` tints a whole row with a semantic/hue color resolved by the canonical `HasColor` owner (return `null` from the Closure for no tint; a tinted row gets a same-hue hover and drops the neutral hover/striping). `Table::rowClass(string|Closure|null)` adds arbitrary classes (the Closure receives the record). Prefer `rowColor()` over hand-written `bg-*` classes; combine both for e.g. a danger tint + `font-semibold`.
- Per-user column memory: `Table::rememberColumns('key')` loads each user's saved hidden-column set on mount and persists it on every toggle, scoped to `auth()->user()` (one key serves all users; stale column names are ignored). Storage is a driver chosen in `config('wire-table.preferences')` — `null` (default, no persistence), `session`, or `database` (publish `wire-table::migrations` → `table_preferences` table). `Table::preferenceDriver($driver)` overrides per table; a "Reset columns" control clears the saved layout. Implement `TablePreferenceDriver` for a custom store.
-- **Record actions (whole-row interaction), a distinct group from `->actions()`/`->bulkActions()`/`->headerActions()`.** `Table::recordActions([...])` / `recordAction(string|Action|RecordAction)` bind an action to a row gesture: `Action::make('edit')->onDoubleClick()` (also `->onClick()`, `->onContextMenu()`, `->onKey('Delete')`, `->on('custom')`). Those fluent triggers are `Action` macros that **return a `RecordAction`** (a table-owned wrapper — the shared `Action` class stays clean); it belongs in `recordActions()`, and `->actions()` rejects it out loud. A bare name (`recordAction('edit')`) references an action already in `->actions()`. Execution reuses `openActionModal`/`executeTableAction` (auth, confirmation, forms unchanged) — no second pipeline. **Behaviour-only by default** (no button — this is what makes a table feel like an app); `->alsoInRowActions()` also renders it in the column, `->behaviorOnly()` states the default. **One delegated Alpine controller (`wireRecordActions`) on the ` `** — never per-row — resolves the row from `data-row-key` and ignores clicks on any interactive element inside the row (buttons/checkboxes/links/editable cells/dropdowns) with no `stopPropagation()` needed. `onContextMenu()` feeds the row context menu (a single delegated menu, positioned at the cursor; closes on outside-click/Escape/scroll). When selectable, the default trigger is **double-click** so a single click still selects. Keyboard nav auto-on when any record action exists: `role="grid"`, roving `tabindex`, ↑/↓ move the active row, Enter/Shift+Enter run the primary/secondary, the Menu key opens the context menu, and each action's `keyboardShortcut()` fires against the active row (`recordActionKeyboard(false)` forces off). Keyboard **selection** shares the one selection component the checkboxes/bulk bar use (reached via `data-selection-root`, optimistic — no per-keystroke roundtrip): Space toggles the active row + sets an anchor, Shift+↑/↓ extends a contiguous range from the anchor, mod+A selects the page. Style with `recordActionHover('primary')` (else neutral) and `activeRowClass(...)`. Desktop pointer + keyboard feature; touch cards and sub-rows are excluded by design.
+- **Record actions (whole-row interaction), a distinct group from `->actions()`/`->bulkActions()`/`->headerActions()`.** `Table::recordActions([...])` / `recordAction(string|Action|RecordAction)` bind an action to a row gesture: `Action::make('edit')->onDoubleClick()` (also `->onClick()`, `->onContextMenu()`, `->onKey('Delete')`, `->on('custom')`). Those fluent triggers are `Action` macros that **return a `RecordAction`** (a table-owned wrapper — the shared `Action` class stays clean); it belongs in `recordActions()`, and `->actions()` rejects it out loud. A bare name (`recordAction('edit')`) references an action already in `->actions()`. Execution reuses `openActionModal`/`executeTableAction` (auth, confirmation, forms unchanged) — no second pipeline. **Behaviour-only by default** (no button — this is what makes a table feel like an app); `->alsoInRowActions()` also renders it in the column, `->behaviorOnly()` states the default. **One delegated Alpine controller (`wireRecordActions`) on the ` `** — never per-row — resolves the row from `data-row-key` and ignores clicks on any interactive element inside the row (buttons/checkboxes/links/editable cells/dropdowns) with no `stopPropagation()` needed. `onContextMenu()` feeds the row context menu (a single delegated menu, positioned at the cursor; closes on outside-click/Escape/scroll). When selectable, the default trigger is **double-click**, leaving the single click free for selection work — a *plain* click only marks the row (active row + range anchor) and never ticks the checkbox, while a **modified** click is a selection gesture and never runs a bound action (see the selection-gestures bullet). Keyboard nav needs `gestures()` **and** a table the keyboard can drive row by row — record actions **or** `selectable()`/`bulkActions()` (`Table::usesGridSemantics()` is the single owner of that decision; see the gesture-layer bullet): `role="grid"`, roving `tabindex`, ↑/↓ move the active row, Enter/Shift+Enter run the primary/secondary, Menu key **and Shift+F10** open the context menu, `?` opens the shortcut help, and each action's `keyboardShortcut()` fires against the active row. **Pointer and keyboard share one active row**: a click marks the row (an Alpine `:class`/`:tabindex` binding, so the marker and the tabstop survive the Livewire morph every update triggers, follow the record through a re-sort, and fall back to the first row when it leaves the page), the active row drops its hover tint so `hover:bg-*` cannot paint over the marker, keys reach the grid only when a **row itself** has the focus (a keystroke inside a row button/editable cell/dropdown is that element's), the grid is inert while a dialog is open, and closing a modal hands the focus back to the active row. Style with `recordActionHover('primary')` (else neutral) and `activeRowClass(...)`. Desktop pointer + keyboard feature; touch cards and sub-rows are excluded by design.
+- **Selection gestures (mouse + keyboard), one shared Alpine component.** Every selection surface — the checkboxes, both select-all toggles, the bulk bar, the mobile cards, the keyboard — drives one `wireRecordSelection` component reached via `[data-selection-root]` (optimistic; no per-keystroke roundtrip). Mouse: the **whole selection cell** toggles (`[data-select-cell]`, not just the 16px box, and the cell is registered interactive so the click never reaches a record action), Shift+click ranges from the anchor, mod+click toggles one row anywhere on it, mod+Shift+click adds a block, and **dragging down the checkbox column sweeps** rows in (additive only, mouse only, engages on the first row-changing move so a plain click stays a click). Keyboard: Space toggles + anchors, Shift+↑/↓ ranges, Shift+Home/End and mod+Shift+↑/↓ range to the edge, Home/End/PageUp/PageDown navigate, mod+A selects the page. **A range writes `base ∪ range`, not the range alone** — the snapshot minus the contiguous block around the anchor, so rows selected elsewhere survive; a range gesture **never rewrites `mode`**, which means in "all matching" mode (where the stored list is the *exclusions*) a range **deselects** and mod+A stands down. The anchor is one-shot and invisible; with no anchor of its own the range grows from the far edge of the block the active row sits in. Drop single rows with Space or mod+click, not a range. Keys reach the grid only when a **row itself** has the focus. **The grid reserves the keys it navigates with** — `->onKey()` on Enter/Space/arrows/Home/End/PageUp/PageDown/ContextMenu/F10/`?` throws a `TableConfigurationException` at configuration time rather than dropping the binding silently (a `keyboardShortcut()` on the action itself is only skipped); `Backspace` is deliberately not reserved and acts as a JS-side alias of `Delete`. ARIA: `aria-rowcount`, `aria-multiselectable`, `aria-rowindex` counted through the **whole result set** (not the page), bound `aria-selected` per row, and a polite live region that is in the DOM from the first paint and empty until the first change. The active-row marker is a tint **plus** a leading stripe (`activeRowClass()` replaces both halves) — the tint alone is ~1.1:1, under the 3:1 non-text contrast floor. `Table::shortcutLegend()` returns the same gesture list as data (`ShortcutHint` value objects) for rendering elsewhere.
- `Table::rowContextMenu([...actions])` is **deprecated** (removed in v2.0) — a thin alias that still feeds the same context menu. Prefer `recordAction(Action::make('edit')->onContextMenu())`.
-- **Mobile (`Table::stackedOnMobile()`).** Below the breakpoint each row becomes a card whose hierarchy is five derived slots — title (first column), metric (last right-aligned, e.g. `money()`), meta (badge columns), subtitle, and a label/value grid for the rest — overridable per column (`->mobileMetric()`, `->mobileMeta()`, …) or per table (`->mobileCard(fn (MobileCardConfig $c) => $c->title('number')->metric('total'))`). The header row is hidden, so its controls move into the card view: an always-visible select-all strip, a sort control, sub-row children with their subtotal, and the summary totals. `->collapseActionsOnMobile()` folds row actions into one dropdown.
+- **Mobile (`Table::stackedOnMobile()`).** Below the breakpoint each row becomes a card whose hierarchy is five derived slots — title (first column), metric (last right-aligned, e.g. `money()`), meta (badge columns), subtitle, and a label/value grid for the rest — overridable per column (`->mobileMetric()`, `->mobileMeta()`, …) or per table (`->mobileCard(fn (MobileCardConfig $c) => $c->title('number')->metric('total'))`). The header row is hidden, so its controls move into the card view: an always-visible select-all strip, a sort control, sub-row children with their subtotal, and the summary totals. `->collapseActionsOnMobile()` folds row actions into one dropdown. **Record actions fall back to buttons here**: every record trigger is a desktop one, so a behaviour-only record action renders as an ordinary button on the card (and only there) — one declaration, a gesture on the desktop and a button on a phone. It never doubles an action already in `->actions()`, one referenced by name, or one promoted with `->alsoInRowActions()`, and the fallback buttons count towards `collapseActionsOnMobile()`; the card's copy drops the action's `keyboardShortcut()` (`HasKeyboardShortcut::withoutKeyboardShortcut()`), because a rendered button binds its shortcut as a *window* listener and the cards are in the document at every width. Opt out with `Table::recordActionButtonsOnMobile(false)`.
- `Table::queryString()` persists state to the URL.
- Browser-testing hooks: every active part carries a stable `data-testid` — `table-search`, `table-filters-trigger`, `table-filter-reset`, `filter-chip-{name}`, `column-filter-chip-{name}`, `table-column-toggle`, `table-per-page`, `table-page-prev|next|{n}`, `table-sort-{col}`, `table-filter-{col}`, `table-cell-{col}`, `table-editable-{col}`, `table-row` (+ `data-row-key`; mobile `table-card`), `table-select-all` / `table-row-select`, `table-row-expand`, `table-bulk-bar` / `table-deselect`, and `action-{name}` / `header-action-{name}` / `bulk-action-{name}` / `menu-action-{name}` (all with `aria-label`) — so Pest v4 Browser Testing targets them at the user level. Actions and filter options are also reachable by visible text. Column-static render metadata is resolved once per column (`$columnMeta`) instead of per cell.
@@ -112,7 +206,10 @@ class pins `query()` to the owner record's relationship, so a subclass cannot wi
- **Defer off-screen tables.** `Table::lazy()` returns no rows and runs no query until the
table scrolls into view (optional `->lazyPlaceholder(...)`). Use it for tables below the fold
- or in tabs.
+ or in tabs. It defers the query and the markup, **not** the JS: the table's Alpine bundles
+ ship with the placeholder render, because they register from `alpine:init` and that fires
+ once, at boot — a bundle arriving with the deferred markup would register nothing. So
+ `lazy()` is a lever for query and render cost, not for first-paint script weight.
- **Defer action-group menus.** `ActionGroup::make([...])->lazyMenu()` ships only the trigger plus
a serialized item spec per row and builds the menu client-side on first open — zero per-row menu
Blade renders (an eager group renders one view per item per row). Opt-in; the default is eager.
diff --git a/packages/core/dist/wire-core-dropdown.js b/packages/core/dist/wire-core-dropdown.js
index 96c22ac8..e6c0f344 100644
--- a/packages/core/dist/wire-core-dropdown.js
+++ b/packages/core/dist/wire-core-dropdown.js
@@ -1 +1 @@
-(()=>{var B=Math.min,E=Math.max,ot=Math.round,st=Math.floor,M=t=>({x:t,y:t}),ge={left:"right",right:"left",bottom:"top",top:"bottom"};function pt(t,e,n){return E(t,B(e,n))}function q(t,e){return typeof t=="function"?t(e):t}function F(t){return t.split("-")[0]}function G(t){return t.split("-")[1]}function gt(t){return t==="x"?"y":"x"}function wt(t){return t==="y"?"height":"width"}function $(t){let e=t[0];return e==="t"||e==="b"?"y":"x"}function vt(t){return gt($(t))}function Pt(t,e,n){n===void 0&&(n=!1);let i=G(t),o=vt(t),s=wt(o),l=o==="x"?i===(n?"end":"start")?"right":"left":i==="start"?"bottom":"top";return e.reference[s]>e.floating[s]&&(l=it(l)),[l,it(l)]}function Mt(t){let e=it(t);return[ct(t),e,ct(e)]}function ct(t){return t.includes("start")?t.replace("start","end"):t.replace("end","start")}var St=["left","right"],Rt=["right","left"],we=["top","bottom"],ve=["bottom","top"];function ye(t,e,n){switch(t){case"top":case"bottom":return n?e?Rt:St:e?St:Rt;case"left":case"right":return e?we:ve;default:return[]}}function Tt(t,e,n,i){let o=G(t),s=ye(F(t),n==="start",i);return o&&(s=s.map(l=>l+"-"+o),e&&(s=s.concat(s.map(ct)))),s}function it(t){let e=F(t);return ge[e]+t.slice(e.length)}function xe(t){return{top:0,right:0,bottom:0,left:0,...t}}function $t(t){return typeof t!="number"?xe(t):{top:t,right:t,bottom:t,left:t}}function H(t){let{x:e,y:n,width:i,height:o}=t;return{width:i,height:o,top:n,left:e,right:e+i,bottom:n+o,x:e,y:n}}function _t(t,e,n){let{reference:i,floating:o}=t,s=$(e),l=vt(e),r=wt(l),c=F(e),f=s==="y",a=i.x+i.width/2-o.width/2,d=i.y+i.height/2-o.height/2,m=i[r]/2-o[r]/2,u;switch(c){case"top":u={x:a,y:i.y-o.height};break;case"bottom":u={x:a,y:i.y+i.height};break;case"right":u={x:i.x+i.width,y:d};break;case"left":u={x:i.x-o.width,y:d};break;default:u={x:i.x,y:i.y}}switch(G(e)){case"start":u[l]-=m*(n&&f?-1:1);break;case"end":u[l]+=m*(n&&f?-1:1);break}return u}async function Dt(t,e){var n;e===void 0&&(e={});let{x:i,y:o,platform:s,rects:l,elements:r,strategy:c}=t,{boundary:f="clippingAncestors",rootBoundary:a="viewport",elementContext:d="floating",altBoundary:m=!1,padding:u=0}=q(e,t),h=$t(u),w=r[m?d==="floating"?"reference":"floating":d],g=H(await s.getClippingRect({element:(n=await(s.isElement==null?void 0:s.isElement(w)))==null||n?w:w.contextElement||await(s.getDocumentElement==null?void 0:s.getDocumentElement(r.floating)),boundary:f,rootBoundary:a,strategy:c})),v=d==="floating"?{x:i,y:o,width:l.floating.width,height:l.floating.height}:l.reference,y=await(s.getOffsetParent==null?void 0:s.getOffsetParent(r.floating)),x=await(s.isElement==null?void 0:s.isElement(y))?await(s.getScale==null?void 0:s.getScale(y))||{x:1,y:1}:{x:1,y:1},b=H(s.convertOffsetParentRelativeRectToViewportRelativeRect?await s.convertOffsetParentRelativeRectToViewportRelativeRect({elements:r,rect:v,offsetParent:y,strategy:c}):v);return{top:(g.top-b.top+h.top)/x.y,bottom:(b.bottom-g.bottom+h.bottom)/x.y,left:(g.left-b.left+h.left)/x.x,right:(b.right-g.right+h.right)/x.x}}var be=50,Ft=async(t,e,n)=>{let{placement:i="bottom",strategy:o="absolute",middleware:s=[],platform:l}=n,r=l.detectOverflow?l:{...l,detectOverflow:Dt},c=await(l.isRTL==null?void 0:l.isRTL(e)),f=await l.getElementRects({reference:t,floating:e,strategy:o}),{x:a,y:d}=_t(f,i,c),m=i,u=0,h={};for(let p=0;pI<=0)){var Q,tt;let I=(((Q=s.flip)==null?void 0:Q.index)||0)+1,mt=O[I];if(mt&&(!(d==="alignment"?v!==$(mt):!1)||D.every(P=>$(P.placement)===v?P.overflows[0]>0:!0)))return{data:{index:I,overflows:D},reset:{placement:mt}};let nt=(tt=D.filter(W=>W.overflows[0]<=0).sort((W,P)=>W.overflows[1]-P.overflows[1])[0])==null?void 0:tt.placement;if(!nt)switch(u){case"bestFit":{var et;let W=(et=D.filter(P=>{if(A){let k=$(P.placement);return k===v||k==="y"}return!0}).map(P=>[P.placement,P.overflows.filter(k=>k>0).reduce((k,pe)=>k+pe,0)]).sort((P,k)=>P[1]-k[1])[0])==null?void 0:et[0];W&&(nt=W);break}case"initialPlacement":nt=r;break}if(o!==nt)return{reset:{placement:nt}}}return{}}}};var Ae=new Set(["left","top"]);async function Ee(t,e){let{placement:n,platform:i,elements:o}=t,s=await(i.isRTL==null?void 0:i.isRTL(o.floating)),l=F(n),r=G(n),c=$(n)==="y",f=Ae.has(l)?-1:1,a=s&&c?-1:1,d=q(e,t),{mainAxis:m,crossAxis:u,alignmentAxis:h}=typeof d=="number"?{mainAxis:d,crossAxis:0,alignmentAxis:null}:{mainAxis:d.mainAxis||0,crossAxis:d.crossAxis||0,alignmentAxis:d.alignmentAxis};return r&&typeof h=="number"&&(u=r==="end"?h*-1:h),c?{x:u*a,y:m*f}:{x:m*f,y:u*a}}var kt=function(t){return t===void 0&&(t=0),{name:"offset",options:t,async fn(e){var n,i;let{x:o,y:s,placement:l,middlewareData:r}=e,c=await Ee(e,t);return l===((n=r.offset)==null?void 0:n.placement)&&(i=r.arrow)!=null&&i.alignmentOffset?{}:{x:o+c.x,y:s+c.y,data:{...c,placement:l}}}}},Bt=function(t){return t===void 0&&(t={}),{name:"shift",options:t,async fn(e){let{x:n,y:i,placement:o,platform:s}=e,{mainAxis:l=!0,crossAxis:r=!1,limiter:c={fn:g=>{let{x:v,y}=g;return{x:v,y}}},...f}=q(t,e),a={x:n,y:i},d=await s.detectOverflow(e,f),m=$(F(o)),u=gt(m),h=a[u],p=a[m];if(l){let g=u==="y"?"top":"left",v=u==="y"?"bottom":"right",y=h+d[g],x=h-d[v];h=pt(y,h,x)}if(r){let g=m==="y"?"top":"left",v=m==="y"?"bottom":"right",y=p+d[g],x=p-d[v];p=pt(y,p,x)}let w=c.fn({...e,[u]:h,[m]:p});return{...w,data:{x:w.x-n,y:w.y-i,enabled:{[u]:l,[m]:r}}}}}};var Nt=function(t){return t===void 0&&(t={}),{name:"size",options:t,async fn(e){var n,i;let{placement:o,rects:s,platform:l,elements:r}=e,{apply:c=()=>{},...f}=q(t,e),a=await l.detectOverflow(e,f),d=F(o),m=G(o),u=$(o)==="y",{width:h,height:p}=s.floating,w,g;d==="top"||d==="bottom"?(w=d,g=m===(await(l.isRTL==null?void 0:l.isRTL(r.floating))?"start":"end")?"left":"right"):(g=d,w=m==="end"?"top":"bottom");let v=p-a.top-a.bottom,y=h-a.left-a.right,x=B(p-a[w],v),b=B(h-a[g],y),A=!e.middlewareData.shift,O=x,R=b;if((n=e.middlewareData.shift)!=null&&n.enabled.x&&(R=y),(i=e.middlewareData.shift)!=null&&i.enabled.y&&(O=v),A&&!m){let D=E(a.left,0),Q=E(a.right,0),tt=E(a.top,0),et=E(a.bottom,0);u?R=h-2*(D!==0||Q!==0?D+Q:E(a.left,a.right)):O=p-2*(tt!==0||et!==0?tt+et:E(a.top,a.bottom))}await c({...e,availableWidth:R,availableHeight:O});let N=await l.getDimensions(r.floating);return h!==N.width||p!==N.height?{reset:{rects:!0}}:{}}}};function at(){return typeof window<"u"}function K(t){return Wt(t)?(t.nodeName||"").toLowerCase():"#document"}function C(t){var e;return(t==null||(e=t.ownerDocument)==null?void 0:e.defaultView)||window}function T(t){var e;return(e=(Wt(t)?t.ownerDocument:t.document)||window.document)==null?void 0:e.documentElement}function Wt(t){return at()?t instanceof Node||t instanceof C(t).Node:!1}function L(t){return at()?t instanceof Element||t instanceof C(t).Element:!1}function _(t){return at()?t instanceof HTMLElement||t instanceof C(t).HTMLElement:!1}function It(t){return!at()||typeof ShadowRoot>"u"?!1:t instanceof ShadowRoot||t instanceof C(t).ShadowRoot}function U(t){let{overflow:e,overflowX:n,overflowY:i,display:o}=S(t);return/auto|scroll|overlay|hidden|clip/.test(e+i+n)&&o!=="inline"&&o!=="contents"}function Ht(t){return/^(table|td|th)$/.test(K(t))}function rt(t){try{if(t.matches(":popover-open"))return!0}catch{}try{return t.matches(":modal")}catch{return!1}}var Oe=/transform|translate|scale|rotate|perspective|filter/,Ce=/paint|layout|strict|content/,z=t=>!!t&&t!=="none",yt;function ft(t){let e=L(t)?S(t):t;return z(e.transform)||z(e.translate)||z(e.scale)||z(e.rotate)||z(e.perspective)||!ut()&&(z(e.backdropFilter)||z(e.filter))||Oe.test(e.willChange||"")||Ce.test(e.contain||"")}function zt(t){let e=V(t);for(;_(e)&&!Y(e);){if(ft(e))return e;if(rt(e))return null;e=V(e)}return null}function ut(){return yt==null&&(yt=typeof CSS<"u"&&CSS.supports&&CSS.supports("-webkit-backdrop-filter","none")),yt}function Y(t){return/^(html|body|#document)$/.test(K(t))}function S(t){return C(t).getComputedStyle(t)}function lt(t){return L(t)?{scrollLeft:t.scrollLeft,scrollTop:t.scrollTop}:{scrollLeft:t.scrollX,scrollTop:t.scrollY}}function V(t){if(K(t)==="html")return t;let e=t.assignedSlot||t.parentNode||It(t)&&t.host||T(t);return It(e)?e.host:e}function Kt(t){let e=V(t);return Y(e)?t.ownerDocument?t.ownerDocument.body:t.body:_(e)&&U(e)?e:Kt(e)}function j(t,e,n){var i;e===void 0&&(e=[]),n===void 0&&(n=!0);let o=Kt(t),s=o===((i=t.ownerDocument)==null?void 0:i.body),l=C(o);if(s){let r=dt(l);return e.concat(l,l.visualViewport||[],U(o)?o:[],r&&n?j(r):[])}else return e.concat(o,j(o,[],n))}function dt(t){return t.parent&&Object.getPrototypeOf(t.parent)?t.frameElement:null}function Gt(t){let e=S(t),n=parseFloat(e.width)||0,i=parseFloat(e.height)||0,o=_(t),s=o?t.offsetWidth:n,l=o?t.offsetHeight:i,r=ot(n)!==s||ot(i)!==l;return r&&(n=s,i=l),{width:n,height:i,$:r}}function bt(t){return L(t)?t:t.contextElement}function Z(t){let e=bt(t);if(!_(e))return M(1);let n=e.getBoundingClientRect(),{width:i,height:o,$:s}=Gt(e),l=(s?ot(n.width):n.width)/i,r=(s?ot(n.height):n.height)/o;return(!l||!Number.isFinite(l))&&(l=1),(!r||!Number.isFinite(r))&&(r=1),{x:l,y:r}}var Le=M(0);function jt(t){let e=C(t);return!ut()||!e.visualViewport?Le:{x:e.visualViewport.offsetLeft,y:e.visualViewport.offsetTop}}function Se(t,e,n){return e===void 0&&(e=!1),!n||e&&n!==C(t)?!1:e}function X(t,e,n,i){e===void 0&&(e=!1),n===void 0&&(n=!1);let o=t.getBoundingClientRect(),s=bt(t),l=M(1);e&&(i?L(i)&&(l=Z(i)):l=Z(t));let r=Se(s,n,i)?jt(s):M(0),c=(o.left+r.x)/l.x,f=(o.top+r.y)/l.y,a=o.width/l.x,d=o.height/l.y;if(s){let m=C(s),u=i&&L(i)?C(i):i,h=m,p=dt(h);for(;p&&i&&u!==h;){let w=Z(p),g=p.getBoundingClientRect(),v=S(p),y=g.left+(p.clientLeft+parseFloat(v.paddingLeft))*w.x,x=g.top+(p.clientTop+parseFloat(v.paddingTop))*w.y;c*=w.x,f*=w.y,a*=w.x,d*=w.y,c+=y,f+=x,h=C(p),p=dt(h)}}return H({width:a,height:d,x:c,y:f})}function ht(t,e){let n=lt(t).scrollLeft;return e?e.left+n:X(T(t)).left+n}function Ut(t,e){let n=t.getBoundingClientRect(),i=n.left+e.scrollLeft-ht(t,n),o=n.top+e.scrollTop;return{x:i,y:o}}function Re(t){let{elements:e,rect:n,offsetParent:i,strategy:o}=t,s=o==="fixed",l=T(i),r=e?rt(e.floating):!1;if(i===l||r&&s)return n;let c={scrollLeft:0,scrollTop:0},f=M(1),a=M(0),d=_(i);if((d||!d&&!s)&&((K(i)!=="body"||U(l))&&(c=lt(i)),d)){let u=X(i);f=Z(i),a.x=u.x+i.clientLeft,a.y=u.y+i.clientTop}let m=l&&!d&&!s?Ut(l,c):M(0);return{width:n.width*f.x,height:n.height*f.y,x:n.x*f.x-c.scrollLeft*f.x+a.x+m.x,y:n.y*f.y-c.scrollTop*f.y+a.y+m.y}}function Pe(t){return Array.from(t.getClientRects())}function Me(t){let e=T(t),n=lt(t),i=t.ownerDocument.body,o=E(e.scrollWidth,e.clientWidth,i.scrollWidth,i.clientWidth),s=E(e.scrollHeight,e.clientHeight,i.scrollHeight,i.clientHeight),l=-n.scrollLeft+ht(t),r=-n.scrollTop;return S(i).direction==="rtl"&&(l+=E(e.clientWidth,i.clientWidth)-o),{width:o,height:s,x:l,y:r}}var Yt=25;function Te(t,e){let n=C(t),i=T(t),o=n.visualViewport,s=i.clientWidth,l=i.clientHeight,r=0,c=0;if(o){s=o.width,l=o.height;let a=ut();(!a||a&&e==="fixed")&&(r=o.offsetLeft,c=o.offsetTop)}let f=ht(i);if(f<=0){let a=i.ownerDocument,d=a.body,m=getComputedStyle(d),u=a.compatMode==="CSS1Compat"&&parseFloat(m.marginLeft)+parseFloat(m.marginRight)||0,h=Math.abs(i.clientWidth-d.clientWidth-u);h<=Yt&&(s-=h)}else f<=Yt&&(s+=f);return{width:s,height:l,x:r,y:c}}function $e(t,e){let n=X(t,!0,e==="fixed"),i=n.top+t.clientTop,o=n.left+t.clientLeft,s=_(t)?Z(t):M(1),l=t.clientWidth*s.x,r=t.clientHeight*s.y,c=o*s.x,f=i*s.y;return{width:l,height:r,x:c,y:f}}function Xt(t,e,n){let i;if(e==="viewport")i=Te(t,n);else if(e==="document")i=Me(T(t));else if(L(e))i=$e(e,n);else{let o=jt(t);i={x:e.x-o.x,y:e.y-o.y,width:e.width,height:e.height}}return H(i)}function Zt(t,e){let n=V(t);return n===e||!L(n)||Y(n)?!1:S(n).position==="fixed"||Zt(n,e)}function _e(t,e){let n=e.get(t);if(n)return n;let i=j(t,[],!1).filter(r=>L(r)&&K(r)!=="body"),o=null,s=S(t).position==="fixed",l=s?V(t):t;for(;L(l)&&!Y(l);){let r=S(l),c=ft(l);!c&&r.position==="fixed"&&(o=null),(s?!c&&!o:!c&&r.position==="static"&&!!o&&(o.position==="absolute"||o.position==="fixed")||U(l)&&!c&&Zt(t,l))?i=i.filter(a=>a!==l):o=r,l=V(l)}return e.set(t,i),i}function De(t){let{element:e,boundary:n,rootBoundary:i,strategy:o}=t,l=[...n==="clippingAncestors"?rt(e)?[]:_e(e,this._c):[].concat(n),i],r=Xt(e,l[0],o),c=r.top,f=r.right,a=r.bottom,d=r.left;for(let m=1;m{l(!1,1e-7)},1e3)}O===1&&!Qt(f,t.getBoundingClientRect())&&l(),x=!1}try{n=new IntersectionObserver(b,{...y,root:o.ownerDocument})}catch{n=new IntersectionObserver(b,y)}n.observe(t)}return l(!0),s}function te(t,e,n,i){i===void 0&&(i={});let{ancestorScroll:o=!0,ancestorResize:s=!0,elementResize:l=typeof ResizeObserver=="function",layoutShift:r=typeof IntersectionObserver=="function",animationFrame:c=!1}=i,f=bt(t),a=o||s?[...f?j(f):[],...e?j(e):[]]:[];a.forEach(g=>{o&&g.addEventListener("scroll",n,{passive:!0}),s&&g.addEventListener("resize",n)});let d=f&&r?Ie(f,n):null,m=-1,u=null;l&&(u=new ResizeObserver(g=>{let[v]=g;v&&v.target===f&&u&&e&&(u.unobserve(e),cancelAnimationFrame(m),m=requestAnimationFrame(()=>{var y;(y=u)==null||y.observe(e)})),n()}),f&&!c&&u.observe(f),e&&u.observe(e));let h,p=c?X(t):null;c&&w();function w(){let g=X(t);p&&!Qt(p,g)&&n(),p=g,h=requestAnimationFrame(w)}return n(),()=>{var g;a.forEach(v=>{o&&v.removeEventListener("scroll",n),s&&v.removeEventListener("resize",n)}),d?.(),(g=u)==null||g.disconnect(),u=null,c&&cancelAnimationFrame(h)}}var ee=kt;var ne=Bt,ie=Vt,oe=Nt;var se=(t,e,n)=>{let i=new Map,o={platform:Ne,...n},s={...o.platform,_c:i};return Ft(t,e,{...o,platform:s})};var We=t=>{let e=t?.parentElement;for(;e;){let n=getComputedStyle(e);if(/(auto|scroll|overlay)/.test(n.overflowY)&&e.scrollHeight>e.clientHeight)return e;e=e.parentElement}return null},re=t=>{let e=null,n=0,i=null,o=()=>i?i.getBoundingClientRect():{top:0,bottom:window.innerHeight},s=r=>i?i.scrollTop+=r:window.scrollBy(0,r),l=()=>{let{top:r,bottom:c}=o(),f=r+60-n,a=n-(c-60);f>0?s(-Math.min(22,f/60*22)):a>0&&s(Math.min(22,a/60*22)),e=requestAnimationFrame(l)};return{start(){i=We(t),e===null&&(e=requestAnimationFrame(l))},update(r){n=r},stop(){e!==null&&cancelAnimationFrame(e),e=null}}};var He=t=>{let e=t?.tBodies?.[0];return e?Array.from(e.children).filter(n=>n.matches("tr[data-row-key]")):[]},le=t=>{let e=[];try{e=JSON.parse(t.dataset.fillColumns||"[]")}catch{e=[]}let n=t.querySelector("table"),i={columns:e,rows:()=>He(n),columnAt:o=>e[o]??null,colOf:o=>{let s=e.indexOf(o);return s===-1?null:s},cellAt(o,s){let l=e[s];return l==null?null:this.rows()[o]?.querySelector(`:scope > td[data-column="${CSS.escape(l)}"]`)??null},rootIn(o){return o?.querySelector("[data-record-key][data-column-name]")??null},describe(o,s){let l=this.cellAt(o,s),r=this.rootIn(l);return!l||!r?null:{row:o,col:s,cell:l,el:r,recordKey:r.dataset.recordKey,version:r.dataset.recordVersion||null,serialized:r.dataset.serverValue??""}},locate(o){let s=o?.closest?.("td[data-column]"),l=s?.closest("tr[data-row-key]");if(!s||!l)return null;let r=i.colOf(s.dataset.column),c=i.rows().indexOf(l);return r===null||c===-1?null:{row:c,col:r}},rowAtY(o){let s=this.rows();if(s.length===0)return null;let l=0;for(let r=0;r=c.top&&o<=c.bottom)return r;o>c.bottom&&(l=r)}return o({anchor:t,focus:e}),ce=t=>({anchor:t.anchor,focus:{row:t.focus.row,col:t.anchor.col}}),Et=t=>({top:Math.min(t.anchor.row,t.focus.row),bottom:Math.max(t.anchor.row,t.focus.row),left:Math.min(t.anchor.col,t.focus.col),right:Math.max(t.anchor.col,t.focus.col)}),Ot=t=>t.anchor.row===t.focus.row&&t.anchor.col===t.focus.col,Ct=t=>{let{top:e,bottom:n,left:i,right:o}=Et(t),s=[];for(let l=e;l<=n;l++)for(let r=i;r<=o;r++)l===t.anchor.row&&r===t.anchor.col||s.push({row:l,col:r});return s};var ae="wire-fill-target",fe=18,ue=12,Lt=new Set,de=!1,ze=()=>{de||!window.Livewire||(de=!0,window.Livewire.hook("morph.updating",({skip:t})=>{Lt.size>0&&t()}))},Ke=()=>({grid:null,handle:null,overlay:null,scroller:null,max:1/0,active:null,range:null,dragging:!1,painted:[],_pending:null,init(){this.grid=le(this.$el),this.handle=this.$el.querySelector("[data-fill-handle]"),this.overlay=this.$el.querySelector("[data-fill-overlay]"),this.scroller=re(this.$el);let t=parseInt(this.$el.dataset.fillMax||"",10);this.max=Number.isFinite(t)&&t>0?t:1/0,ze(),this._onFocusIn=e=>this.onFocusIn(e),this._onPointerOver=e=>this.onHover(e),this._onPointerLeave=()=>this.onLeave(),this._onPointerDown=e=>this.startDrag(e),this._reposition=()=>{this.active&&!this.dragging&&this.place()},this.$el.addEventListener("focusin",this._onFocusIn),this.$el.addEventListener("pointerover",this._onPointerOver),this.$el.addEventListener("pointerleave",this._onPointerLeave),this.handle?.addEventListener("pointerdown",this._onPointerDown),window.addEventListener("resize",this._reposition),window.addEventListener("scroll",this._reposition,!0)},destroy(){this.stopDrag(),this.$el.removeEventListener("focusin",this._onFocusIn),this.$el.removeEventListener("pointerover",this._onPointerOver),this.$el.removeEventListener("pointerleave",this._onPointerLeave),this.handle?.removeEventListener("pointerdown",this._onPointerDown),window.removeEventListener("resize",this._reposition),window.removeEventListener("scroll",this._reposition,!0)},onFocusIn(t){if(this.dragging||this.handle?.contains(t.target))return;let e=this.grid.locate(t.target),n=e&&this.grid.describe(e.row,e.col);if(!n||this.isLocked(n)){this.deactivate();return}this.active=e,this.place()},onHover(t){if(this.dragging||this.handle?.contains(t.target)||this.withinGrabRadius(t)||this.focusedPoint())return;let e=this.grid.locate(t.target),n=e&&this.grid.describe(e.row,e.col);!n||this.isLocked(n)||(this.active=e,this.place())},withinGrabRadius(t){if(!this.handle||this.handle.hidden)return!1;let e=this.handle.getBoundingClientRect();return Math.abs(t.clientX-(e.left+e.width/2))<=fe&&Math.abs(t.clientY-(e.top+e.height/2))<=fe},onLeave(){this.dragging||this.focusedPoint()||this.deactivate()},focusedPoint(){let t=document.activeElement;return!t||!this.$el.contains(t)?null:this.grid.locate(t)},isLocked(t){return!!t.el.querySelector("input, select, textarea, button")?.disabled},deactivate(){this.active=null,this.handle&&(this.handle.hidden=!0)},place(){let t=this.active&&this.grid.describe(this.active.row,this.active.col);if(!t||!this.handle){this.deactivate();return}let e=this.$el.getBoundingClientRect(),n=t.cell.getBoundingClientRect();this.handle.style.left=`${n.right-e.left+this.$el.scrollLeft-ue}px`,this.handle.style.top=`${n.bottom-e.top+this.$el.scrollTop-ue}px`,this.handle.hidden=!1},startDrag(t){if(this.active){t.preventDefault();try{this.handle.setPointerCapture?.(t.pointerId)}catch{}this.dragging=!0,this.range=At(this.active),Lt.add(this),document.body.classList.add("wire-filling"),this.scroller.start(),this._onMove=e=>this.onMove(e),this._onUp=e=>this.finish(e),this._onCancel=()=>this.cancel(),this._onKey=e=>{e.key==="Escape"&&this.cancel()},window.addEventListener("pointermove",this._onMove),window.addEventListener("pointerup",this._onUp),window.addEventListener("pointercancel",this._onCancel),window.addEventListener("keydown",this._onKey)}},onMove(t){if(!this.dragging)return;this.scroller.update(t.clientY);let e=this.grid.rowAtY(t.clientY);if(e===null)return;let n=this.range.anchor.row,i=e>=n?Math.min(e,n+this.max):Math.max(e,n-this.max);this.range=ce(At(this.range.anchor,{row:i,col:this.range.anchor.col})),this.paint()},paint(){this.clearPaint();for(let t of Ct(this.range)){let e=this.grid.describe(t.row,t.col);!e||this.isLocked(e)||(e.cell.classList.add(ae),this.painted.push(e.cell))}this.placeOverlay()},clearPaint(){for(let t of this.painted)t.classList.remove(ae);this.painted=[]},placeOverlay(){if(!this.overlay)return;let t=Et(this.range),e=this.grid.cellAt(t.top,t.left),n=this.grid.cellAt(t.bottom,t.right);if(!e||!n)return;let i=this.$el.getBoundingClientRect(),o=e.getBoundingClientRect(),s=n.getBoundingClientRect();this.overlay.style.left=`${o.left-i.left+this.$el.scrollLeft}px`,this.overlay.style.top=`${o.top-i.top+this.$el.scrollTop}px`,this.overlay.style.width=`${s.right-o.left}px`,this.overlay.style.height=`${s.bottom-o.top}px`,this.overlay.hidden=Ot(this.range)},stopDrag(){this.dragging&&(window.removeEventListener("pointermove",this._onMove),window.removeEventListener("pointerup",this._onUp),window.removeEventListener("pointercancel",this._onCancel),window.removeEventListener("keydown",this._onKey)),this.dragging=!1,Lt.delete(this),document.body.classList.remove("wire-filling"),this.scroller?.stop(),this.clearPaint(),this.overlay&&(this.overlay.hidden=!0)},cancel(){this.stopDrag(),this.range=null},finish(){let t=this.range,e=this.active&&this.grid.describe(this.active.row,this.active.col);if(this.stopDrag(),this.range=null,!t||Ot(t)||!e)return;let n=Ct(t).map(i=>this.grid.describe(i.row,i.col)).filter(i=>i&&!this.isLocked(i));n.length!==0&&this.write(this.grid.columnAt(e.col),e,n)},write(t,e,n){let i=this.liveValue(e),o=new Map;for(let s of n)o.set(s.recordKey,{value:this.liveValue(s),version:s.version}),this.applyValue(s.el,i);return this._pending=(this._pending??Promise.resolve()).catch(()=>{}).then(()=>this.send(t,i,n,o)),this._pending},async send(t,e,n,i){let o={};for(let r of n)o[r.recordKey]=r.el.dataset.recordVersion||null;let s=null;try{s=await this.$wire.fillTableCells([{column:t,value:e,records:o}])}catch{s=null}let l=s?.results?.[t]??null;for(let r of n){let c=l?.[r.recordKey];if(c?.success){this.applyVersion(r.el,c.version),this.announce(r,t,c.version);continue}let f=i.get(r.recordKey);if(c?.conflict){let a=this.stateOf(r.el);this.applyValue(r.el,a?a.parse(c.currentValue):c.currentValue),this.applyVersion(r.el,c.currentVersion)}else this.applyValue(r.el,f.value),this.applyVersion(r.el,f.version)}},stateOf(t){try{return window.Alpine.$data(t)}catch{return null}},liveValue(t){let e=this.stateOf(t.el);return e?e.value:t.serialized},applyValue(t,e){t.dataset.serverValue=this.serialize(e);let n=this.stateOf(t);n&&(n.value=e,n.serverValue=e,n.error=null)},serialize(t){return typeof t=="boolean"?t?"1":"0":t==null?"":String(t)},applyVersion(t,e){if(!e)return;t.dataset.recordVersion=e;let n=this.stateOf(t);n&&(n.recordVersion=e)},announce(t,e,n){n&&window.dispatchEvent(new CustomEvent("wire-editable-committed",{detail:{componentId:this.$el.closest("[wire\\:id]")?.getAttribute("wire:id")??null,recordKey:t.recordKey,column:e,version:n}}))}}),he=Ke;var Ye=1,Xe=t=>{let e=null;for(let n=t.parentElement;n&&n!==document.body;n=n.parentElement){let i=parseInt(getComputedStyle(n).zIndex,10);Number.isNaN(i)||(e=i)}return e===null?null:e+Ye},me=(t,e,n={})=>{if(!t||!e)return()=>{};let i=n.placement||"bottom-end",o=n.offset??6,s=n.matchWidth??!1,l=n.sheetBreakpoint??640,r=n.sheetOnMobile?window.matchMedia(`(max-width: ${l-.02}px)`):null,c=parseFloat(getComputedStyle(e).maxHeight),f=Number.isNaN(c)?1/0:c,a=parseInt(getComputedStyle(e).zIndex,10),d=Number.isNaN(a)?-1/0:a,m=[ee(o),ie(),ne({padding:8}),oe({padding:8,apply({availableHeight:b,rects:A,elements:O}){Object.assign(O.floating.style,{maxHeight:`${Math.round(Math.min(b,f))}px`,overflowY:"auto"}),s&&(O.floating.style.minWidth=`${A.reference.width}px`)}})],u=null,h=()=>{!t.isConnected||!e.isConnected||se(t,e,{placement:i,middleware:m}).then(({x:b,y:A})=>{Object.assign(e.style,{left:`${b}px`,top:`${A}px`}),u!==null&&(e.style.zIndex=`${u}`)})},p=null,w=null,g=()=>!!r&&r.matches,v=()=>{if(g()){Object.assign(e.style,{position:"",top:"",left:"",maxHeight:"",overflowY:"",minWidth:"",zIndex:""}),u=null;return}let b=Xe(t);u=b!==null&&b>d?b:null,Object.assign(e.style,{position:"absolute",top:"0",left:"0"}),u!==null&&(e.style.zIndex=`${u}`),p=te(t,e,h),w=new MutationObserver(A=>{A.every(R=>R.target===e&&R.type==="attributes"&&R.attributeName==="style")||h()}),w.observe(e,{childList:!0,subtree:!0,attributes:!0})},y=()=>{p&&(p(),p=null),w&&(w.disconnect(),w=null)},x=()=>{y(),v()};return r?.addEventListener("change",x),v(),()=>{y(),r?.removeEventListener("change",x)}},qe=(t,e)=>{if(!t||!e)return!1;let n=e;for(;n;){if(n===t)return!0;n=n._x_teleportBack??n.parentElement}return!1},Ge=(t={},e=null)=>({open:!1,_cleanup:null,items:e,_wire:null,init(){this._wireId=(this.$root??this.$el)?.closest("[wire\\:id]")?.getAttribute("wire:id")??null},runAction(n){if(!n||!n.method||!this._wireId)return;let i=window.Livewire?.find(this._wireId);i&&typeof i.call=="function"&&i.call(n.method,...n.args||[])},toggle(){this.open?this.close():this.show()},show(){this.open=!0,this.$nextTick(()=>{this._cleanup=me(this.$refs.trigger,this.$refs.panel,t)})},close(){this.open=!1,this.stop()},stop(){this._cleanup&&(this._cleanup(),this._cleanup=null)},destroy(){this.stop()}}),je=t=>{t.directive("sheet-dismiss",(e,{expression:n},{evaluateLater:i,cleanup:o})=>{let s=i(n),l=()=>e.parentElement,r=()=>window.matchMedia(`(max-width: ${parseFloat(e.dataset.sheetBp)||639.98}px)`).matches,c=0,f=0,a=!1,d=h=>{if(!r())return;a=!0,f=0,c=h.touches[0].clientY;let p=l();p&&(p.style.transition="none")},m=h=>{if(!a)return;f=Math.max(0,h.touches[0].clientY-c);let p=l();p&&(p.style.transform=`translateY(${f}px)`)},u=()=>{if(!a)return;a=!1;let h=l();h&&(h.style.transition="",h.style.transform=""),f>90&&s(()=>{})};e.addEventListener("touchstart",d,{passive:!0}),e.addEventListener("touchmove",m,{passive:!0}),e.addEventListener("touchend",u),o(()=>{e.removeEventListener("touchstart",d),e.removeEventListener("touchmove",m),e.removeEventListener("touchend",u)})})},Ue='a[href],button:not([disabled]),input:not([disabled]),select:not([disabled]),textarea:not([disabled]),[tabindex]:not([tabindex="-1"])',Ze=t=>{t.directive("focus-trap",(e,{expression:n},{evaluateLater:i,effect:o,cleanup:s})=>{let l=i(n),r=()=>window.matchMedia(`(max-width: ${parseFloat(e.dataset.sheetBp)||639.98}px)`).matches,c=()=>[...e.querySelectorAll(Ue)].filter(h=>h.offsetParent!==null),f=null,a=null,d=!1,m=()=>{if(d||!r())return;d=!0,f=document.activeElement,e.setAttribute("aria-modal","true"),(c()[0]??e).focus({preventScroll:!0}),a=p=>{if(p.key!=="Tab")return;let w=c();if(w.length===0){p.preventDefault();return}let g=w[0],v=w[w.length-1];p.shiftKey&&document.activeElement===g?(p.preventDefault(),v.focus()):!p.shiftKey&&document.activeElement===v&&(p.preventDefault(),g.focus())},e.addEventListener("keydown",a)},u=()=>{if(!d)return;d=!1,e.removeAttribute("aria-modal"),a&&(e.removeEventListener("keydown",a),a=null);let h=f;f=null,h&&typeof h.focus=="function"&&requestAnimationFrame(()=>h.focus({preventScroll:!0}))};o(()=>{l(h=>{h?requestAnimationFrame(m):u()})}),s(u)})},Je=(t=0)=>({tabs:[],active:t,registerTab(e){return this.tabs.push(e),this.tabs.length-1}}),Qe=(t=0)=>({steps:[],current:t,registerStep(e){return this.steps.push(e),this.steps.length-1},get isFirst(){return this.current===0},get isLast(){return this.current>=this.steps.length-1},next(){this.isLast||this.current++},prev(){this.isFirst||this.current--}}),tn=(t={})=>({value:t.value,serverValue:t.value,recordVersion:t.recordVersion??"0",commitMethod:t.commitMethod??"updateTableCell",validateMethod:t.validateMethod??"validateTableCell",recordKey:null,columnName:null,componentId:null,saving:!1,error:null,success:!1,focused:!1,get dirty(){return this.value!==this.serverValue},parse(e){return t.parse?t.parse(e):e},messages:{},init(){this.recordKey=this.$el.dataset.recordKey,this.columnName=this.$el.dataset.columnName,this.messages={error:this.messages.error,saveFailed:this.messages.saveFailed,invalid:this.messages.invalid},t.liveValidation&&this.$watch("value",window.Alpine.debounce(()=>{this.dirty&&this.validate()},t.debounce??500));let e=new MutationObserver(n=>{for(let i of n)if(i.attributeName==="data-server-value"||i.attributeName==="data-record-version"){let o=this.parse(this.$el.dataset.serverValue);o!==this.serverValue&&this.syncFromServer(o,this.$el.dataset.recordVersion)}});e.observe(this.$el,{attributes:!0,attributeFilter:["data-server-value","data-record-version"]}),this._observer=e,this.componentId=this.$el.closest("[wire\\:id]")?.getAttribute("wire:id")??null,this._onSiblingCommit=n=>{let i=n.detail||{};i.componentId===this.componentId&&String(i.recordKey)===String(this.recordKey)&&i.column!==this.columnName&&(this.saving||this.focused&&this.dirty||i.version&&(this.recordVersion=i.version))},window.addEventListener("wire-editable-committed",this._onSiblingCommit)},destroy(){this._observer?.disconnect(),window.removeEventListener("wire-editable-committed",this._onSiblingCommit)},syncFromServer(e,n){this.saving||this.focused&&this.dirty||(this.value=e,this.serverValue=e,n&&(this.recordVersion=n),this.error=null)},onFocus(){this.focused=!0},onBlur(){this.focused=!1,t.saveOnBlur&&this.dirty&&this.save()},onEnter(){t.saveOnEnter&&this.dirty&&this.save()},onEscape(){this.value=this.serverValue,this.error=null,this.$refs.input?.blur()},save(){this.dirty&&this.commit(this.value)},async commit(e){if(!this.saving){this.value=e,this.saving=!0,this.error=null;try{let n=await this.$wire[this.commitMethod](this.recordKey,this.columnName,e,this.recordVersion);n?.success===!1?(this.value=this.serverValue,this.error=n.message||n.errors?.[0]||this.messages.error,n?.conflict&&(this.value=this.parse(n.currentValue),this.serverValue=this.value,this.recordVersion=n.currentVersion??this.recordVersion)):(this.serverValue=e,n?.version&&(this.recordVersion=n.version),this.success=!0,setTimeout(()=>{this.success=!1},1500),n?.version&&window.dispatchEvent(new CustomEvent("wire-editable-committed",{detail:{componentId:this.componentId,recordKey:this.recordKey,column:this.columnName,version:n.version}})))}catch{this.value=this.serverValue,this.error=this.messages.saveFailed}finally{this.saving=!1}}},async validate(){try{let e=await this.$wire[this.validateMethod](this.recordKey,this.columnName,this.value);this.error=e&&!e.valid?e.errors?.[0]||this.messages.invalid:null}catch{}}}),J=null,en=()=>({open:!1,x:0,y:0,openAt(t){J&&J!==this&&J.close(),J=this,this.x=t.clientX,this.y=t.clientY,this.open=!0,this.$nextTick(()=>this.place())},place(){let t=this.$refs.panel;if(!t)return;let e=8,{width:n,height:i}=t.getBoundingClientRect(),o=this.x,s=this.y;o+n+e>window.innerWidth&&(o=window.innerWidth-n-e),s+i+e>window.innerHeight&&(s=window.innerHeight-i-e),t.style.left=`${Math.max(e,o)}px`,t.style.top=`${Math.max(e,s)}px`},close(){this.open=!1,J===this&&(J=null)}});document.addEventListener("alpine:init",()=>{window.Alpine.magic("float",()=>me),window.Alpine.magic("clickedInside",t=>e=>qe(t,e?.target)),window.Alpine.data("wireDropdown",Ge),window.Alpine.data("wireContextMenu",en),window.Alpine.data("wireTabs",Je),window.Alpine.data("wireWizard",Qe),window.Alpine.data("wireEditableCell",tn),window.Alpine.data("wireFillHandle",he),je(window.Alpine),Ze(window.Alpine)});})();
+(()=>{var V=Math.min,R=Math.max,tt=Math.round,et=Math.floor,T=t=>({x:t,y:t}),ge={left:"right",right:"left",bottom:"top",top:"bottom"};function St(t,e,n){return R(t,V(e,n))}function X(t,e){return typeof t=="function"?t(e):t}function k(t){return t.split("-")[0]}function q(t){return t.split("-")[1]}function dt(t){return t==="x"?"y":"x"}function ht(t){return t==="y"?"height":"width"}function _(t){let e=t[0];return e==="t"||e==="b"?"y":"x"}function mt(t){return dt(_(t))}function Pt(t,e,n){n===void 0&&(n=!1);let i=q(t),o=mt(t),s=ht(o),l=o==="x"?i===(n?"end":"start")?"right":"left":i==="start"?"bottom":"top";return e.reference[s]>e.floating[s]&&(l=Q(l)),[l,Q(l)]}function $t(t){let e=Q(t);return[st(t),e,st(e)]}function st(t){return t.includes("start")?t.replace("start","end"):t.replace("end","start")}var Lt=["left","right"],Rt=["right","left"],we=["top","bottom"],ve=["bottom","top"];function ye(t,e,n){switch(t){case"top":case"bottom":return n?e?Rt:Lt:e?Lt:Rt;case"left":case"right":return e?we:ve;default:return[]}}function Tt(t,e,n,i){let o=q(t),s=ye(k(t),n==="start",i);return o&&(s=s.map(l=>l+"-"+o),e&&(s=s.concat(s.map(st)))),s}function Q(t){let e=k(t);return ge[e]+t.slice(e.length)}function xe(t){var e,n,i,o;return{top:(e=t.top)!=null?e:0,right:(n=t.right)!=null?n:0,bottom:(i=t.bottom)!=null?i:0,left:(o=t.left)!=null?o:0}}function _t(t){return typeof t!="number"?xe(t):{top:t,right:t,bottom:t,left:t}}function H(t){let{x:e,y:n,width:i,height:o}=t;return{width:i,height:o,top:n,left:e,right:e+i,bottom:n+o,x:e,y:n}}function Mt(t,e,n){let{reference:i,floating:o}=t,s=_(e),l=mt(e),r=ht(l),c=k(e),a=s==="y",f=i.x+i.width/2-o.width/2,d=i.y+i.height/2-o.height/2,m=i[r]/2-o[r]/2,u;switch(c){case"top":u={x:f,y:i.y-o.height};break;case"bottom":u={x:f,y:i.y+i.height};break;case"right":u={x:i.x+i.width,y:d};break;case"left":u={x:i.x-o.width,y:d};break;default:u={x:i.x,y:i.y}}let h=q(e);return h&&(u[l]+=m*(h==="end"?1:-1)*(n&&a?-1:1)),u}async function Dt(t,e){var n;e===void 0&&(e={});let{x:i,y:o,platform:s,rects:l,elements:r,strategy:c}=t,{boundary:a="clippingAncestors",rootBoundary:f="viewport",elementContext:d="floating",altBoundary:m=!1,padding:u=0}=X(e,t),h=_t(u),g=r[m?d==="floating"?"reference":"floating":d],w=H(await s.getClippingRect({element:(n=await(s.isElement==null?void 0:s.isElement(g)))==null||n?g:g.contextElement||await(s.getDocumentElement==null?void 0:s.getDocumentElement(r.floating)),boundary:a,rootBoundary:f,strategy:c})),v=d==="floating"?{x:i,y:o,width:l.floating.width,height:l.floating.height}:l.reference,y=await(s.getOffsetParent==null?void 0:s.getOffsetParent(r.floating)),x=await(s.isElement==null?void 0:s.isElement(y))&&await(s.getScale==null?void 0:s.getScale(y))||{x:1,y:1},A=H(s.convertOffsetParentRelativeRectToViewportRelativeRect?await s.convertOffsetParentRelativeRectToViewportRelativeRect({elements:r,rect:v,offsetParent:y,strategy:c}):v);return{top:(w.top-A.top+h.top)/x.y,bottom:(A.bottom-w.bottom+h.bottom)/x.y,left:(w.left-A.left+h.left)/x.x,right:(A.right-w.right+h.right)/x.x}}var be=50,Ft=async(t,e,n)=>{let{placement:i="bottom",strategy:o="absolute",middleware:s=[],platform:l}=n,r=l.detectOverflow?l:{...l,detectOverflow:Dt},c=await(l.isRTL==null?void 0:l.isRTL(e)),a=await l.getElementRects({reference:t,floating:e,strategy:o}),{x:f,y:d}=Mt(a,i,c),m=i,u=0,h={};for(let p=0;pI<=0)){var Et,Ot;let I=(((Et=s.flip)==null?void 0:Et.index)||0)+1,ut=C[I];if(ut&&(!(d==="alignment"?v!==_(ut):!1)||$.every(L=>_(L.placement)===v?L.overflows[0]>0:!0)))return{data:{index:I,overflows:$},reset:{placement:ut}};let J=(Ot=$.filter(W=>W.overflows[0]<=0).sort((W,L)=>W.overflows[1]-L.overflows[1])[0])==null?void 0:Ot.placement;if(!J)switch(u){case"bestFit":{var Ct;let W=(Ct=$.filter(L=>{if(b){let F=_(L.placement);return F===v||F==="y"}return!0}).map(L=>[L.placement,L.overflows.filter(F=>F>0).reduce((F,pe)=>F+pe,0)]).sort((L,F)=>L[1]-F[1])[0])==null?void 0:Ct[0];W&&(J=W);break}case"initialPlacement":J=r;break}if(o!==J)return{reset:{placement:J}}}return{}}}};var Ae=new Set(["left","top"]);async function Ee(t,e){let{placement:n,platform:i,elements:o}=t,s=await(i.isRTL==null?void 0:i.isRTL(o.floating)),l=k(n),r=q(n),c=_(n)==="y",a=Ae.has(l)?-1:1,f=s&&c?-1:1,d=X(e,t),{mainAxis:m,crossAxis:u,alignmentAxis:h}=typeof d=="number"?{mainAxis:d,crossAxis:0,alignmentAxis:null}:{mainAxis:d.mainAxis||0,crossAxis:d.crossAxis||0,alignmentAxis:d.alignmentAxis};return r&&typeof h=="number"&&(u=r==="end"?h*-1:h),c?{x:u*f,y:m*a}:{x:m*a,y:u*f}}var kt=function(t){return t===void 0&&(t=0),{name:"offset",options:t,async fn(e){var n,i;let{x:o,y:s,placement:l,middlewareData:r}=e,c=await Ee(e,t);return l===((n=r.offset)==null?void 0:n.placement)&&(i=r.arrow)!=null&&i.alignmentOffset?{}:{x:o+c.x,y:s+c.y,data:{...c,placement:l}}}}},Nt=function(t){return t===void 0&&(t={}),{name:"shift",options:t,async fn(e){let{x:n,y:i,placement:o,platform:s}=e,{mainAxis:l=!0,crossAxis:r=!1,limiter:c={fn:v=>{let{x:y,y:x}=v;return{x:y,y:x}}},...a}=X(t,e),f={x:n,y:i},d=await s.detectOverflow(e,a),m=_(o),u=dt(m),h=f[u],p=f[m],g=(v,y)=>St(y+d[v==="y"?"top":"left"],y,y-d[v==="y"?"bottom":"right"]);l&&(h=g(u,h)),r&&(p=g(m,p));let w=c.fn({...e,[u]:h,[m]:p});return{...w,data:{x:w.x-n,y:w.y-i,enabled:{[u]:l,[m]:r}}}}}};var Bt=function(t){return t===void 0&&(t={}),{name:"size",options:t,async fn(e){let{placement:n,rects:i,platform:o,elements:s}=e,{apply:l=()=>{},...r}=X(t,e),c=await o.detectOverflow(e,r),a=k(n),f=q(n),d=_(n)==="y",{width:m,height:u}=i.floating,h,p;a==="top"||a==="bottom"?(h=a,p=f===(await(o.isRTL==null?void 0:o.isRTL(s.floating))?"start":"end")?"left":"right"):(p=a,h=f==="end"?"top":"bottom");let g=u-c.top-c.bottom,w=m-c.left-c.right,v=V(u-c[h],g),y=V(m-c[p],w),x=e.middlewareData.shift,A=!x,b=v,C=y;x!=null&&x.enabled.x&&(C=w),x!=null&&x.enabled.y&&(b=g),A&&!f&&(d?C=m-2*R(c.left,c.right):b=u-2*R(c.top,c.bottom)),await l({...e,availableWidth:C,availableHeight:b});let O=await o.getDimensions(s.floating);return m!==O.width||u!==O.height?{reset:{rects:!0}}:{}}}};function rt(){return typeof window<"u"}function K(t){return Wt(t)?(t.nodeName||"").toLowerCase():"#document"}function E(t){var e;return(t==null||(e=t.ownerDocument)==null?void 0:e.defaultView)||window}function M(t){var e;return(e=(Wt(t)?t.ownerDocument:t.document)||window.document)==null?void 0:e.documentElement}function Wt(t){return rt()?t instanceof Node||t instanceof E(t).Node:!1}function S(t){return rt()?t instanceof Element||t instanceof E(t).Element:!1}function D(t){return rt()?t instanceof HTMLElement||t instanceof E(t).HTMLElement:!1}function It(t){return!rt()||typeof ShadowRoot>"u"?!1:t instanceof ShadowRoot||t instanceof E(t).ShadowRoot}function nt(t){let{overflow:e,overflowX:n,overflowY:i,display:o}=P(t);return/auto|scroll|overlay|hidden|clip/.test(e+i+n)&&o!=="inline"&&o!=="contents"}function Ht(t){return/^(table|td|th)$/.test(K(t))}function it(t){try{if(t.matches(":popover-open"))return!0}catch{}try{return t.matches(":modal")}catch{return!1}}var Oe=/transform|translate|scale|rotate|perspective|filter/,Ce=/paint|layout|strict|content/,z=t=>!!t&&t!=="none",pt;function lt(t){let e=S(t)?P(t):t;return z(e.transform)||z(e.translate)||z(e.scale)||z(e.rotate)||z(e.perspective)||!ct()&&(z(e.backdropFilter)||z(e.filter))||Oe.test(e.willChange||"")||Ce.test(e.contain||"")}function zt(t){let e=N(t);for(;D(e)&&!j(e);){if(lt(e))return e;if(it(e))return null;e=N(e)}return null}function ct(){return pt==null&&(pt=typeof CSS<"u"&&CSS.supports&&CSS.supports("-webkit-backdrop-filter","none")),pt}function j(t){return/^(html|body|#document)$/.test(K(t))}function P(t){return E(t).getComputedStyle(t)}function ot(t){return S(t)?{scrollLeft:t.scrollLeft,scrollTop:t.scrollTop}:{scrollLeft:t.scrollX,scrollTop:t.scrollY}}function N(t){if(K(t)==="html")return t;let e=t.assignedSlot||t.parentNode||It(t)&&t.host||M(t);return It(e)?e.host:e}function Kt(t){let e=N(t);return j(e)?(t.ownerDocument||t).body:D(e)&&nt(e)?e:Kt(e)}function G(t,e,n){var i;e===void 0&&(e=[]),n===void 0&&(n=!0);let o=Kt(t),s=o===((i=t.ownerDocument)==null?void 0:i.body),l=E(o);if(s){let r=at(l);return e.concat(l,l.visualViewport||[],nt(o)?o:[],r&&n?G(r):[])}else return e.concat(o,G(o,[],n))}function at(t){return t.parent&&Object.getPrototypeOf(t.parent)?t.frameElement:null}function qt(t){let e=P(t),n=parseFloat(e.width)||0,i=parseFloat(e.height)||0,o=D(t),s=o?t.offsetWidth:n,l=o?t.offsetHeight:i,r=tt(n)!==s||tt(i)!==l;return r&&(n=s,i=l),{width:n,height:i,$:r}}function wt(t){return S(t)?t:t.contextElement}function U(t){let e=wt(t);if(!D(e))return T(1);let n=e.getBoundingClientRect(),{width:i,height:o,$:s}=qt(e),l=(s?tt(n.width):n.width)/i,r=(s?tt(n.height):n.height)/o;return(!l||!Number.isFinite(l))&&(l=1),(!r||!Number.isFinite(r))&&(r=1),{x:l,y:r}}var Le=T(0);function Gt(t){let e=E(t);return!ct()||!e.visualViewport?Le:{x:e.visualViewport.offsetLeft,y:e.visualViewport.offsetTop}}function Re(t,e,n){return e===void 0&&(e=!1),!!n&&e&&n===E(t)}function Y(t,e,n,i){e===void 0&&(e=!1),n===void 0&&(n=!1);let o=t.getBoundingClientRect(),s=wt(t),l=T(1);e&&(i?S(i)&&(l=U(i)):l=U(t));let r=Re(s,n,i)?Gt(s):T(0),c=(o.left+r.x)/l.x,a=(o.top+r.y)/l.y,f=o.width/l.x,d=o.height/l.y;if(s&&i){let m=E(s),u=S(i)?E(i):i,h=m,p=at(h);for(;p&&u!==h;){let g=U(p),w=p.getBoundingClientRect(),v=P(p),y=w.left+(p.clientLeft+parseFloat(v.paddingLeft))*g.x,x=w.top+(p.clientTop+parseFloat(v.paddingTop))*g.y;c*=g.x,a*=g.y,f*=g.x,d*=g.y,c+=y,a+=x,h=E(p),p=at(h)}}return H({width:f,height:d,x:c,y:a})}function ft(t,e){let n=ot(t).scrollLeft;return e?e.left+n:Y(M(t)).left+n}function jt(t,e){let n=t.getBoundingClientRect(),i=n.left+e.scrollLeft-ft(t,n),o=n.top+e.scrollTop;return{x:i,y:o}}function Se(t){let{elements:e,rect:n,offsetParent:i,strategy:o}=t,s=o==="fixed",l=M(i),r=e?it(e.floating):!1;if(i===l||r&&s)return n;let c={scrollLeft:0,scrollTop:0},a=T(1),f=T(0),d=D(i);if((d||!s)&&((K(i)!=="body"||nt(l))&&(c=ot(i)),d)){let u=Y(i);a=U(i),f.x=u.x+i.clientLeft,f.y=u.y+i.clientTop}let m=l&&!d&&!s?jt(l,c):T(0);return{width:n.width*a.x,height:n.height*a.y,x:n.x*a.x-c.scrollLeft*a.x+f.x+m.x,y:n.y*a.y-c.scrollTop*a.y+f.y+m.y}}function Pe(t){return t.getClientRects?Array.from(t.getClientRects()):[]}function $e(t){let e=ot(t),n=t.ownerDocument.body,i=R(t.scrollWidth,t.clientWidth,n.scrollWidth,n.clientWidth),o=R(t.scrollHeight,t.clientHeight,n.scrollHeight,n.clientHeight),s=-e.scrollLeft+ft(t),l=-e.scrollTop;return P(n).direction==="rtl"&&(s+=R(t.clientWidth,n.clientWidth)-i),{width:i,height:o,x:s,y:l}}var Te=25;function _e(t,e,n){n===void 0&&(n="viewport");let i=n==="layoutViewport",o=E(t),s=M(t),l=o.visualViewport,r=s.clientWidth,c=s.clientHeight,a=0,f=0;if(l){let m=!ct()||e==="fixed";i?m||(a=-l.offsetLeft,f=-l.offsetTop):(r=l.width,c=l.height,m&&(a=l.offsetLeft,f=l.offsetTop))}if(ft(s)<=0){let m=s.ownerDocument,u=m.body,h=getComputedStyle(u),p=m.compatMode==="CSS1Compat"&&parseFloat(h.marginLeft)+parseFloat(h.marginRight)||0,g=Math.abs(s.clientWidth-u.clientWidth-p),w=getComputedStyle(s).scrollbarGutter==="stable both-edges"?g/2:g;w<=Te&&(r-=w)}return{width:r,height:c,x:a,y:f}}function Me(t,e){let n=Y(t,!0,e==="fixed"),i=n.top+t.clientTop,o=n.left+t.clientLeft,s=U(t),l=t.clientWidth*s.x,r=t.clientHeight*s.y,c=o*s.x,a=i*s.y;return{width:l,height:r,x:c,y:a}}function Yt(t,e,n){let i;if(e==="viewport"||e==="layoutViewport")i=_e(t,n,e);else if(e==="document")i=$e(M(t));else if(S(e))i=Me(e,n);else{let o=Gt(t);i={x:e.x-o.x,y:e.y-o.y,width:e.width,height:e.height}}return H(i)}function De(t,e){let n=e.get(t);if(n)return n;let i=G(t,[],!1).filter(r=>S(r)&&K(r)!=="body"),o=null,s=P(t).position==="fixed",l=s?N(t):t;for(;S(l)&&!j(l);){let r=P(l),c=lt(l),a=o?o.position:s?"fixed":"";!c&&(a==="fixed"||a==="absolute"&&r.position==="static")?i=i.filter(d=>d!==l):o=r,l=N(l)}return e.set(t,i),i}function Fe(t){let{element:e,boundary:n,rootBoundary:i,strategy:o}=t,l=[...n==="clippingAncestors"?it(e)?[]:De(e,this._c):[].concat(n),i],r=Yt(e,l[0],o),c=r.top,a=r.right,f=r.bottom,d=r.left;for(let m=1;m{r(!1,1e-7)},1e3)}C=!1}try{i=new IntersectionObserver(O,{...b,root:s.ownerDocument})}catch{i=new IntersectionObserver(O,b)}i.observe(t)}let c=E(t),a=()=>r(n);return c.addEventListener("resize",a),r(!0),()=>{c.removeEventListener("resize",a),l()}}function Jt(t,e,n,i){i===void 0&&(i={});let{ancestorScroll:o=!0,ancestorResize:s=!0,elementResize:l=typeof ResizeObserver=="function",layoutShift:r=typeof IntersectionObserver=="function",animationFrame:c=!1}=i,a=wt(t),f=o||s?[...a?G(a):[],...e?G(e):[]]:[];f.forEach(w=>{o&&w.addEventListener("scroll",n),s&&w.addEventListener("resize",n)});let d=a&&r?We(a,n,s):null,m=-1,u=null;l&&(u=new ResizeObserver(w=>{let[v]=w;v&&v.target===a&&u&&e&&(u.unobserve(e),cancelAnimationFrame(m),m=requestAnimationFrame(()=>{var y;(y=u)==null||y.observe(e)})),n()}),a&&!c&&u.observe(a),e&&u.observe(e));let h,p=c?Y(t):null;c&&g();function g(){let w=Y(t);p&&!Zt(p,w)&&n(),p=w,h=requestAnimationFrame(g)}return n(),()=>{var w;f.forEach(v=>{o&&v.removeEventListener("scroll",n),s&&v.removeEventListener("resize",n)}),d?.(),(w=u)==null||w.disconnect(),u=null,c&&cancelAnimationFrame(h)}}var Qt=kt;var te=Nt,ee=Vt,ne=Bt;var ie=(t,e,n)=>{let i=new Map,o=n??{},s={...Ie,...o.platform,_c:i};return Ft(t,e,{...o,platform:s})};var He=t=>{let e=t?.parentElement;for(;e;){let n=getComputedStyle(e);if(/(auto|scroll|overlay)/.test(n.overflowY)&&e.scrollHeight>e.clientHeight)return e;e=e.parentElement}return null},oe=t=>{let e=null,n=0,i=null,o=()=>i?i.getBoundingClientRect():{top:0,bottom:window.innerHeight},s=r=>i?i.scrollTop+=r:window.scrollBy(0,r),l=()=>{let{top:r,bottom:c}=o(),a=r+60-n,f=n-(c-60);a>0?s(-Math.min(22,a/60*22)):f>0&&s(Math.min(22,f/60*22)),e=requestAnimationFrame(l)};return{start(){i=He(t),e===null&&(e=requestAnimationFrame(l))},update(r){n=r},stop(){e!==null&&cancelAnimationFrame(e),e=null}}};var se=t=>{let e=t?.tBodies?.[0];return e?Array.from(e.children).filter(n=>n.matches("tr[data-row-key]")):[]},re=(t,e)=>{if(t.length===0)return null;let n=0;for(let i=0;i=o.top&&e<=o.bottom)return i;e>o.bottom&&(n=i)}return e{let e=[];try{e=JSON.parse(t.dataset.fillColumns||"[]")}catch{e=[]}let n=t.querySelector("table"),i={columns:e,rows:()=>se(n),columnAt:o=>e[o]??null,colOf:o=>{let s=e.indexOf(o);return s===-1?null:s},cellAt(o,s){let l=e[s];return l==null?null:this.rows()[o]?.querySelector(`:scope > td[data-column="${CSS.escape(l)}"]`)??null},rootIn(o){return o?.querySelector("[data-record-key][data-column-name]")??null},describe(o,s){let l=this.cellAt(o,s),r=this.rootIn(l);return!l||!r?null:{row:o,col:s,cell:l,el:r,recordKey:r.dataset.recordKey,version:r.dataset.recordVersion||null,serialized:r.dataset.serverValue??""}},locate(o){let s=o?.closest?.("td[data-column]"),l=s?.closest("tr[data-row-key]");if(!s||!l)return null;let r=i.colOf(s.dataset.column),c=i.rows().indexOf(l);return r===null||c===-1?null:{row:c,col:r}},rowAtY(o){return re(this.rows(),o)}};return i};var vt=(t,e=t)=>({anchor:t,focus:e}),ce=t=>({anchor:t.anchor,focus:{row:t.focus.row,col:t.anchor.col}}),yt=t=>({top:Math.min(t.anchor.row,t.focus.row),bottom:Math.max(t.anchor.row,t.focus.row),left:Math.min(t.anchor.col,t.focus.col),right:Math.max(t.anchor.col,t.focus.col)}),xt=t=>t.anchor.row===t.focus.row&&t.anchor.col===t.focus.col,bt=t=>{let{top:e,bottom:n,left:i,right:o}=yt(t),s=[];for(let l=e;l<=n;l++)for(let r=i;r<=o;r++)l===t.anchor.row&&r===t.anchor.col||s.push({row:l,col:r});return s};var ae="wire-fill-target",fe=18,ue=12,At=new Set,de=!1,ze=()=>{de||!window.Livewire||(de=!0,window.Livewire.hook("morph.updating",({skip:t})=>{At.size>0&&t()}))},Ke=()=>({grid:null,handle:null,overlay:null,scroller:null,max:1/0,active:null,range:null,dragging:!1,painted:[],_pending:null,init(){this.grid=le(this.$el),this.handle=this.$el.querySelector("[data-fill-handle]"),this.overlay=this.$el.querySelector("[data-fill-overlay]"),this.scroller=oe(this.$el);let t=parseInt(this.$el.dataset.fillMax||"",10);this.max=Number.isFinite(t)&&t>0?t:1/0,ze(),this._onFocusIn=e=>this.onFocusIn(e),this._onPointerOver=e=>this.onHover(e),this._onPointerLeave=()=>this.onLeave(),this._onPointerDown=e=>this.startDrag(e),this._reposition=()=>{this.active&&!this.dragging&&this.place()},this.$el.addEventListener("focusin",this._onFocusIn),this.$el.addEventListener("pointerover",this._onPointerOver),this.$el.addEventListener("pointerleave",this._onPointerLeave),this.handle?.addEventListener("pointerdown",this._onPointerDown),window.addEventListener("resize",this._reposition),window.addEventListener("scroll",this._reposition,!0)},destroy(){this.stopDrag(),this.$el.removeEventListener("focusin",this._onFocusIn),this.$el.removeEventListener("pointerover",this._onPointerOver),this.$el.removeEventListener("pointerleave",this._onPointerLeave),this.handle?.removeEventListener("pointerdown",this._onPointerDown),window.removeEventListener("resize",this._reposition),window.removeEventListener("scroll",this._reposition,!0)},onFocusIn(t){if(this.dragging||this.handle?.contains(t.target))return;let e=this.grid.locate(t.target),n=e&&this.grid.describe(e.row,e.col);if(!n||this.isLocked(n)){this.deactivate();return}this.active=e,this.place()},onHover(t){if(this.dragging||this.handle?.contains(t.target)||this.withinGrabRadius(t)||this.focusedPoint())return;let e=this.grid.locate(t.target),n=e&&this.grid.describe(e.row,e.col);!n||this.isLocked(n)||(this.active=e,this.place())},withinGrabRadius(t){if(!this.handle||this.handle.hidden)return!1;let e=this.handle.getBoundingClientRect();return Math.abs(t.clientX-(e.left+e.width/2))<=fe&&Math.abs(t.clientY-(e.top+e.height/2))<=fe},onLeave(){this.dragging||this.focusedPoint()||this.deactivate()},focusedPoint(){let t=document.activeElement;return!t||!this.$el.contains(t)?null:this.grid.locate(t)},isLocked(t){return!!t.el.querySelector("input, select, textarea, button")?.disabled},deactivate(){this.active=null,this.handle&&(this.handle.hidden=!0)},place(){let t=this.active&&this.grid.describe(this.active.row,this.active.col);if(!t||!this.handle){this.deactivate();return}let e=this.$el.getBoundingClientRect(),n=t.cell.getBoundingClientRect();this.handle.style.left=`${n.right-e.left+this.$el.scrollLeft-ue}px`,this.handle.style.top=`${n.bottom-e.top+this.$el.scrollTop-ue}px`,this.handle.hidden=!1},startDrag(t){if(this.active){t.preventDefault();try{this.handle.setPointerCapture?.(t.pointerId)}catch{}this.dragging=!0,this.range=vt(this.active),At.add(this),document.body.classList.add("wire-filling"),this.scroller.start(),this._onMove=e=>this.onMove(e),this._onUp=e=>this.finish(e),this._onCancel=()=>this.cancel(),this._onKey=e=>{e.key==="Escape"&&this.cancel()},window.addEventListener("pointermove",this._onMove),window.addEventListener("pointerup",this._onUp),window.addEventListener("pointercancel",this._onCancel),window.addEventListener("keydown",this._onKey)}},onMove(t){if(!this.dragging)return;this.scroller.update(t.clientY);let e=this.grid.rowAtY(t.clientY);if(e===null)return;let n=this.range.anchor.row,i=e>=n?Math.min(e,n+this.max):Math.max(e,n-this.max);this.range=ce(vt(this.range.anchor,{row:i,col:this.range.anchor.col})),this.paint()},paint(){this.clearPaint();for(let t of bt(this.range)){let e=this.grid.describe(t.row,t.col);!e||this.isLocked(e)||(e.cell.classList.add(ae),this.painted.push(e.cell))}this.placeOverlay()},clearPaint(){for(let t of this.painted)t.classList.remove(ae);this.painted=[]},placeOverlay(){if(!this.overlay)return;let t=yt(this.range),e=this.grid.cellAt(t.top,t.left),n=this.grid.cellAt(t.bottom,t.right);if(!e||!n)return;let i=this.$el.getBoundingClientRect(),o=e.getBoundingClientRect(),s=n.getBoundingClientRect();this.overlay.style.left=`${o.left-i.left+this.$el.scrollLeft}px`,this.overlay.style.top=`${o.top-i.top+this.$el.scrollTop}px`,this.overlay.style.width=`${s.right-o.left}px`,this.overlay.style.height=`${s.bottom-o.top}px`,this.overlay.hidden=xt(this.range)},stopDrag(){this.dragging&&(window.removeEventListener("pointermove",this._onMove),window.removeEventListener("pointerup",this._onUp),window.removeEventListener("pointercancel",this._onCancel),window.removeEventListener("keydown",this._onKey)),this.dragging=!1,At.delete(this),document.body.classList.remove("wire-filling"),this.scroller?.stop(),this.clearPaint(),this.overlay&&(this.overlay.hidden=!0)},cancel(){this.stopDrag(),this.range=null},finish(){let t=this.range,e=this.active&&this.grid.describe(this.active.row,this.active.col);if(this.stopDrag(),this.range=null,!t||xt(t)||!e)return;let n=bt(t).map(i=>this.grid.describe(i.row,i.col)).filter(i=>i&&!this.isLocked(i));n.length!==0&&this.write(this.grid.columnAt(e.col),e,n)},write(t,e,n){let i=this.liveValue(e),o=new Map;for(let s of n)o.set(s.recordKey,{value:this.liveValue(s),version:s.version}),this.applyValue(s.el,i);return this._pending=(this._pending??Promise.resolve()).catch(()=>{}).then(()=>this.send(t,i,n,o)),this._pending},async send(t,e,n,i){let o={};for(let r of n)o[r.recordKey]=r.el.dataset.recordVersion||null;let s=null;try{s=await this.$wire.fillTableCells([{column:t,value:e,records:o}])}catch{s=null}let l=s?.results?.[t]??null;for(let r of n){let c=l?.[r.recordKey];if(c?.success){this.applyVersion(r.el,c.version),this.announce(r,t,c.version);continue}let a=i.get(r.recordKey);if(c?.conflict){let f=this.stateOf(r.el);this.applyValue(r.el,f?f.parse(c.currentValue):c.currentValue),this.applyVersion(r.el,c.currentVersion)}else this.applyValue(r.el,a.value),this.applyVersion(r.el,a.version)}},stateOf(t){try{return window.Alpine.$data(t)}catch{return null}},liveValue(t){let e=this.stateOf(t.el);return e?e.value:t.serialized},applyValue(t,e){t.dataset.serverValue=this.serialize(e);let n=this.stateOf(t);n&&(n.value=e,n.serverValue=e,n.error=null)},serialize(t){return typeof t=="boolean"?t?"1":"0":t==null?"":String(t)},applyVersion(t,e){if(!e)return;t.dataset.recordVersion=e;let n=this.stateOf(t);n&&(n.recordVersion=e)},announce(t,e,n){n&&window.dispatchEvent(new CustomEvent("wire-editable-committed",{detail:{componentId:this.$el.closest("[wire\\:id]")?.getAttribute("wire:id")??null,recordKey:t.recordKey,column:e,version:n}}))}}),he=Ke;var Ye=1,Xe=t=>{let e=null;for(let n=t.parentElement;n&&n!==document.body;n=n.parentElement){let i=parseInt(getComputedStyle(n).zIndex,10);Number.isNaN(i)||(e=i)}return e===null?null:e+Ye},me=(t,e,n={})=>{if(!t||!e)return()=>{};let i=n.placement||"bottom-end",o=n.offset??6,s=n.matchWidth??!1,l=n.sheetBreakpoint??640,r=n.sheetOnMobile?window.matchMedia(`(max-width: ${l-.02}px)`):null,c=parseFloat(getComputedStyle(e).maxHeight),a=Number.isNaN(c)?1/0:c,f=parseInt(getComputedStyle(e).zIndex,10),d=Number.isNaN(f)?-1/0:f,m=[Qt(o),ee(),te({padding:8}),ne({padding:8,apply({availableHeight:A,rects:b,elements:C}){Object.assign(C.floating.style,{maxHeight:`${Math.round(Math.min(A,a))}px`,overflowY:"auto"}),s&&(C.floating.style.minWidth=`${b.reference.width}px`)}})],u=null,h=()=>{!t.isConnected||!e.isConnected||ie(t,e,{placement:i,middleware:m}).then(({x:A,y:b})=>{Object.assign(e.style,{left:`${A}px`,top:`${b}px`}),u!==null&&(e.style.zIndex=`${u}`)})},p=null,g=null,w=()=>!!r&&r.matches,v=()=>{if(w()){Object.assign(e.style,{position:"",top:"",left:"",maxHeight:"",overflowY:"",minWidth:"",zIndex:""}),u=null;return}let A=Xe(t);u=A!==null&&A>d?A:null,Object.assign(e.style,{position:"absolute",top:"0",left:"0"}),u!==null&&(e.style.zIndex=`${u}`),p=Jt(t,e,h),g=new MutationObserver(b=>{b.every(O=>O.target===e&&O.type==="attributes"&&O.attributeName==="style")||h()}),g.observe(e,{childList:!0,subtree:!0,attributes:!0})},y=()=>{p&&(p(),p=null),g&&(g.disconnect(),g=null)},x=()=>{y(),v()};return r?.addEventListener("change",x),v(),()=>{y(),r?.removeEventListener("change",x)}},qe=(t,e)=>{if(!t||!e)return!1;let n=e;for(;n;){if(n===t)return!0;n=n._x_teleportBack??n.parentElement}return!1},Ge=(t={},e=null)=>({open:!1,_cleanup:null,items:e,_wire:null,init(){this._wireId=(this.$root??this.$el)?.closest("[wire\\:id]")?.getAttribute("wire:id")??null},runAction(n){if(!n||!n.method||!this._wireId)return;let i=window.Livewire?.find(this._wireId);i&&typeof i.call=="function"&&i.call(n.method,...n.args||[])},toggle(){this.open?this.close():this.show()},show(){this.open=!0,this.$nextTick(()=>{this._cleanup=me(this.$refs.trigger,this.$refs.panel,t)})},close(){this.open=!1,this.stop()},stop(){this._cleanup&&(this._cleanup(),this._cleanup=null)},destroy(){this.stop()}}),je=t=>{t.directive("sheet-dismiss",(e,{expression:n},{evaluateLater:i,cleanup:o})=>{let s=i(n),l=()=>e.parentElement,r=()=>window.matchMedia(`(max-width: ${parseFloat(e.dataset.sheetBp)||639.98}px)`).matches,c=0,a=0,f=!1,d=h=>{if(!r())return;f=!0,a=0,c=h.touches[0].clientY;let p=l();p&&(p.style.transition="none")},m=h=>{if(!f)return;a=Math.max(0,h.touches[0].clientY-c);let p=l();p&&(p.style.transform=`translateY(${a}px)`)},u=()=>{if(!f)return;f=!1;let h=l();h&&(h.style.transition="",h.style.transform=""),a>90&&s(()=>{})};e.addEventListener("touchstart",d,{passive:!0}),e.addEventListener("touchmove",m,{passive:!0}),e.addEventListener("touchend",u),o(()=>{e.removeEventListener("touchstart",d),e.removeEventListener("touchmove",m),e.removeEventListener("touchend",u)})})},Ue='a[href],button:not([disabled]),input:not([disabled]),select:not([disabled]),textarea:not([disabled]),[tabindex]:not([tabindex="-1"])',Ze=t=>{t.directive("focus-trap",(e,{expression:n},{evaluateLater:i,effect:o,cleanup:s})=>{let l=i(n),r=()=>window.matchMedia(`(max-width: ${parseFloat(e.dataset.sheetBp)||639.98}px)`).matches,c=()=>[...e.querySelectorAll(Ue)].filter(h=>h.offsetParent!==null),a=null,f=null,d=!1,m=()=>{if(d||!r())return;d=!0,a=document.activeElement,e.setAttribute("aria-modal","true"),(c()[0]??e).focus({preventScroll:!0}),f=p=>{if(p.key!=="Tab")return;let g=c();if(g.length===0){p.preventDefault();return}let w=g[0],v=g[g.length-1];p.shiftKey&&document.activeElement===w?(p.preventDefault(),v.focus()):!p.shiftKey&&document.activeElement===v&&(p.preventDefault(),w.focus())},e.addEventListener("keydown",f)},u=()=>{if(!d)return;d=!1,e.removeAttribute("aria-modal"),f&&(e.removeEventListener("keydown",f),f=null);let h=a;a=null,h&&typeof h.focus=="function"&&requestAnimationFrame(()=>h.focus({preventScroll:!0}))};o(()=>{l(h=>{h?requestAnimationFrame(m):u()})}),s(u)})},Je=(t=0)=>({tabs:[],active:t,registerTab(e){return this.tabs.push(e),this.tabs.length-1}}),Qe=(t=0)=>({steps:[],current:t,registerStep(e){return this.steps.push(e),this.steps.length-1},get isFirst(){return this.current===0},get isLast(){return this.current>=this.steps.length-1},next(){this.isLast||this.current++},prev(){this.isFirst||this.current--}}),tn=(t={})=>({value:t.value,serverValue:t.value,recordVersion:t.recordVersion??"0",commitMethod:t.commitMethod??"updateTableCell",validateMethod:t.validateMethod??"validateTableCell",recordKey:null,columnName:null,componentId:null,saving:!1,error:null,success:!1,focused:!1,get dirty(){return this.value!==this.serverValue},parse(e){return t.parse?t.parse(e):e},messages:{},init(){this.recordKey=this.$el.dataset.recordKey,this.columnName=this.$el.dataset.columnName,this.messages={error:this.messages.error,saveFailed:this.messages.saveFailed,invalid:this.messages.invalid},t.liveValidation&&this.$watch("value",window.Alpine.debounce(()=>{this.dirty&&this.validate()},t.debounce??500));let e=new MutationObserver(n=>{for(let i of n)if(i.attributeName==="data-server-value"||i.attributeName==="data-record-version"){let o=this.parse(this.$el.dataset.serverValue);o!==this.serverValue&&this.syncFromServer(o,this.$el.dataset.recordVersion)}});e.observe(this.$el,{attributes:!0,attributeFilter:["data-server-value","data-record-version"]}),this._observer=e,this.componentId=this.$el.closest("[wire\\:id]")?.getAttribute("wire:id")??null,this._onSiblingCommit=n=>{let i=n.detail||{};i.componentId===this.componentId&&String(i.recordKey)===String(this.recordKey)&&i.column!==this.columnName&&(this.saving||this.focused&&this.dirty||i.version&&(this.recordVersion=i.version))},window.addEventListener("wire-editable-committed",this._onSiblingCommit)},destroy(){this._observer?.disconnect(),window.removeEventListener("wire-editable-committed",this._onSiblingCommit)},syncFromServer(e,n){this.saving||this.focused&&this.dirty||(this.value=e,this.serverValue=e,n&&(this.recordVersion=n),this.error=null)},onFocus(){this.focused=!0},onBlur(){this.focused=!1,t.saveOnBlur&&this.dirty&&this.save()},onEnter(){t.saveOnEnter&&this.dirty&&this.save()},onEscape(){this.value=this.serverValue,this.error=null,this.$refs.input?.blur()},save(){this.dirty&&this.commit(this.value)},async commit(e){if(!this.saving){this.value=e,this.saving=!0,this.error=null;try{let n=await this.$wire[this.commitMethod](this.recordKey,this.columnName,e,this.recordVersion);n?.success===!1?(this.value=this.serverValue,this.error=n.message||n.errors?.[0]||this.messages.error,n?.conflict&&(this.value=this.parse(n.currentValue),this.serverValue=this.value,this.recordVersion=n.currentVersion??this.recordVersion)):(this.serverValue=e,n?.version&&(this.recordVersion=n.version),this.success=!0,setTimeout(()=>{this.success=!1},1500),n?.version&&window.dispatchEvent(new CustomEvent("wire-editable-committed",{detail:{componentId:this.componentId,recordKey:this.recordKey,column:this.columnName,version:n.version}})))}catch{this.value=this.serverValue,this.error=this.messages.saveFailed}finally{this.saving=!1}}},async validate(){try{let e=await this.$wire[this.validateMethod](this.recordKey,this.columnName,this.value);this.error=e&&!e.valid?e.errors?.[0]||this.messages.invalid:null}catch{}}}),Z=null,en=()=>({open:!1,x:0,y:0,openAt(t){Z&&Z!==this&&Z.close(),Z=this,this.x=t.clientX,this.y=t.clientY,this.open=!0,this.$nextTick(()=>this.place())},place(){let t=this.$refs.panel;if(!t)return;let e=8,{width:n,height:i}=t.getBoundingClientRect(),o=this.x,s=this.y;o+n+e>window.innerWidth&&(o=window.innerWidth-n-e),s+i+e>window.innerHeight&&(s=window.innerHeight-i-e),t.style.left=`${Math.max(e,o)}px`,t.style.top=`${Math.max(e,s)}px`},close(){this.open=!1,Z===this&&(Z=null)}});document.addEventListener("alpine:init",()=>{window.Alpine.magic("float",()=>me),window.Alpine.magic("clickedInside",t=>e=>qe(t,e?.target)),window.Alpine.data("wireDropdown",Ge),window.Alpine.data("wireContextMenu",en),window.Alpine.data("wireTabs",Je),window.Alpine.data("wireWizard",Qe),window.Alpine.data("wireEditableCell",tn),window.Alpine.data("wireFillHandle",he),je(window.Alpine),Ze(window.Alpine)});})();
diff --git a/packages/core/resources/js/fill/controller.js b/packages/core/resources/js/fill/controller.js
index b94e1525..c2d6a646 100644
--- a/packages/core/resources/js/fill/controller.js
+++ b/packages/core/resources/js/fill/controller.js
@@ -1,4 +1,4 @@
-import { createAutoScroller } from './autoscroll'
+import { createAutoScroller } from '../support/autoscroll'
import { createGrid } from './grid'
import { bounds, clampToColumn, isEmpty, makeRange, targets } from './range'
diff --git a/packages/core/resources/js/fill/grid.js b/packages/core/resources/js/fill/grid.js
index 28fc2c62..14a69a25 100644
--- a/packages/core/resources/js/fill/grid.js
+++ b/packages/core/resources/js/fill/grid.js
@@ -12,15 +12,7 @@
* (later) horizontally.
*/
-/** Direct-child rows only: group headers and sub-row rows carry no data-row-key,
- * and a sub-row's own has its own tbody, so it is never reached here. */
-const bodyRows = (table) => {
- const tbody = table?.tBodies?.[0]
-
- if (! tbody) return []
-
- return Array.from(tbody.children).filter((el) => el.matches('tr[data-row-key]'))
-}
+import { bodyRows, rowAtY } from '../support/rows'
export const createGrid = (root) => {
let columns = []
@@ -98,20 +90,7 @@ export const createGrid = (root) => {
/** The row nearest a viewport y — used while dragging past the last row. */
rowAtY(clientY) {
- const rows = this.rows()
-
- if (rows.length === 0) return null
-
- let nearest = 0
-
- for (let i = 0; i < rows.length; i++) {
- const rect = rows[i].getBoundingClientRect()
-
- if (clientY >= rect.top && clientY <= rect.bottom) return i
- if (clientY > rect.bottom) nearest = i
- }
-
- return clientY < rows[0].getBoundingClientRect().top ? 0 : nearest
+ return rowAtY(this.rows(), clientY)
},
}
diff --git a/packages/core/resources/js/fill/autoscroll.js b/packages/core/resources/js/support/autoscroll.js
similarity index 100%
rename from packages/core/resources/js/fill/autoscroll.js
rename to packages/core/resources/js/support/autoscroll.js
diff --git a/packages/core/resources/js/support/rows.js b/packages/core/resources/js/support/rows.js
new file mode 100644
index 00000000..8394e8bc
--- /dev/null
+++ b/packages/core/resources/js/support/rows.js
@@ -0,0 +1,33 @@
+/**
+ * Row geometry helpers shared by every drag-over-rows gesture (the fill
+ * handle, the table selection sweep). Promoted out of fill/grid.js — the
+ * grid keeps its richer cell/column view and delegates the row arithmetic
+ * here.
+ */
+
+/** Direct-child rows only: group headers and sub-row rows carry no data-row-key,
+ * and a sub-row's own has its own tbody, so it is never reached here. */
+export const bodyRows = (table) => {
+ const tbody = table?.tBodies?.[0]
+
+ if (! tbody) return []
+
+ return Array.from(tbody.children).filter((el) => el.matches('tr[data-row-key]'))
+}
+
+/** The index of the row nearest a viewport y — used while dragging past the
+ * first or last row, so the gesture keeps tracking instead of going dead. */
+export const rowAtY = (rows, clientY) => {
+ if (rows.length === 0) return null
+
+ let nearest = 0
+
+ for (let i = 0; i < rows.length; i++) {
+ const rect = rows[i].getBoundingClientRect()
+
+ if (clientY >= rect.top && clientY <= rect.bottom) return i
+ if (clientY > rect.bottom) nearest = i
+ }
+
+ return clientY < rows[0].getBoundingClientRect().top ? 0 : nearest
+}
diff --git a/packages/core/resources/views/modals/confirmation.blade.php b/packages/core/resources/views/modals/confirmation.blade.php
index 36c5cf75..5a131d3c 100644
--- a/packages/core/resources/views/modals/confirmation.blade.php
+++ b/packages/core/resources/views/modals/confirmation.blade.php
@@ -9,10 +9,29 @@
// isset()/?? keep $attributes untouched when it is absent (object path).
$modelBinding = $wireModel ?? (isset($attributes) ? $attributes->wire('model') : null);
$confirmClick = ($wireClick ?? (isset($attributes) ? $attributes->wire('click')->value() : null)) ?: null;
+ // The consumer-path wire() macro returns a WireDirective even when the
+ // attribute is absent, so "has a binding" must go through value().
+ $hasModelBinding = $modelBinding instanceof \Livewire\WireDirective
+ ? ! in_array($modelBinding->value(), [null, false, ''], true)
+ : filled($modelBinding);
+ // Without a wire:model binding, `show` is plain Alpine state and an
+ // optional $openOn window event opens the dialog from JS.
+ $openEvent = ($openOn ?? null) ?: null;
+ // Attribute-name position: only a safe token may reach the x-on: binding
+ // (a space would inject a new attribute — Blade only escapes quotes).
+ if ($openEvent !== null && ! preg_match('/^[a-zA-Z][a-zA-Z0-9_-]*$/', $openEvent)) {
+ $openEvent = null;
+ }
@endphp
-
+
wire('model') : null);
+ // The consumer-path wire() macro returns a WireDirective even when the
+ // attribute is absent, so "has a binding" must go through value().
+ $hasModelBinding = $modelBinding instanceof \Livewire\WireDirective
+ ? ! in_array($modelBinding->value(), [null, false, ''], true)
+ : filled($modelBinding);
+ // Without a wire:model binding, `show` is plain Alpine state and an
+ // optional $openOn window event opens the modal from JS.
+ $openEvent = ($openOn ?? null) ?: null;
+ // Attribute-name position: only a safe token may reach the x-on: binding
+ // (a space would inject a new attribute — Blade only escapes quotes).
+ if ($openEvent !== null && ! preg_match('/^[a-zA-Z][a-zA-Z0-9_-]*$/', $openEvent)) {
+ $openEvent = null;
+ }
@endphp
-
+
'cropping'])
+
+ Without this the focus simply stays wherever it was when the modal opened.
+ On a grid table that means it stays on the row behind the dialog: Tab then
+ walks the page *behind* the modal instead of its buttons, the dialog cannot
+ be operated from the keyboard at all, and when it closes the focus is left
+ on whatever the tabbing landed on — so the grid's arrow keys, which only
+ answer when a row itself has the focus, are dead until the user clicks a
+ row again.
+
+ Three parts: take the focus on open (remembering where it came from), keep
+ Tab inside while open, and hand it back on close.
+
+ Deliberately expression-only, with no dependency on a JS bundle: a modal
+ must not need an asset that a page rendering only modals would not load.
+ Single quotes throughout — a double quote inside an Alpine attribute
+ truncates it, which is how this file would silently stop working. --}}
+x-effect="
+ if ({{ $openExpression ?? 'show' }}) {
+ if (! $el._wireFocusFrom) $el._wireFocusFrom = document.activeElement;
+ {{-- Pull the focus in whenever it is outside, not only on the first
+ open: a surface that closes and reopens without the effect running
+ for the closed state in between (confirm-then-reopen) would
+ otherwise keep the stale marker and never take the focus again. --}}
+ {{-- Twice: on the tick after the state changed, and again a frame later.
+ x-show may not have made the surface visible yet on the first pass,
+ and focus() on a display:none element silently does nothing. --}}
+ $nextTick(() => { $el._wireFocusIn(); requestAnimationFrame(() => $el._wireFocusIn()); });
+ } else if ($el._wireFocusFrom) {
+ const back = $el._wireFocusFrom;
+ $el._wireFocusFrom = null;
+ {{-- Only if it is still in the document and still focusable: a Livewire
+ re-render may have replaced the row the modal was opened from. --}}
+ if (back && back.isConnected && back !== document.body) {
+ back.focus({ preventScroll: true });
+ }
+ }
+"
+x-on:keydown.tab="
+ const items = $el._wireFocusable();
+ if (! items.length) return;
+
+ const first = items[0];
+ const last = items[items.length - 1];
+ const active = document.activeElement;
+
+ {{-- Focus outside the dialog (the row behind it, say) is pulled back in. --}}
+ if (! $el.contains(active)) {
+ $event.preventDefault();
+ ($event.shiftKey ? last : first).focus();
+ } else if ($event.shiftKey && active === first) {
+ $event.preventDefault();
+ last.focus();
+ } else if (! $event.shiftKey && active === last) {
+ $event.preventDefault();
+ first.focus();
+ }
+"
+x-init="
+ {{-- tabIndex >= 0 rather than a :not([tabindex='-1']) selector, which would
+ need quotes this attribute cannot carry. --}}
+ $el._wireFocusable = () => [...$el.querySelectorAll('a[href], button, input, select, textarea, [tabindex]')]
+ .filter((el) => ! el.disabled && el.tabIndex >= 0 && el.getClientRects().length > 0);
+
+ $el._wireFocusIn = () => {
+ if ($el.contains(document.activeElement)) return;
+ const target = $el.querySelector('[autofocus]') || $el._wireFocusable()[0] || $el;
+ target.focus({ preventScroll: true });
+ }
+"
+tabindex="-1"
diff --git a/packages/core/resources/views/modals/slide-over.blade.php b/packages/core/resources/views/modals/slide-over.blade.php
index a327684b..96cecf6c 100644
--- a/packages/core/resources/views/modals/slide-over.blade.php
+++ b/packages/core/resources/views/modals/slide-over.blade.php
@@ -7,10 +7,29 @@
// (consumer path) or the Htmlable SlideOver object, which passes $wireModel
// (Rule 5). isset()/?? keep $attributes untouched off the component path.
$modelBinding = $wireModel ?? (isset($attributes) ? $attributes->wire('model') : null);
+ // The consumer-path wire() macro returns a WireDirective even when the
+ // attribute is absent, so "has a binding" must go through value().
+ $hasModelBinding = $modelBinding instanceof \Livewire\WireDirective
+ ? ! in_array($modelBinding->value(), [null, false, ''], true)
+ : filled($modelBinding);
+ // Without a wire:model binding, `show` is plain Alpine state and an
+ // optional $openOn window event opens the panel from JS.
+ $openEvent = ($openOn ?? null) ?: null;
+ // Attribute-name position: only a safe token may reach the x-on: binding
+ // (a space would inject a new attribute — Blade only escapes quotes).
+ if ($openEvent !== null && ! preg_match('/^[a-zA-Z][a-zA-Z0-9_-]*$/', $openEvent)) {
+ $openEvent = null;
+ }
@endphp
-
+
|null */
protected ?array $formValidation = null;
@@ -295,14 +296,14 @@ public function noSubmit(bool $noSubmit = true): static
}
/**
- * @param array
|Form $fields
+ * @param array|ModalForm $fields
*/
- public function form(array|Form $fields): static
+ public function form(array|ModalForm $fields): static
{
- if ($fields instanceof Form) {
+ if ($fields instanceof ModalForm) {
$this->formInstance = $fields;
} else {
- $this->formInstance = Form::make()->schema($fields);
+ $this->formInstance = ModalForms::make($fields);
}
return $this;
@@ -421,7 +422,7 @@ public function hasForm(): bool
return ! $this->isInformative && $this->formInstance !== null;
}
- public function getFormInstance(): ?Form
+ public function getFormInstance(): ?ModalForm
{
return $this->formInstance;
}
diff --git a/packages/core/src/Actions/Concerns/HasKeyboardShortcut.php b/packages/core/src/Actions/Concerns/HasKeyboardShortcut.php
index 3fc1f10f..fb050e4d 100644
--- a/packages/core/src/Actions/Concerns/HasKeyboardShortcut.php
+++ b/packages/core/src/Actions/Concerns/HasKeyboardShortcut.php
@@ -4,6 +4,8 @@
namespace NyonCode\WireCore\Actions\Concerns;
+use NyonCode\WireCore\Foundation\Support\ShortcutLabelFormatter;
+
/**
* Trait HasKeyboardShortcut
*
@@ -43,6 +45,23 @@ public function keyboardShortcut(string $shortcut, ?string $label = null): stati
return $this;
}
+ /**
+ * Drop the shortcut for this copy of the action.
+ *
+ * A rendered button binds its shortcut as a *window* listener, so an action
+ * rendered on more than one surface answers the same key once per surface —
+ * and a surface that is merely present but not shown (the stacked mobile
+ * cards on a desktop, say) would answer it invisibly, once per record.
+ * A surface that must not own the key clones the action and calls this.
+ */
+ public function withoutKeyboardShortcut(): static
+ {
+ $this->keyboardShortcut = null;
+ $this->keyboardShortcutLabel = null;
+
+ return $this;
+ }
+
public function getKeyboardShortcut(): ?string
{
return $this->keyboardShortcut;
@@ -129,29 +148,11 @@ public function shortcutUsesMod(): bool
/**
* Format shortcut for display (e.g. 'mod+s' → 'Ctrl+S' or '⌘S').
+ * Delegates to the canonical {@see ShortcutLabelFormatter}, so an action
+ * label and a shortcut legend always read the same.
*/
protected function formatShortcutLabel(string $shortcut): string
{
- $parts = array_map('trim', explode('+', $shortcut));
- $formatted = [];
-
- foreach ($parts as $part) {
- $formatted[] = match (strtolower($part)) {
- 'mod' => 'Ctrl',
- 'ctrl', 'control' => 'Ctrl',
- 'shift' => 'Shift',
- 'alt', 'option' => 'Alt',
- 'meta', 'cmd', 'command' => '⌘',
- 'enter', 'return' => '↵',
- 'escape', 'esc' => 'Esc',
- 'delete' => 'Del',
- 'backspace' => '⌫',
- 'space' => 'Space',
- 'tab' => 'Tab',
- default => strtoupper($part),
- };
- }
-
- return implode('+', $formatted);
+ return ShortcutLabelFormatter::format($shortcut);
}
}
diff --git a/packages/core/src/Actions/Concerns/HasModal.php b/packages/core/src/Actions/Concerns/HasModal.php
index 75c75921..c2aa4aa3 100644
--- a/packages/core/src/Actions/Concerns/HasModal.php
+++ b/packages/core/src/Actions/Concerns/HasModal.php
@@ -6,7 +6,9 @@
use Closure;
use Livewire\Component;
+use NyonCode\WireCore\Actions\Contracts\ModalForm;
use NyonCode\WireCore\Actions\ModalStep;
+use NyonCode\WireCore\Actions\Support\ModalForms;
use NyonCode\WireCore\Core\State\StateContainer;
use NyonCode\WireCore\Core\Support\Trans;
use NyonCode\WireCore\Foundation\Colors\Color;
@@ -19,7 +21,6 @@
use NyonCode\WireCore\Modals\Modal;
use NyonCode\WireCore\Modals\SlideOver;
use NyonCode\WireCore\Modals\Wizard;
-use NyonCode\WireForms\Forms\Form;
/**
* Trait HasModal
@@ -62,8 +63,8 @@ trait HasModal
protected bool $modalCloseOnEscape = true;
- /** @var Form|Closure|null Form instance or closure returning Form */
- protected Form|Closure|null $formInstance = null;
+ /** @var ModalForm|Closure|null Form instance or closure returning a form */
+ protected ModalForm|Closure|null $formInstance = null;
/** @var Infolist|Closure|null Infolist instance or closure returning Infolist */
protected Infolist|Closure|null $infolistInstance = null;
@@ -275,16 +276,16 @@ public function getMobileBreakpoint(): ?string
* - Closure returning Form: ->form(fn ($record) => Form::make()->schema([...]))
* - Closure returning array of components: ->form(fn ($record) => [TextInput::make('name')])
*
- * @param array|Form|Closure $fields
+ * @param array|ModalForm|Closure $fields
*/
- public function form(array|Form|Closure $fields): static
+ public function form(array|ModalForm|Closure $fields): static
{
- if ($fields instanceof Form) {
+ if ($fields instanceof ModalForm) {
$this->formInstance = $fields;
} elseif ($fields instanceof Closure) {
$this->formInstance = $fields;
} else {
- $this->formInstance = Form::make()->schema($fields);
+ $this->formInstance = ModalForms::make($fields);
}
$this->hasModal = true;
@@ -598,18 +599,18 @@ public function getInfolistInstance(mixed $context = null): ?Infolist
* When a closure was passed to form(), it will be resolved here.
* The Form is automatically configured with statePath and livewire binding.
*/
- public function getFormInstance(?Component $livewire = null, mixed $context = null, ?string $statePath = null): ?Form
+ public function getFormInstance(?Component $livewire = null, mixed $context = null, ?string $statePath = null): ?ModalForm
{
$form = null;
if ($this->formInstance instanceof Closure) {
$resolved = ($this->formInstance)($context);
- if ($resolved instanceof Form) {
+ if ($resolved instanceof ModalForm) {
$form = $resolved;
} elseif (is_array($resolved)) {
- $form = Form::make()->schema($resolved);
+ $form = ModalForms::make($resolved);
}
- } elseif ($this->formInstance instanceof Form) {
+ } elseif ($this->formInstance instanceof ModalForm) {
$form = $this->formInstance;
}
@@ -812,19 +813,19 @@ public function getFormDefaults(mixed $context = null): array
* component and sets no state path, so calling {@see Form::getInitialState()}
* on the result never mutates the instance that later renders.
*/
- protected function resolveFormForSeeding(mixed $context = null): ?Form
+ protected function resolveFormForSeeding(mixed $context = null): ?ModalForm
{
if ($this->formInstance instanceof Closure) {
$resolved = ($this->formInstance)($context);
- if ($resolved instanceof Form) {
+ if ($resolved instanceof ModalForm) {
return $resolved;
}
- return is_array($resolved) ? Form::make()->schema($resolved) : null;
+ return is_array($resolved) ? ModalForms::make($resolved) : null;
}
- if ($this->formInstance instanceof Form) {
+ if ($this->formInstance instanceof ModalForm) {
return $this->formInstance;
}
@@ -1041,7 +1042,7 @@ public function getModalStep(int $index): ?ModalStep
* `modal.action.formData` bag and data persists as the user moves between
* steps. Returns null when this action is not a multi-step wizard.
*/
- public function getStepFormInstance(?Component $livewire = null, mixed $context = null, int $stepIndex = 0, ?string $statePath = null): ?Form
+ public function getStepFormInstance(?Component $livewire = null, mixed $context = null, int $stepIndex = 0, ?string $statePath = null): ?ModalForm
{
$step = $this->getModalStep($stepIndex);
@@ -1049,7 +1050,11 @@ public function getStepFormInstance(?Component $livewire = null, mixed $context
return null;
}
- $form = Form::make()->schema($step->getSchema($context));
+ $form = ModalForms::make($step->getSchema($context));
+
+ if ($form === null) {
+ return null;
+ }
$form->statePath($statePath ?? $this->resolveModalFormStatePath($livewire));
diff --git a/packages/core/src/Actions/Concerns/InteractsWithActions.php b/packages/core/src/Actions/Concerns/InteractsWithActions.php
index 91158529..94598230 100644
--- a/packages/core/src/Actions/Concerns/InteractsWithActions.php
+++ b/packages/core/src/Actions/Concerns/InteractsWithActions.php
@@ -8,6 +8,7 @@
use NyonCode\WireCore\Actions\ActionHalt;
use NyonCode\WireCore\Actions\BaseAction;
use NyonCode\WireCore\Actions\BulkAction;
+use NyonCode\WireCore\Actions\Contracts\ModalForm;
use NyonCode\WireCore\Actions\HeaderAction;
use NyonCode\WireCore\Actions\ModalFooterAction;
use NyonCode\WireCore\Core\Actions\ActionContext;
@@ -967,6 +968,27 @@ public function getActionModalInfolistInstance(): ?Infolist
return $this->actionModalInfolistInstance;
}
+ /**
+ * The resolved form instance for the current action modal, or null.
+ *
+ * Form-agnostic default: wire-core hosts have no form runtime, so the modal
+ * renders without a form body. The wire-forms bridge
+ * (`NyonCode\WireForms\Concerns\InteractsWithActionForms`) overrides
+ * this (resolved via `insteadof` in the composing host) to build the real
+ * {@see ModalForm}. Declared here so the core action modal-host view can call
+ * it on any host — including a standalone wire-core one — without a fatal.
+ */
+ public function getActionModalFormInstance(): ?ModalForm
+ {
+ return null;
+ }
+
+ /** Form-agnostic default for a stacked modal frame; see {@see getActionModalFormInstance()}. */
+ public function getActionModalFormInstanceForDepth(int $depth): ?ModalForm
+ {
+ return null;
+ }
+
// ==========================================
// Infolist actions (entry + section header)
// ==========================================
diff --git a/packages/core/src/Actions/Contracts/HasForm.php b/packages/core/src/Actions/Contracts/HasForm.php
index 70fba79a..18206917 100644
--- a/packages/core/src/Actions/Contracts/HasForm.php
+++ b/packages/core/src/Actions/Contracts/HasForm.php
@@ -5,14 +5,14 @@
namespace NyonCode\WireCore\Actions\Contracts;
use Livewire\Component;
-use NyonCode\WireForms\Forms\Form;
/**
* Contract for actions that support form integration.
*
* Implementation is provided by HasModal trait on BaseAction,
* which offers form(), fillFormUsing(), getFormInstance(),
- * and hasFormModal() methods.
+ * and hasFormModal() methods. Typed against the core {@see ModalForm} seam so
+ * the contract never names wire-forms' concrete Form.
*/
interface HasForm
{
@@ -22,11 +22,11 @@ interface HasForm
public function hasFormInstance(): bool;
/**
- * Resolve the Form instance for this action's modal.
+ * Resolve the form instance for this action's modal.
*
* $statePath is the frame's binding base resolved by the host (per modal
* stack depth); when null the action falls back to the legacy single-slot
* path for host-less callers.
*/
- public function getFormInstance(?Component $livewire = null, mixed $context = null, ?string $statePath = null): ?Form;
+ public function getFormInstance(?Component $livewire = null, mixed $context = null, ?string $statePath = null): ?ModalForm;
}
diff --git a/packages/core/src/Actions/Contracts/ModalForm.php b/packages/core/src/Actions/Contracts/ModalForm.php
new file mode 100644
index 00000000..0ab8cb84
--- /dev/null
+++ b/packages/core/src/Actions/Contracts/ModalForm.php
@@ -0,0 +1,54 @@
+ wire-table -> wire-forms -> wire-core`. This contract is the
+ * canonical seam: core type-hints and calls only these methods, wire-forms'
+ * `Form` implements them, and the concrete instance is produced by a
+ * {@see ModalFormFactory} resolved from the container. Core never names `Form`.
+ *
+ * Deliberately narrow: exactly what core production code and the generic action
+ * modal runtime call — not the full wire-forms `Form` API (no `model()`,
+ * `save()`, wizards, or `getFlatComponents()`). Extends {@see Htmlable} so a
+ * modal body can render it with `{{ $formInstance }}`.
+ */
+interface ModalForm extends Htmlable
+{
+ /** Bind the form's fields to a Livewire state path (per modal-stack depth). */
+ public function statePath(string $path): static;
+
+ /** Bind the form to its host Livewire component. */
+ public function livewire(Component $component): static;
+
+ /**
+ * Seed the form's fields with default values.
+ *
+ * @param array $data
+ */
+ public function fill(array $data): static;
+
+ /**
+ * The form's initial (default) state, used to seed modal form data.
+ *
+ * @return array
+ */
+ public function getInitialState(): array;
+
+ /**
+ * Validate the bound state, returning the validated data.
+ *
+ * @return array
+ */
+ public function validate(): array;
+}
diff --git a/packages/core/src/Actions/Contracts/ModalFormFactory.php b/packages/core/src/Actions/Contracts/ModalFormFactory.php
new file mode 100644
index 00000000..6f5499c6
--- /dev/null
+++ b/packages/core/src/Actions/Contracts/ModalFormFactory.php
@@ -0,0 +1,26 @@
+ $schema Field components for the form.
+ */
+ public function make(array $schema = []): ModalForm;
+}
diff --git a/packages/core/src/Actions/Support/ModalForms.php b/packages/core/src/Actions/Support/ModalForms.php
new file mode 100644
index 00000000..8d6604ef
--- /dev/null
+++ b/packages/core/src/Actions/Support/ModalForms.php
@@ -0,0 +1,37 @@
+bound(ModalFormFactory::class)
+ ? app(ModalFormFactory::class)
+ : null;
+ }
+
+ /**
+ * Build a modal form from a field schema, or `null` when no form runtime is
+ * available. Callers already guard a `null` form (the modal degrades to a
+ * confirmation/heading dialog).
+ *
+ * @param array $schema
+ */
+ public static function make(array $schema = []): ?ModalForm
+ {
+ return self::factory()?->make($schema);
+ }
+}
diff --git a/packages/core/src/Core/Plugin/Contracts/FormConfigContract.php b/packages/core/src/Core/Plugin/Contracts/FormConfigContract.php
new file mode 100644
index 00000000..4a7d8105
--- /dev/null
+++ b/packages/core/src/Core/Plugin/Contracts/FormConfigContract.php
@@ -0,0 +1,33 @@
+ $data The validated form data (modifiable)
*/
public function __construct(
- public readonly FormConfig $config,
+ public readonly FormConfigContract $config,
public array $data,
) {}
diff --git a/packages/core/src/Exceptions/InvalidDateBoundaryException.php b/packages/core/src/Exceptions/InvalidDateBoundaryException.php
new file mode 100644
index 00000000..7d614c27
--- /dev/null
+++ b/packages/core/src/Exceptions/InvalidDateBoundaryException.php
@@ -0,0 +1,36 @@
+` ignores a `min` that is not
+ * `Y-m-d\TH:i` outright, so the bound just disappears;
+ * - the custom picker compares `'2026-07-15' < bound`, where a foreign shape
+ * disables every day (`'2026-07-15' < 'today'`) or none.
+ *
+ * Both failures are silent, which is why an unreadable bound throws here
+ * instead of being passed along.
+ */
+final class DateBoundary
+{
+ /**
+ * Lower bound, formatted for the widget.
+ */
+ public static function min(mixed $value, string $format): ?string
+ {
+ return self::parse($value)?->format($format);
+ }
+
+ /**
+ * Upper bound, formatted for the widget.
+ *
+ * A day-granular bound on a datetime widget means "up to the end of that
+ * day", not "up to its first second": `maxDate('2026-07-20')` has to leave
+ * 20 July selectable at any time, so a bound that lands on midnight is
+ * carried to the end of its day. Only a widget that shows both a date and a
+ * time is affected — on a date-only widget the time is dropped by the
+ * format anyway, and on a time-only one midnight is a deliberate choice.
+ */
+ public static function max(mixed $value, string $format): ?string
+ {
+ $date = self::parse($value);
+
+ if ($date === null) {
+ return null;
+ }
+
+ if (self::carriesDateAndTime($format) && $date->format('H:i:s') === '00:00:00') {
+ $date = $date->endOfDay();
+ }
+
+ return $date->format($format);
+ }
+
+ /**
+ * The date half of a normalized bound, or null when it carries none
+ * (a time-only widget's bound).
+ */
+ public static function datePart(?string $bound): ?string
+ {
+ if ($bound === null || preg_match('/^\d{4}-\d{2}/', $bound) !== 1) {
+ return null;
+ }
+
+ return preg_split('/[T ]/', $bound)[0];
+ }
+
+ /**
+ * The time half of a normalized bound as `HH:MM:SS`, or null when it
+ * carries none (a date-only widget's bound). Always padded to seconds so
+ * that two halves stay comparable as strings.
+ */
+ public static function timePart(?string $bound): ?string
+ {
+ if ($bound === null) {
+ return null;
+ }
+
+ $parts = preg_split('/[T ]/', $bound);
+ $time = count($parts) > 1 ? $parts[1] : $parts[0];
+
+ if (preg_match('/^(\d{2}):(\d{2})(?::(\d{2}))?$/', $time, $m) !== 1) {
+ return null;
+ }
+
+ return $m[1].':'.$m[2].':'.($m[3] ?? '00');
+ }
+
+ private static function parse(mixed $value): ?Carbon
+ {
+ if ($value === null || $value === '') {
+ return null;
+ }
+
+ if ($value instanceof DateTimeInterface) {
+ return Carbon::instance($value);
+ }
+
+ if (! is_string($value)) {
+ throw InvalidDateBoundaryException::unsupportedType(get_debug_type($value));
+ }
+
+ try {
+ return Carbon::parse($value);
+ } catch (Throwable) {
+ throw InvalidDateBoundaryException::unreadable($value);
+ }
+ }
+
+ /**
+ * Whether the widget's format shows a date and a time, so that a bound's
+ * time half is meaningful on top of its date half.
+ */
+ private static function carriesDateAndTime(string $format): bool
+ {
+ return str_contains($format, 'Y') && str_contains($format, 'H');
+ }
+}
diff --git a/packages/core/src/Foundation/Support/ShortcutLabelFormatter.php b/packages/core/src/Foundation/Support/ShortcutLabelFormatter.php
new file mode 100644
index 00000000..d6d83188
--- /dev/null
+++ b/packages/core/src/Foundation/Support/ShortcutLabelFormatter.php
@@ -0,0 +1,71 @@
+ $mac ? '⌘' : 'Ctrl',
+ 'ctrl', 'control' => $mac ? '⌃' : 'Ctrl',
+ 'shift' => $mac ? '⇧' : 'Shift',
+ 'alt', 'option' => $mac ? '⌥' : 'Alt',
+ 'meta', 'cmd', 'command' => '⌘',
+ 'enter', 'return' => '↵',
+ 'escape', 'esc' => 'Esc',
+ 'delete' => 'Del',
+ 'backspace' => '⌫',
+ 'space' => 'Space',
+ 'tab' => 'Tab',
+ 'arrowup', 'up' => '↑',
+ 'arrowdown', 'down' => '↓',
+ 'arrowleft', 'left' => '←',
+ 'arrowright', 'right' => '→',
+ 'home' => 'Home',
+ 'end' => 'End',
+ 'pageup' => 'PgUp',
+ 'pagedown' => 'PgDn',
+ 'contextmenu' => 'Menu',
+ default => mb_strlen($part) === 1
+ ? mb_strtoupper($part)
+ : ucfirst(strtolower($part)),
+ };
+ }
+}
diff --git a/packages/core/src/Foundation/ValueObjects/ShortcutHint.php b/packages/core/src/Foundation/ValueObjects/ShortcutHint.php
new file mode 100644
index 00000000..9ea4cdf8
--- /dev/null
+++ b/packages/core/src/Foundation/ValueObjects/ShortcutHint.php
@@ -0,0 +1,62 @@
+ */
+ public readonly array $keys;
+
+ /**
+ * @param array|string $keys canonical shortcut strings (`mod+a`, `shift+ArrowDown`, `?`)
+ */
+ public function __construct(
+ array|string $keys,
+ public readonly string $description,
+ ) {
+ $this->keys = array_values((array) $keys);
+ }
+
+ /**
+ * @param array|string $keys
+ */
+ public static function make(array|string $keys, string $description): self
+ {
+ return new self($keys, $description);
+ }
+
+ /**
+ * The keys formatted for display on the given platform.
+ *
+ * @return list
+ */
+ public function labels(bool $mac = false): array
+ {
+ return array_map(
+ fn (string $key): string => ShortcutLabelFormatter::format($key, $mac),
+ $this->keys,
+ );
+ }
+
+ /**
+ * Stable identity for deduplication — the same key set is the same row,
+ * regardless of key order or casing.
+ */
+ public function signature(): string
+ {
+ $keys = array_map('strtolower', $this->keys);
+ sort($keys);
+
+ return implode('|', $keys);
+ }
+}
diff --git a/packages/core/src/Modals/Html/Confirmation.php b/packages/core/src/Modals/Html/Confirmation.php
index f26ccbbe..c1751b2e 100644
--- a/packages/core/src/Modals/Html/Confirmation.php
+++ b/packages/core/src/Modals/Html/Confirmation.php
@@ -19,6 +19,10 @@
* consumer-facing `` tag stays available and renders
* the same shell + {@see ConfirmationStyle}.
*
+ * With no `wireModel` binding, an optional `openOn` window event opens the
+ * dialog purely client-side (`show` is plain Alpine state). `openOn` is only
+ * honoured when `wireModel` is null.
+ *
* Lives in `Modals\Html\` — the Htmlable *render* objects — distinct from the
* `Modals\*` modal *config* objects (`Modals\Modal`, a `ModalContract`) and the
* `Modals\View\*Component` Blade components.
@@ -45,6 +49,7 @@ public function __construct(
public ?string $closeAction = null,
public ?int $zIndex = null,
public ?string $wireModel = null,
+ public ?string $openOn = null,
public ?string $wireClick = null,
public string|Htmlable|null $body = null,
/** @var array> Additional footer actions (Action API). */
@@ -81,6 +86,7 @@ public function toHtml(): string
'closeAction' => $this->closeAction,
'zIndex' => $this->zIndex,
'wireModel' => $this->wireModel,
+ 'openOn' => $this->openOn,
'wireClick' => $this->wireClick,
'body' => $this->body instanceof Htmlable ? $this->body->toHtml() : $this->body,
'footerActions' => $this->footerActions,
diff --git a/packages/core/src/Modals/Html/Modal.php b/packages/core/src/Modals/Html/Modal.php
index 5a29e341..3e5e70bc 100644
--- a/packages/core/src/Modals/Html/Modal.php
+++ b/packages/core/src/Modals/Html/Modal.php
@@ -21,6 +21,12 @@
* `footerView` / `footerData`) — the latter lets a call site keep its existing
* body/footer partial and pass the scope it needs.
*
+ * With no `wireModel` binding, an optional `openOn` window event opens the
+ * modal purely client-side (`show` is plain Alpine state) — the seam for
+ * JS-triggered surfaces such as the keyboard-shortcut help. `openOn` is only
+ * honoured when `wireModel` is null; a Livewire-entangled modal keeps a single
+ * owner of `show`.
+ *
* Lives in `Modals\Html\` — the Htmlable *render* objects — distinct from the
* `Modals\Modal` *config* object (a `ModalContract`) and the
* `Modals\View\ModalComponent` Blade component.
@@ -49,6 +55,7 @@ public function __construct(
public ?string $breakpoint = null,
public ?int $zIndex = null,
public ?string $wireModel = null,
+ public ?string $openOn = null,
public string|Htmlable|null $body = null,
public ?string $bodyView = null,
public array $bodyData = [],
@@ -80,6 +87,7 @@ public function toHtml(): string
'closeAction' => $this->closeAction,
'zIndex' => $this->zIndex,
'wireModel' => $this->wireModel,
+ 'openOn' => $this->openOn,
'body' => $this->body instanceof Htmlable ? $this->body->toHtml() : $this->body,
'bodyView' => $this->bodyView,
'bodyData' => $this->bodyData,
diff --git a/packages/core/src/Modals/Html/SlideOver.php b/packages/core/src/Modals/Html/SlideOver.php
index 235d33e8..0d4da833 100644
--- a/packages/core/src/Modals/Html/SlideOver.php
+++ b/packages/core/src/Modals/Html/SlideOver.php
@@ -17,6 +17,10 @@
* `` tag stays available and renders the same shell +
* {@see SlideOverStyle}.
*
+ * With no `wireModel` binding, an optional `openOn` window event opens the
+ * panel purely client-side (`show` is plain Alpine state). `openOn` is only
+ * honoured when `wireModel` is null.
+ *
* Body / footer accept a pre-rendered `string`/`Htmlable` or a partial + data to
* `@include`. Lives in `Modals\Html\` — the Htmlable *render* objects — distinct
* from the `Modals\SlideOver` *config* object and the
@@ -44,6 +48,7 @@ public function __construct(
public ?string $breakpoint = null,
public ?int $zIndex = null,
public ?string $wireModel = null,
+ public ?string $openOn = null,
public string|Htmlable|null $body = null,
public ?string $bodyView = null,
public array $bodyData = [],
@@ -72,6 +77,7 @@ public function toHtml(): string
'closeAction' => $this->closeAction,
'zIndex' => $this->zIndex,
'wireModel' => $this->wireModel,
+ 'openOn' => $this->openOn,
'body' => $this->body instanceof Htmlable ? $this->body->toHtml() : $this->body,
'bodyView' => $this->bodyView,
'bodyData' => $this->bodyData,
diff --git a/packages/core/src/Modals/View/ConfirmationComponent.php b/packages/core/src/Modals/View/ConfirmationComponent.php
index 6ef6e78c..fb673814 100644
--- a/packages/core/src/Modals/View/ConfirmationComponent.php
+++ b/packages/core/src/Modals/View/ConfirmationComponent.php
@@ -47,6 +47,7 @@ public function __construct(
public ?string $id = null,
public ?string $closeAction = null,
public ?int $zIndex = null,
+ public ?string $openOn = null,
) {
$this->submitLabel ??= Trans::get('wire-core::actions.confirm_submit');
$this->cancelLabel ??= Trans::get('wire-core::actions.confirm_cancel');
diff --git a/packages/core/src/Modals/View/ModalComponent.php b/packages/core/src/Modals/View/ModalComponent.php
index ffe692db..86702d49 100644
--- a/packages/core/src/Modals/View/ModalComponent.php
+++ b/packages/core/src/Modals/View/ModalComponent.php
@@ -47,6 +47,7 @@ public function __construct(
public ?string $closeAction = null,
public ?string $breakpoint = null,
public ?int $zIndex = null,
+ public ?string $openOn = null,
) {}
public function style(): ModalStyle
diff --git a/packages/core/src/Modals/View/SlideOverComponent.php b/packages/core/src/Modals/View/SlideOverComponent.php
index 8632a774..930b751f 100644
--- a/packages/core/src/Modals/View/SlideOverComponent.php
+++ b/packages/core/src/Modals/View/SlideOverComponent.php
@@ -45,6 +45,7 @@ public function __construct(
public bool $bottomSheetOnMobile = false,
public ?string $breakpoint = null,
public ?int $zIndex = null,
+ public ?string $openOn = null,
) {}
public function style(): SlideOverStyle
diff --git a/packages/core/tests/Feature/Actions/InteractsWithActionsTest.php b/packages/core/tests/Feature/Actions/InteractsWithActionsTest.php
index bde8f80c..5f768cb6 100644
--- a/packages/core/tests/Feature/Actions/InteractsWithActionsTest.php
+++ b/packages/core/tests/Feature/Actions/InteractsWithActionsTest.php
@@ -202,15 +202,6 @@ public function openModal(string $name): void
$this->actionModalConfigCache = $this->catalog()[$name]->getModalConfig();
}
- /**
- * The modal-host blade reads the form bridge (a wire-forms concern); this
- * form-free host has no form instance, so it renders as a form-less modal.
- */
- public function getActionModalFormInstance(): mixed
- {
- return null;
- }
-
/** Mounts a name that resolves to no action, then reads the modal config. */
public function peekGhostConfig(): void
{
@@ -340,6 +331,16 @@ public function render(): string
->assertSet('mountedActions.0.name', 'ghost');
});
+it('exposes null form-instance seams on a form-free host (the wire-forms bridge overrides them)', function () {
+ // A standalone wire-core host has no form runtime; the core seams return null
+ // so the action modal-host renders form-less instead of calling a bridge that
+ // does not exist. The wire-forms bridge overrides these via `insteadof`.
+ $host = new CoreActionsHost;
+
+ expect($host->getActionModalFormInstance())->toBeNull()
+ ->and($host->getActionModalFormInstanceForDepth(0))->toBeNull();
+});
+
it('carries a custom mobileBreakpoint into the core modal-host slide-over (regression: it was dropped)', function () {
Livewire::test(CoreActionsHost::class)
->call('openModal', 'slideLeft')
diff --git a/packages/core/tests/TestCase.php b/packages/core/tests/TestCase.php
index 8cd0540a..14002bcb 100644
--- a/packages/core/tests/TestCase.php
+++ b/packages/core/tests/TestCase.php
@@ -5,7 +5,9 @@
namespace NyonCode\WireCore\Tests;
use Livewire\LivewireServiceProvider;
+use NyonCode\WireCore\Actions\Contracts\ModalFormFactory;
use NyonCode\WireCore\WireCoreServiceProvider;
+use NyonCode\WireForms\Forms\Support\FormModalFormFactory;
use Orchestra\Testbench\TestCase as BaseTestCase;
abstract class TestCase extends BaseTestCase
@@ -23,6 +25,14 @@ protected function getEnvironmentSetUp($app): void
$app['config']->set('app.key', 'base64:'.base64_encode(random_bytes(32)));
$app['config']->set('database.default', 'testing');
$app['config']->set('database.connections.testing', static::testing_database_connection());
+
+ // Production wire-core does not depend on wire-forms — the action-modal
+ // form runtime is resolved through the ModalFormFactory seam, bound by
+ // WireFormsServiceProvider in a real app. The core suite's form-behavior
+ // tests exercise that integration against the real Form, so bind the
+ // factory here rather than booting the whole forms provider.
+ // (Graceful degradation when it is *unbound* is covered by ModalFormsTest.)
+ $app->singleton(ModalFormFactory::class, FormModalFormFactory::class);
}
/**
diff --git a/packages/core/tests/Unit/Actions/HasKeyboardShortcutTest.php b/packages/core/tests/Unit/Actions/HasKeyboardShortcutTest.php
index 32c57041..6b5190bc 100644
--- a/packages/core/tests/Unit/Actions/HasKeyboardShortcutTest.php
+++ b/packages/core/tests/Unit/Actions/HasKeyboardShortcutTest.php
@@ -48,3 +48,67 @@
expect($action->shortcutUsesMod())->toBeFalse();
});
+
+// ─── Alpine keydown expressions ──────────────────────────────────────
+
+it('maps each modifier to its Alpine name', function (string $shortcut, string $expected) {
+ expect(Action::make('a')->keyboardShortcut($shortcut)->getAlpineKeydownExpression())->toBe($expected);
+})->with([
+ // mod resolves to ctrl here and is switched to meta in JS, where the
+ // platform is actually known.
+ 'mod' => ['mod+s', 'ctrl.s'],
+ 'ctrl' => ['ctrl+s', 'ctrl.s'],
+ 'control' => ['control+s', 'ctrl.s'],
+ 'shift' => ['shift+s', 'shift.s'],
+ 'alt' => ['alt+s', 'alt.s'],
+ 'option' => ['option+s', 'alt.s'],
+ 'meta' => ['meta+s', 'meta.s'],
+ 'cmd' => ['cmd+s', 'meta.s'],
+ 'command' => ['command+s', 'meta.s'],
+ 'stacked' => ['ctrl+shift+s', 'ctrl.shift.s'],
+]);
+
+it('maps the named keys Alpine spells differently', function (string $shortcut, string $expected) {
+ expect(Action::make('a')->keyboardShortcut($shortcut)->getAlpineKeydownExpression())->toBe($expected);
+})->with([
+ 'delete' => ['Delete', 'delete'],
+ 'enter' => ['Enter', 'enter'],
+ 'return' => ['Return', 'enter'],
+ 'escape' => ['Escape', 'escape'],
+ 'esc' => ['Esc', 'escape'],
+ 'space' => ['Space', 'space'],
+ 'tab' => ['Tab', 'tab'],
+ 'backspace' => ['Backspace', 'backspace'],
+ 'arrowup' => ['ArrowUp', 'up'],
+ 'up' => ['up', 'up'],
+ 'arrowdown' => ['ArrowDown', 'down'],
+ 'down' => ['down', 'down'],
+ 'arrowleft' => ['ArrowLeft', 'left'],
+ 'left' => ['left', 'left'],
+ 'arrowright' => ['ArrowRight', 'right'],
+ 'right' => ['right', 'right'],
+ 'unmapped key passes through' => ['F5', 'f5'],
+]);
+
+it('has no expression for a shortcut that is only modifiers', function () {
+ // Nothing to press: a modifier on its own is not a shortcut.
+ expect(Action::make('a')->keyboardShortcut('ctrl+shift')->getAlpineKeydownExpression())->toBeNull();
+});
+
+it('reports no mod usage when no shortcut is set at all', function () {
+ expect(Action::make('a')->shortcutUsesMod())->toBeFalse();
+});
+
+it('drops the shortcut for a surface that must not own the key', function () {
+ // A rendered button binds its shortcut as a window listener, so an action
+ // rendered on a second (possibly hidden) surface would answer the same key
+ // twice over. That surface renders a copy with the key taken off.
+ $action = Action::make('archive')->keyboardShortcut('Delete', '⌫');
+ $copy = (clone $action)->withoutKeyboardShortcut();
+
+ expect($copy->getKeyboardShortcut())->toBeNull()
+ ->and($copy->getKeyboardShortcutLabel())->toBeNull()
+ ->and($copy->getAlpineKeydownExpression())->toBeNull()
+ // The original is untouched — it is the one the key still fires.
+ ->and($action->getKeyboardShortcut())->toBe('Delete');
+});
diff --git a/packages/core/tests/Unit/Actions/ModalFormsTest.php b/packages/core/tests/Unit/Actions/ModalFormsTest.php
new file mode 100644
index 00000000..070e8f4c
--- /dev/null
+++ b/packages/core/tests/Unit/Actions/ModalFormsTest.php
@@ -0,0 +1,54 @@
+toBeInstanceOf(ModalFormFactory::class)
+ ->and(ModalForms::make([]))->toBeInstanceOf(ModalForm::class);
+});
+
+it('degrades to null when no form factory is bound (standalone wire-core)', function () {
+ app()->offsetUnset(ModalFormFactory::class);
+
+ expect(ModalForms::factory())->toBeNull()
+ ->and(ModalForms::make([]))->toBeNull();
+});
+
+it('degrades an action form modal to a form-less modal when the factory is unbound', function () {
+ app()->offsetUnset(ModalFormFactory::class);
+
+ $action = Action::make('edit')->form([]);
+
+ // The modal still opens (hasModal), it simply carries no form body — a
+ // standalone wire-core renders it as a confirmation/heading dialog.
+ expect($action->hasModal())->toBeTrue()
+ ->and($action->hasFormInstance())->toBeFalse()
+ ->and($action->getFormInstance())->toBeNull()
+ ->and($action->getStepFormInstance())->toBeNull();
+});
+
+it('degrades a wizard step form to null when the factory is unbound', function () {
+ app()->offsetUnset(ModalFormFactory::class);
+
+ $action = Action::make('wizard')->steps([
+ ModalStep::make('One')->schema([]),
+ ]);
+
+ // The step itself resolves; only its form body cannot be built without a
+ // form runtime, so the wizard degrades instead of fataling.
+ expect($action->getModalStep(0))->not->toBeNull()
+ ->and($action->getStepFormInstance(null, null, 0))->toBeNull();
+});
diff --git a/packages/core/tests/Unit/Concerns/HasColorTest.php b/packages/core/tests/Unit/Concerns/HasColorTest.php
index a409936f..3f5e0cbd 100644
--- a/packages/core/tests/Unit/Concerns/HasColorTest.php
+++ b/packages/core/tests/Unit/Concerns/HasColorTest.php
@@ -418,3 +418,33 @@ function rawHues(): array
->and(TestColorClass::getAlertColorClasses('success'))->not->toBe($blue)
->and(TestColorClass::getAlertColorClasses('warning'))->not->toBe($blue);
});
+
+// ─── Row hover (the clickable-row tint) ──────────────────────────────
+
+it('resolves a same-hue row hover across the palette', function () {
+ // The canonical owner of the row hover vocabulary: a record-action table
+ // uses it for rows that are clickable but carry no tint of their own.
+ $hues = ['blue', 'green', 'yellow', 'orange', 'lime', 'teal', 'sky', 'indigo',
+ 'violet', 'purple', 'fuchsia', 'pink', 'rose', 'slate', 'zinc', 'neutral', 'stone'];
+
+ foreach ($hues as $hue) {
+ expect(TestColorClass::getRowHoverClasses($hue))->toBe("hover:bg-$hue-50 dark:hover:bg-$hue-900/20");
+ }
+});
+
+it('maps the semantic names onto their hue for the row hover', function () {
+ expect(TestColorClass::getRowHoverClasses('primary'))->toBe('hover:bg-primary-50 dark:hover:bg-primary-900/20')
+ ->and(TestColorClass::getRowHoverClasses('success'))->toBe(TestColorClass::getRowHoverClasses('emerald'))
+ ->and(TestColorClass::getRowHoverClasses('danger'))->toBe(TestColorClass::getRowHoverClasses('red'))
+ ->and(TestColorClass::getRowHoverClasses('warning'))->toBe(TestColorClass::getRowHoverClasses('amber'))
+ ->and(TestColorClass::getRowHoverClasses('info'))->toBe(TestColorClass::getRowHoverClasses('cyan'));
+});
+
+it('gives black and white a neutral row hover, and falls back to gray', function () {
+ // There is no bg-black-50: the adaptive pair borrows the gray ramp, and an
+ // unknown name lands on the same neutral hover as no override at all.
+ expect(TestColorClass::getRowHoverClasses('black'))->toBe('hover:bg-gray-100 dark:hover:bg-gray-800/80')
+ ->and(TestColorClass::getRowHoverClasses('white'))->toBe('hover:bg-gray-50 dark:hover:bg-gray-800')
+ ->and(TestColorClass::getRowHoverClasses('gray'))->toBe('hover:bg-gray-50 dark:hover:bg-gray-700/30')
+ ->and(TestColorClass::getRowHoverClasses('not-a-colour'))->toBe('hover:bg-gray-50 dark:hover:bg-gray-700/30');
+});
diff --git a/packages/core/tests/Unit/Foundation/Support/DateBoundaryTest.php b/packages/core/tests/Unit/Foundation/Support/DateBoundaryTest.php
new file mode 100644
index 00000000..ae53c9c0
--- /dev/null
+++ b/packages/core/tests/Unit/Foundation/Support/DateBoundaryTest.php
@@ -0,0 +1,89 @@
+toBeNull()
+ ->and(DateBoundary::min('', 'Y-m-d'))->toBeNull()
+ ->and(DateBoundary::max(null, 'Y-m-d'))->toBeNull()
+ ->and(DateBoundary::max('', 'Y-m-d'))->toBeNull();
+});
+
+it('reformats a bound into the widget format', function () {
+ expect(DateBoundary::min('2026-07-10', 'Y-m-d'))->toBe('2026-07-10')
+ ->and(DateBoundary::min('2026-07-10', 'Y-m'))->toBe('2026-07')
+ ->and(DateBoundary::min('2026-07-10 08:30', 'Y-m-d\TH:i'))->toBe('2026-07-10T08:30')
+ ->and(DateBoundary::min('2026-07-10', 'Y-m-d\TH:i'))->toBe('2026-07-10T00:00');
+});
+
+it('reads the date shapes an owner would reasonably write', function () {
+ expect(DateBoundary::min('10.07.2026', 'Y-m-d'))->toBe('2026-07-10')
+ ->and(DateBoundary::min('2026/07/10', 'Y-m-d'))->toBe('2026-07-10')
+ ->and(DateBoundary::min(Carbon::parse('2026-07-10 08:30'), 'Y-m-d\TH:i'))->toBe('2026-07-10T08:30')
+ ->and(DateBoundary::min(CarbonImmutable::parse('2026-07-10'), 'Y-m-d'))->toBe('2026-07-10')
+ ->and(DateBoundary::min(new DateTimeImmutable('2026-07-10'), 'Y-m-d'))->toBe('2026-07-10');
+});
+
+it('reads a relative bound against today', function () {
+ Carbon::setTestNow('2026-07-27 12:00:00');
+
+ expect(DateBoundary::min('today', 'Y-m-d'))->toBe('2026-07-27')
+ ->and(DateBoundary::min('+1 week', 'Y-m-d'))->toBe('2026-08-03');
+
+ Carbon::setTestNow();
+});
+
+it('refuses a bound it cannot read', function () {
+ DateBoundary::min('not a date at all', 'Y-m-d');
+})->throws(InvalidDateBoundaryException::class, 'not a date at all');
+
+it('refuses a bound of an unsupported type', function () {
+ DateBoundary::max(['2026-07-10'], 'Y-m-d');
+})->throws(InvalidDateBoundaryException::class, 'array');
+
+// ─── An upper bound covers its whole day ────────────────────────────────────
+
+it('carries a day-granular upper bound to the end of that day', function () {
+ expect(DateBoundary::max('2026-07-20', 'Y-m-d\TH:i'))->toBe('2026-07-20T23:59')
+ ->and(DateBoundary::max('2026-07-20', 'Y-m-d\TH:i:s'))->toBe('2026-07-20T23:59:59');
+});
+
+it('leaves an upper bound that names a time alone', function () {
+ expect(DateBoundary::max('2026-07-20 17:30', 'Y-m-d\TH:i'))->toBe('2026-07-20T17:30');
+});
+
+it('only stretches the day on a widget that shows a date and a time', function () {
+ // A date-only widget drops the time anyway; on a time-only one midnight is
+ // the owner's actual choice, not an unspoken "end of day".
+ expect(DateBoundary::max('2026-07-20', 'Y-m-d'))->toBe('2026-07-20')
+ ->and(DateBoundary::max('00:00', 'H:i'))->toBe('00:00');
+});
+
+// ─── Splitting a bound for the calendar and the clock ───────────────────────
+
+it('splits a bound into its day and its time', function () {
+ expect(DateBoundary::datePart('2026-07-10T08:30'))->toBe('2026-07-10')
+ ->and(DateBoundary::timePart('2026-07-10T08:30'))->toBe('08:30:00')
+ ->and(DateBoundary::datePart('2026-07-10 08:30:45'))->toBe('2026-07-10')
+ ->and(DateBoundary::timePart('2026-07-10 08:30:45'))->toBe('08:30:45');
+});
+
+it('reports the half a bound does not carry as absent', function () {
+ expect(DateBoundary::datePart('08:30'))->toBeNull()
+ ->and(DateBoundary::timePart('2026-07-10'))->toBeNull()
+ ->and(DateBoundary::datePart(null))->toBeNull()
+ ->and(DateBoundary::timePart(null))->toBeNull()
+ ->and(DateBoundary::timePart('2026-07'))->toBeNull();
+});
+
+it('reads a time-only bound as a time', function () {
+ expect(DateBoundary::timePart('08:30'))->toBe('08:30:00')
+ ->and(DateBoundary::timePart('08:30:45'))->toBe('08:30:45');
+});
diff --git a/packages/core/tests/Unit/Foundation/Support/ShortcutLabelFormatterTest.php b/packages/core/tests/Unit/Foundation/Support/ShortcutLabelFormatterTest.php
new file mode 100644
index 00000000..1aa64aa4
--- /dev/null
+++ b/packages/core/tests/Unit/Foundation/Support/ShortcutLabelFormatterTest.php
@@ -0,0 +1,62 @@
+toBe($other)
+ ->and(ShortcutLabelFormatter::format($shortcut, mac: true))->toBe($mac);
+})->with([
+ 'mod' => ['mod+s', 'Ctrl+S', '⌘S'],
+ 'ctrl' => ['ctrl+d', 'Ctrl+D', '⌃D'],
+ 'control' => ['control+d', 'Ctrl+D', '⌃D'],
+ 'shift' => ['shift+ArrowUp', 'Shift+↑', '⇧↑'],
+ 'alt' => ['alt+f', 'Alt+F', '⌥F'],
+ 'option' => ['option+f', 'Alt+F', '⌥F'],
+ 'meta' => ['meta+k', '⌘+K', '⌘K'],
+ 'cmd' => ['cmd+k', '⌘+K', '⌘K'],
+ 'command' => ['command+k', '⌘+K', '⌘K'],
+ 'stacked modifiers' => ['mod+shift+ArrowDown', 'Ctrl+Shift+↓', '⌘⇧↓'],
+]);
+
+it('formats special keys with their glyphs', function (string $shortcut, string $label) {
+ // Key glyphs are platform-independent; only modifiers and joining differ.
+ expect(ShortcutLabelFormatter::format($shortcut))->toBe($label)
+ ->and(ShortcutLabelFormatter::format($shortcut, mac: true))->toBe($label);
+})->with([
+ 'enter' => ['Enter', '↵'],
+ 'return' => ['return', '↵'],
+ 'escape' => ['Escape', 'Esc'],
+ 'esc' => ['esc', 'Esc'],
+ 'delete' => ['Delete', 'Del'],
+ 'backspace' => ['Backspace', '⌫'],
+ 'space' => ['Space', 'Space'],
+ 'tab' => ['Tab', 'Tab'],
+ 'arrowup' => ['ArrowUp', '↑'],
+ 'up' => ['up', '↑'],
+ 'arrowdown' => ['ArrowDown', '↓'],
+ 'down' => ['down', '↓'],
+ 'arrowleft' => ['ArrowLeft', '←'],
+ 'left' => ['left', '←'],
+ 'arrowright' => ['ArrowRight', '→'],
+ 'right' => ['right', '→'],
+ 'home' => ['Home', 'Home'],
+ 'end' => ['End', 'End'],
+ 'pageup' => ['PageUp', 'PgUp'],
+ 'pagedown' => ['PageDown', 'PgDn'],
+ 'contextmenu' => ['ContextMenu', 'Menu'],
+ 'function key' => ['F10', 'F10'],
+ 'question mark' => ['?', '?'],
+ 'single letter uppercases' => ['a', 'A'],
+]);
+
+it('skips empty parts instead of rendering stray separators', function () {
+ expect(ShortcutLabelFormatter::format('shift+'))->toBe('Shift')
+ ->and(ShortcutLabelFormatter::format(' mod + s '))->toBe('Ctrl+S');
+});
diff --git a/packages/core/tests/Unit/Foundation/ValueObjects/ShortcutHintTest.php b/packages/core/tests/Unit/Foundation/ValueObjects/ShortcutHintTest.php
new file mode 100644
index 00000000..018bda5f
--- /dev/null
+++ b/packages/core/tests/Unit/Foundation/ValueObjects/ShortcutHintTest.php
@@ -0,0 +1,39 @@
+keys)->toBe(['mod+a'])
+ ->and($hint->description)->toBe('Select the whole page');
+});
+
+it('keeps multiple equivalent keys in order', function () {
+ $hint = ShortcutHint::make(['Delete', 'Backspace'], 'Remove');
+
+ expect($hint->keys)->toBe(['Delete', 'Backspace']);
+});
+
+it('formats its keys per platform through the canonical formatter', function () {
+ $hint = ShortcutHint::make(['mod+shift+ArrowUp', 'shift+Home'], 'Extend');
+
+ expect($hint->labels())->toBe(['Ctrl+Shift+↑', 'Shift+Home'])
+ ->and($hint->labels(mac: true))->toBe(['⌘⇧↑', '⇧Home']);
+});
+
+it('has a stable signature regardless of key order and casing', function () {
+ $a = ShortcutHint::make(['Delete', 'Backspace'], 'Remove');
+ $b = ShortcutHint::make(['backspace', 'DELETE'], 'Anything else');
+ $c = ShortcutHint::make(['Delete'], 'Remove');
+
+ expect($a->signature())->toBe($b->signature())
+ ->and($a->signature())->not->toBe($c->signature());
+});
diff --git a/packages/core/tests/Unit/Modals/ConfirmationObjectTest.php b/packages/core/tests/Unit/Modals/ConfirmationObjectTest.php
index 6ea76d1a..45a7707c 100644
--- a/packages/core/tests/Unit/Modals/ConfirmationObjectTest.php
+++ b/packages/core/tests/Unit/Modals/ConfirmationObjectTest.php
@@ -75,6 +75,31 @@ public function render(): string
->assertDontSeeHtml('x-wire-modals::confirmation'); // never falls back to the component
});
+class ConfirmationOpenOnHost extends Component
+{
+ public function render(): string
+ {
+ return <<<'BLADE'
+
+ {!! new \NyonCode\WireCore\Modals\Html\Confirmation(
+ heading: 'Event-opened?',
+ openOn: 'demo-open-confirm',
+ isInformative: true,
+ ) !!}
+
+ BLADE;
+ }
+}
+
+it('opens on a window event instead of a wire:model binding when openOn is set', function () {
+ Livewire::test(ConfirmationOpenOnHost::class)
+ ->assertSeeHtml('x-on:demo-open-confirm.window="show = true"')
+ ->assertSeeHtml('x-data="{ show: false }"')
+ ->assertDontSeeHtml('entangle')
+ // the cancel button still closes the client-side state
+ ->assertSeeHtml('data-testid="confirmation-cancel"');
+});
+
class ConfirmationVariantHost extends Component
{
public bool $show = true;
diff --git a/packages/core/tests/Unit/Modals/ModalComponentRenderTest.php b/packages/core/tests/Unit/Modals/ModalComponentRenderTest.php
index f97b65e6..b38a260e 100644
--- a/packages/core/tests/Unit/Modals/ModalComponentRenderTest.php
+++ b/packages/core/tests/Unit/Modals/ModalComponentRenderTest.php
@@ -44,6 +44,31 @@ public function render(): string
->assertSeeHtml('$wire.closePanel()');
});
+// ─── Event-opened shells (open-on) ────────────────────────────────
+
+class ModalOpenOnTagComponent extends Component
+{
+ public function render(): string
+ {
+ return <<<'BLADE'
+
+ Body
+
+ Body
+
+ BLADE;
+ }
+}
+
+it('renders event-opened shells (open-on) without a wire:model binding', function () {
+ Livewire::test(ModalOpenOnTagComponent::class)
+ ->assertSeeHtml('x-on:demo-tag-modal.window="show = true"')
+ ->assertSeeHtml('x-on:demo-tag-confirm.window="show = true"')
+ ->assertSeeHtml('x-on:demo-tag-panel.window="show = true"')
+ ->assertSeeHtml('x-data="{ show: false }"')
+ ->assertDontSeeHtml('entangle');
+});
+
// ─── Modal stacking z-index ───────────────────────────────────────
class ModalZIndexComponent extends Component
@@ -261,3 +286,33 @@ public function render(): string
config(['wire-core.mobile.breakpoint' => 'sm']);
});
+
+// ─── Focus management (regression: the keyboard died around a modal) ───
+
+it('gives every modal shell the focus trap', function () {
+ // Without it the focus stays wherever it was when the modal opened — on a
+ // grid table, the row behind the dialog. Tab then walks the page behind the
+ // modal, the dialog's own buttons are unreachable, and once it closes the
+ // focus is left on whatever tabbing landed on, which kills the grid's arrow
+ // keys (they only answer when a row itself has the focus).
+ $html = Livewire::test(ModalComponentRenderComponent::class)->html();
+
+ // Three shells, one shared implementation.
+ expect(substr_count($html, 'x-on:keydown.tab'))->toBe(3)
+ ->and(substr_count($html, '_wireFocusFrom'))->toBe(15)
+ ->and(substr_count($html, '_wireFocusable'))->toBe(9)
+ // The root has to be focusable itself, for a dialog with no controls.
+ ->and(substr_count($html, 'tabindex="-1"'))->toBeGreaterThanOrEqual(3);
+});
+
+it('renders the trap expressions whole, not truncated at a quote', function () {
+ // The trap lives in Alpine attributes, where a double quote ends the
+ // attribute early and takes the rest of the expression with it. Assert the
+ // LAST thing in each expression survived, which is what a truncation would
+ // remove first.
+ $html = Livewire::test(ModalComponentRenderComponent::class)->html();
+
+ expect($html)->toContain('preventScroll: true') // tail of x-effect
+ ->toContain('first.focus()') // tail of keydown.tab
+ ->toContain('el.tabIndex'); // tail of x-init
+});
diff --git a/packages/core/tests/Unit/Modals/ModalHtmlObjectTest.php b/packages/core/tests/Unit/Modals/ModalHtmlObjectTest.php
index b808b3e4..716a6fe3 100644
--- a/packages/core/tests/Unit/Modals/ModalHtmlObjectTest.php
+++ b/packages/core/tests/Unit/Modals/ModalHtmlObjectTest.php
@@ -58,6 +58,86 @@ public function render(): string
}
}
+class ModalOpenOnHost extends Component
+{
+ public function render(): string
+ {
+ return <<<'BLADE'
+
+ {!! new \NyonCode\WireCore\Modals\Html\Modal(
+ heading: 'Keyboard shortcuts',
+ openOn: 'demo-open-help',
+ body: '
help
',
+ ) !!}
+ {!! new \NyonCode\WireCore\Modals\Html\SlideOver(
+ heading: 'Event panel',
+ openOn: 'demo-open-panel',
+ body: '
panel
',
+ ) !!}
+
+ BLADE;
+ }
+}
+
+it('opens on a window event instead of a wire:model binding when openOn is set', function () {
+ Livewire::test(ModalOpenOnHost::class)
+ // `show` is plain Alpine state, opened by the window event…
+ ->assertSeeHtml('x-on:demo-open-help.window="show = true"')
+ ->assertSeeHtml('x-on:demo-open-panel.window="show = true"')
+ ->assertSeeHtml('x-data="{ show: false }"')
+ // …and never a Livewire-entangled binding.
+ ->assertDontSeeHtml('entangle');
+});
+
+class ModalOpenOnUnsafeHost extends Component
+{
+ public function render(): string
+ {
+ return <<<'BLADE'
+
+ {!! new \NyonCode\WireCore\Modals\Html\Modal(
+ heading: 'Unsafe',
+ openOn: 'evil onmouseover=alert(1) x',
+ ) !!}
+
+ BLADE;
+ }
+}
+
+it('drops an openOn event name that is not a safe attribute token', function () {
+ // x-on:{event} sits in attribute-name position, where Blade escaping does
+ // not stop a space from starting a brand-new attribute.
+ Livewire::test(ModalOpenOnUnsafeHost::class)
+ ->assertDontSeeHtml('onmouseover')
+ ->assertDontSeeHtml('x-on:evil')
+ ->assertSeeHtml('x-data="{ show: false }"');
+});
+
+class ModalOpenOnBoundHost extends Component
+{
+ public bool $show = false;
+
+ public function render(): string
+ {
+ return <<<'BLADE'
+
+ {!! new \NyonCode\WireCore\Modals\Html\Modal(
+ heading: 'Bound',
+ wireModel: 'show',
+ openOn: 'demo-open-bound',
+ ) !!}
+
+ BLADE;
+ }
+}
+
+it('ignores openOn when a wire:model binding owns the show state', function () {
+ Livewire::test(ModalOpenOnBoundHost::class)
+ ->assertSeeHtml('entangle')
+ ->assertDontSeeHtml('x-on:demo-open-bound.window')
+ ->assertDontSeeHtml('x-data="{ show: false }"');
+});
+
it('renders both as dialogs with body/footer and wire bindings — no ', function () {
Livewire::test(ModalHtmlHost::class)
// Modal
diff --git a/packages/forms/dist/tiptap/chunk-72BVZGAJ.js b/packages/forms/dist/tiptap/chunk-72BVZGAJ.js
new file mode 100644
index 00000000..d1ec8de2
--- /dev/null
+++ b/packages/forms/dist/tiptap/chunk-72BVZGAJ.js
@@ -0,0 +1,104 @@
+function j(n){this.content=n}j.prototype={constructor:j,find:function(n){for(var e=0;e>1}};j.from=function(n){if(n instanceof j)return n;var e=[];if(n)for(var t in n)e.push(t,n[t]);return new j(e)};var En=j;function hi(n,e,t){for(let r=0;;r++){if(r==n.childCount||r==e.childCount)return n.childCount==e.childCount?null:t;let i=n.child(r),s=e.child(r);if(i==s){t+=i.nodeSize;continue}if(!i.sameMarkup(s))return t;if(i.isText&&i.text!=s.text){let o=i.text,l=s.text,a=0;for(;o[a]==l[a];a++)t++;return a&&a0&&u>0&&c[d-1]==f[u-1];)d--,u--,t--,r--;return d&&u&&d=56320&&n<57344}function gi(n){return n>=55296&&n<56320}var b=class n{constructor(e,t){if(this.content=e,this.size=t||0,t==null)for(let r=0;re&&r(a,i+l,s||null,o)!==!1&&a.content.size){let f=l+1;a.nodesBetween(Math.max(0,e-f),Math.min(a.content.size,t-f),r,i+f)}l=c}}descendants(e){this.nodesBetween(0,this.size,e)}textBetween(e,t,r,i){let s="",o=!0;return this.nodesBetween(e,t,(l,a)=>{let c=l.isText?l.text.slice(Math.max(e,a)-a,t-a):l.isLeaf?i?typeof i=="function"?i(l):i:l.type.spec.leafText?l.type.spec.leafText(l):"":"";l.isBlock&&(l.isLeaf&&c||l.isTextblock)&&r&&(o?o=!1:s+=r),s+=c},0),s}append(e){if(!e.size)return this;if(!this.size)return e;let t=this.lastChild,r=e.firstChild,i=this.content.slice(),s=0;for(t.isText&&t.sameMarkup(r)&&(i[i.length-1]=t.withText(t.text+r.text),s=1);se)for(let s=0,o=0;oe&&((ot)&&(l.isText?l=l.cut(Math.max(0,e-o),Math.min(l.text.length,t-o)):l=l.cut(Math.max(0,e-o-1),Math.min(l.content.size,t-o-1))),r.push(l),i+=l.nodeSize),o=a}return new n(r,i)}cutByIndex(e,t){return e==t?n.empty:e==0&&t==this.content.length?this:new n(this.content.slice(e,t))}replaceChild(e,t){let r=this.content[e];if(r==t)return this;let i=this.content.slice(),s=this.size+t.nodeSize-r.nodeSize;return i[e]=t,new n(i,s)}addToStart(e){return new n([e].concat(this.content),this.size+e.nodeSize)}addToEnd(e){return new n(this.content.concat(e),this.size+e.nodeSize)}eq(e){if(this.content.length!=e.content.length)return!1;for(let t=0;tthis.size||e<0)throw new RangeError(`Position ${e} outside of fragment (${this})`);for(let t=0,r=0;;t++){let i=this.child(t),s=r+i.nodeSize;if(s>=e)return s==e?Lt(t+1,s):Lt(t,r);r=s}}toString(){return"<"+this.toStringInner()+">"}toStringInner(){return this.content.join(", ")}toJSON(){return this.content.length?this.content.map(e=>e.toJSON()):null}static fromJSON(e,t){if(!t)return n.empty;if(!Array.isArray(t))throw new RangeError("Invalid input for Fragment.fromJSON");return n.fromArray(t.map(e.nodeFromJSON))}static fromArray(e){if(!e.length)return n.empty;let t,r=0;for(let i=0;ithis.type.rank&&(t||(t=e.slice(0,i)),t.push(this),r=!0),t&&t.push(s)}}return t||(t=e.slice()),r||t.push(this),t}removeFromSet(e){for(let t=0;tr.type.rank-i.type.rank),t}};A.none=[];var Ce=class extends Error{},x=class n{constructor(e,t,r){this.content=e,this.openStart=t,this.openEnd=r}get size(){return this.content.size-this.openStart-this.openEnd}insertAt(e,t){let r=bi(this.content,e+this.openStart,t,this.openStart+1,this.openEnd+1);return r&&new n(r,this.openStart,this.openEnd)}removeBetween(e,t){return new n(yi(this.content,e+this.openStart,t+this.openStart),this.openStart,this.openEnd)}eq(e){return this.content.eq(e.content)&&this.openStart==e.openStart&&this.openEnd==e.openEnd}toString(){return this.content+"("+this.openStart+","+this.openEnd+")"}toJSON(){if(!this.content.size)return null;let e={content:this.content.toJSON()};return this.openStart>0&&(e.openStart=this.openStart),this.openEnd>0&&(e.openEnd=this.openEnd),e}static fromJSON(e,t){if(!t)return n.empty;let r=t.openStart||0,i=t.openEnd||0;if(typeof r!="number"||typeof i!="number")throw new RangeError("Invalid input for Slice.fromJSON");return new n(b.fromJSON(e,t.content),r,i)}static maxOpen(e,t=!0){let r=0,i=0;for(let s=e.firstChild;s&&!s.isLeaf&&(t||!s.type.spec.isolating);s=s.firstChild)r++;for(let s=e.lastChild;s&&!s.isLeaf&&(t||!s.type.spec.isolating);s=s.lastChild)i++;return new n(e,r,i)}};x.empty=new x(b.empty,0,0);function yi(n,e,t){let{index:r,offset:i}=n.findIndex(e),s=n.maybeChild(r),{index:o,offset:l}=n.findIndex(t);if(i==e||s.isText){if(l!=t&&!n.child(o).isText)throw new RangeError("Removing non-flat range");return n.cut(0,e).append(n.cut(t))}if(r!=o)throw new RangeError("Removing non-flat range");return n.replaceChild(r,s.copy(yi(s.content,e-i-1,t-i-1)))}function bi(n,e,t,r,i,s){let{index:o,offset:l}=n.findIndex(e),a=n.maybeChild(o);if(l==e||a.isText)return s&&r<=0&&i<=0&&!s.canReplace(o,o,t)?null:n.cut(0,e).append(t).append(n.cut(e));let c=bi(a.content,e-l-1,t,o==0?r-1:0,o==n.childCount-1?i-1:0,a);return c&&n.replaceChild(o,a.copy(c))}function bl(n,e,t){if(t.openStart>n.depth)throw new Ce("Inserted content deeper than insertion position");if(n.depth-t.openStart!=e.depth-t.openEnd)throw new Ce("Inconsistent open depths");return xi(n,e,t,0)}function xi(n,e,t,r){let i=n.index(r),s=n.node(r);if(i==e.index(r)&&r=0&&n.isText&&n.sameMarkup(e[t])?e[t]=n.withText(e[t].text+n.text):e.push(n)}function dt(n,e,t,r){let i=(e||n).node(t),s=0,o=e?e.index(t):i.childCount;n&&(s=n.index(t),n.depth>t?s++:n.textOffset&&(Pe(n.nodeAfter,r),s++));for(let l=s;li&&On(n,e,i+1),o=r.depth>i&&On(t,r,i+1),l=[];return dt(null,n,i,l),s&&o&&e.index(i)==t.index(i)?(Si(s,o),Pe(Ie(s,ki(n,e,t,r,i+1)),l)):(s&&Pe(Ie(s,Ht(n,e,i+1)),l),dt(e,t,i,l),o&&Pe(Ie(o,Ht(t,r,i+1)),l)),dt(r,null,i,l),new b(l)}function Ht(n,e,t){let r=[];if(dt(null,n,t,r),n.depth>t){let i=On(n,e,t+1);Pe(Ie(i,Ht(n,e,t+1)),r)}return dt(e,null,t,r),new b(r)}function xl(n,e){let t=e.depth-n.openStart,i=e.node(t).copy(n.content);for(let s=t-1;s>=0;s--)i=e.node(s).copy(b.from(i));return{start:i.resolveNoCache(n.openStart+t),end:i.resolveNoCache(i.content.size-n.openEnd-t)}}var Jt=class n{constructor(e,t,r){this.pos=e,this.path=t,this.parentOffset=r,this.depth=t.length/3-1}resolveDepth(e){return e==null?this.depth:e<0?this.depth+e:e}get parent(){return this.node(this.depth)}get doc(){return this.node(0)}node(e){return this.path[this.resolveDepth(e)*3]}index(e){return this.path[this.resolveDepth(e)*3+1]}indexAfter(e){return e=this.resolveDepth(e),this.index(e)+(e==this.depth&&!this.textOffset?0:1)}start(e){return e=this.resolveDepth(e),e==0?0:this.path[e*3-1]+1}end(e){return e=this.resolveDepth(e),this.start(e)+this.node(e).content.size}before(e){if(e=this.resolveDepth(e),!e)throw new RangeError("There is no position before the top-level node");return e==this.depth+1?this.pos:this.path[e*3-1]}after(e){if(e=this.resolveDepth(e),!e)throw new RangeError("There is no position after the top-level node");return e==this.depth+1?this.pos:this.path[e*3-1]+this.path[e*3].nodeSize}get textOffset(){return this.pos-this.path[this.path.length-1]}get nodeAfter(){let e=this.parent,t=this.index(this.depth);if(t==e.childCount)return null;let r=this.pos-this.path[this.path.length-1],i=e.child(t);return r?e.child(t).cut(r):i}get nodeBefore(){let e=this.index(this.depth),t=this.pos-this.path[this.path.length-1];return t?this.parent.child(e).cut(0,t):e==0?null:this.parent.child(e-1)}posAtIndex(e,t){t=this.resolveDepth(t);let r=this.path[t*3],i=t==0?0:this.path[t*3-1]+1;for(let s=0;s0;t--)if(this.start(t)<=e&&this.end(t)>=e)return t;return 0}blockRange(e=this,t){if(e.pos=0;r--)if(e.pos<=this.end(r)&&(!t||t(this.node(r))))return new ze(this,e,r);return null}sameParent(e){return this.pos-this.parentOffset==e.pos-e.parentOffset}max(e){return e.pos>this.pos?e:this}min(e){return e.pos=0&&t<=e.content.size))throw new RangeError("Position "+t+" out of range");let r=[],i=0,s=t;for(let o=e;;){let{index:l,offset:a}=o.content.findIndex(s),c=s-a;if(r.push(o,l,i+a),!c||(o=o.child(l),o.isText))break;s=c-1,i+=a+1}return new n(t,r,s)}static resolveCached(e,t){let r=ii.get(e);if(r)for(let s=0;se&&this.nodesBetween(e,t,s=>(r.isInSet(s.marks)&&(i=!0),!i)),i}get isBlock(){return this.type.isBlock}get isTextblock(){return this.type.isTextblock}get inlineContent(){return this.type.inlineContent}get isInline(){return this.type.isInline}get isText(){return this.type.isText}get isLeaf(){return this.type.isLeaf}get isAtom(){return this.type.isAtom}toString(){if(this.type.spec.toDebugString)return this.type.spec.toDebugString(this);let e=this.type.name;return this.content.size&&(e+="("+this.content.toStringInner()+")"),Mi(this.marks,e)}contentMatchAt(e){let t=this.type.contentMatch.matchFragment(this.content,0,e);if(!t)throw new Error("Called contentMatchAt on a node with invalid content");return t}canReplace(e,t,r=b.empty,i=0,s=r.childCount){let o=this.contentMatchAt(e).matchFragment(r,i,s),l=o&&o.matchFragment(this.content,t);if(!l||!l.validEnd)return!1;for(let a=i;at.type.name)}`);this.content.forEach(t=>t.check())}toJSON(){let e={type:this.type.name};for(let t in this.attrs){e.attrs=this.attrs;break}return this.content.size&&(e.content=this.content.toJSON()),this.marks.length&&(e.marks=this.marks.map(t=>t.toJSON())),e}static fromJSON(e,t){if(!t)throw new RangeError("Invalid input for Node.fromJSON");let r;if(t.marks){if(!Array.isArray(t.marks))throw new RangeError("Invalid mark data for Node.fromJSON");r=t.marks.map(e.markFromJSON)}if(t.type=="text"){if(typeof t.text!="string")throw new RangeError("Invalid text node in JSON");return e.text(t.text,r)}let i=b.fromJSON(e,t.content),s=e.nodeType(t.type).create(t.attrs,i,r);return s.type.checkAttrs(s.attrs),s}};Q.prototype.text=void 0;var Dn=class n extends Q{constructor(e,t,r,i){if(super(e,t,null,i),!r)throw new RangeError("Empty text nodes are not allowed");this.text=r}toString(){return this.type.spec.toDebugString?this.type.spec.toDebugString(this):Mi(this.marks,JSON.stringify(this.text))}get textContent(){return this.text}textBetween(e,t){return this.text.slice(e,t)}get nodeSize(){return this.text.length}mark(e){return e==this.marks?this:new n(this.type,this.attrs,this.text,e)}withText(e){return e==this.text?this:new n(this.type,this.attrs,e,this.marks)}cut(e=0,t=this.text.length){return e==0&&t==this.text.length?this:this.withText(this.text.slice(e,t))}eq(e){return this.sameMarkup(e)&&this.text==e.text}toJSON(){let e=super.toJSON();return e.text=this.text,e}};function Mi(n,e){for(let t=n.length-1;t>=0;t--)e=n[t].type.name+"("+e+")";return e}var Be=class n{constructor(e){this.validEnd=e,this.next=[],this.wrapCache=[]}static parse(e,t){let r=new Rn(e,t);if(r.next==null)return n.empty;let i=wi(r);r.next&&r.err("Unexpected trailing text");let s=vl(El(i));return Ol(s,r),s}matchType(e){for(let t=0;tc.createAndFill()));for(let c=0;c=this.next.length)throw new RangeError(`There's no ${e}th edge in this content match`);return this.next[e]}toString(){let e=[];function t(r){e.push(r);for(let i=0;i{let s=i+(r.validEnd?"*":" ")+" ";for(let o=0;o"+e.indexOf(r.next[o].next);return s}).join(`
+`)}};Be.empty=new Be(!0);var Rn=class{constructor(e,t){this.string=e,this.nodeTypes=t,this.inline=null,this.pos=0,this.tokens=e.split(/\s*(?=\b|\W|$)/),this.tokens[this.tokens.length-1]==""&&this.tokens.pop(),this.tokens[0]==""&&this.tokens.shift()}get next(){return this.tokens[this.pos]}eat(e){return this.next==e&&(this.pos++||!0)}err(e){throw new SyntaxError(e+" (in content expression '"+this.string+"')")}};function wi(n){let e=[];do e.push(Ml(n));while(n.eat("|"));return e.length==1?e[0]:{type:"choice",exprs:e}}function Ml(n){let e=[];do e.push(wl(n));while(n.next&&n.next!=")"&&n.next!="|");return e.length==1?e[0]:{type:"seq",exprs:e}}function wl(n){let e=Nl(n);for(;;)if(n.eat("+"))e={type:"plus",expr:e};else if(n.eat("*"))e={type:"star",expr:e};else if(n.eat("?"))e={type:"opt",expr:e};else if(n.eat("{"))e=Cl(n,e);else break;return e}function si(n){/\D/.test(n.next)&&n.err("Expected number, got '"+n.next+"'");let e=Number(n.next);return n.pos++,e}function Cl(n,e){let t=si(n),r=t;return n.eat(",")&&(n.next!="}"?r=si(n):r=-1),n.eat("}")||n.err("Unclosed braced range"),{type:"range",min:t,max:r,expr:e}}function Tl(n,e){let t=n.nodeTypes,r=t[e];if(r)return[r];let i=[];for(let s in t){let o=t[s];o.isInGroup(e)&&i.push(o)}return i.length==0&&n.err("No node type or group '"+e+"' found"),i}function Nl(n){if(n.eat("(")){let e=wi(n);return n.eat(")")||n.err("Missing closing paren"),e}else if(/\W/.test(n.next))n.err("Unexpected token '"+n.next+"'");else{let e=Tl(n,n.next).map(t=>(n.inline==null?n.inline=t.isInline:n.inline!=t.isInline&&n.err("Mixing inline and block content"),{type:"name",value:t}));return n.pos++,e.length==1?e[0]:{type:"choice",exprs:e}}}function El(n){let e=[[]];return i(s(n,0),t()),e;function t(){return e.push([])-1}function r(o,l,a){let c={term:a,to:l};return e[o].push(c),c}function i(o,l){o.forEach(a=>a.to=l)}function s(o,l){if(o.type=="choice")return o.exprs.reduce((a,c)=>a.concat(s(c,l)),[]);if(o.type=="seq")for(let a=0;;a++){let c=s(o.exprs[a],l);if(a==o.exprs.length-1)return c;i(c,l=t())}else if(o.type=="star"){let a=t();return r(l,a),i(s(o.expr,a),a),[r(a)]}else if(o.type=="plus"){let a=t();return i(s(o.expr,l),a),i(s(o.expr,a),a),[r(a)]}else{if(o.type=="opt")return[r(l)].concat(s(o.expr,l));if(o.type=="range"){let a=l;for(let c=0;c{n[o].forEach(({term:l,to:a})=>{if(!l)return;let c;for(let f=0;f{c||i.push([l,c=[]]),c.indexOf(f)==-1&&c.push(f)})})});let s=e[r.join(",")]=new Be(r.indexOf(n.length-1)>-1);for(let o=0;o-1}get whitespace(){return this.spec.whitespace||(this.spec.code?"pre":"normal")}hasRequiredAttrs(){for(let e in this.attrs)if(this.attrs[e].isRequired)return!0;return!1}compatibleContent(e){return this==e||this.contentMatch.compatible(e.contentMatch)}computeAttrs(e){return!e&&this.defaultAttrs?this.defaultAttrs:Ni(this.attrs,e)}create(e=null,t,r){if(this.isText)throw new Error("NodeType.create can't construct text nodes");return new Q(this,this.computeAttrs(e),b.from(t),A.setFrom(r))}createChecked(e=null,t,r){return t=b.from(t),this.checkContent(t),new Q(this,this.computeAttrs(e),t,A.setFrom(r))}createAndFill(e=null,t,r){if(e=this.computeAttrs(e),t=b.from(t),t.size){let o=this.contentMatch.fillBefore(t);if(!o)return null;t=o.append(t)}let i=this.contentMatch.matchFragment(t),s=i&&i.fillBefore(b.empty,!0);return s?new Q(this,e,t.append(s),A.setFrom(r)):null}validContent(e){let t=this.contentMatch.matchFragment(e);if(!t||!t.validEnd)return!1;for(let r=0;r-1}allowsMarks(e){if(this.markSet==null)return!0;for(let t=0;tr[s]=new n(s,t,o));let i=t.spec.topNode||"doc";if(!r[i])throw new RangeError("Schema is missing its top node type ('"+i+"')");if(!r.text)throw new RangeError("Every schema needs a 'text' type");for(let s in r.text.attrs)throw new RangeError("The text node type should not have attributes");return r}};function Al(n,e,t){let r=t.split("|");return i=>{let s=i===null?"null":typeof i;if(r.indexOf(s)<0)throw new RangeError(`Expected value of type ${r} for attribute ${e} on type ${n}, got ${s}`)}}var Pn=class{constructor(e,t,r){this.hasDefault=Object.prototype.hasOwnProperty.call(r,"default"),this.default=r.default,this.validate=typeof r.validate=="string"?Al(e,t,r.validate):r.validate}get isRequired(){return!this.hasDefault}},ht=class n{constructor(e,t,r,i){this.name=e,this.rank=t,this.schema=r,this.spec=i,this.attrs=vi(e,i.attrs),this.excluded=null;let s=Ti(this.attrs);this.instance=s?new A(this,s):null}create(e=null){return!e&&this.instance?this.instance:new A(this,Ni(this.attrs,e))}static compile(e,t){let r=Object.create(null),i=0;return e.forEach((s,o)=>r[s]=new n(s,i++,t,o)),r}removeFromSet(e){for(var t=0;t-1}},Ge=class{constructor(e){this.linebreakReplacement=null,this.cached=Object.create(null);let t=this.spec={};for(let i in e)t[i]=e[i];t.nodes=En.from(e.nodes),t.marks=En.from(e.marks||{}),this.nodes=jt.compile(this.spec.nodes,this),this.marks=ht.compile(this.spec.marks,this);let r=Object.create(null);for(let i in this.nodes){if(i in this.marks)throw new RangeError(i+" can not be both a node and a mark");let s=this.nodes[i],o=s.spec.content||"",l=s.spec.marks;if(s.contentMatch=r[o]||(r[o]=Be.parse(o,this.nodes)),s.inlineContent=s.contentMatch.inlineContent,s.spec.linebreakReplacement){if(this.linebreakReplacement)throw new RangeError("Multiple linebreak nodes defined");if(!s.isInline||!s.isLeaf)throw new RangeError("Linebreak replacement nodes must be inline leaf nodes");this.linebreakReplacement=s}s.markSet=l=="_"?null:l?li(this,l.split(" ")):l==""||!s.inlineContent?[]:null}for(let i in this.marks){let s=this.marks[i],o=s.spec.excludes;s.excluded=o==null?[s]:o==""?[]:li(this,o.split(" "))}this.nodeFromJSON=i=>Q.fromJSON(this,i),this.markFromJSON=i=>A.fromJSON(this,i),this.topNodeType=this.nodes[this.spec.topNode||"doc"],this.cached.wrappings=Object.create(null)}node(e,t=null,r,i){if(typeof e=="string")e=this.nodeType(e);else if(e instanceof jt){if(e.schema!=this)throw new RangeError("Node type from different schema used ("+e.name+")")}else throw new RangeError("Invalid node type: "+e);return e.createChecked(t,r,i)}text(e,t){let r=this.nodes.text;return new Dn(r,r.defaultAttrs,e,A.setFrom(t))}mark(e,t){return typeof e=="string"&&(e=this.marks[e]),e.create(t)}nodeType(e){let t=this.nodes[e];if(!t)throw new RangeError("Unknown node type: "+e);return t}};function li(n,e){let t=[];for(let r=0;r-1)&&t.push(o=a)}if(!o)throw new SyntaxError("Unknown mark type: '"+e[r]+"'")}return t}function Dl(n){return n.tag!=null}function Rl(n){return n.style!=null}var fe=class n{constructor(e,t){this.schema=e,this.rules=t,this.tags=[],this.styles=[];let r=this.matchedStyles=[];t.forEach(i=>{if(Dl(i))this.tags.push(i);else if(Rl(i)){let s=/[^=]*/.exec(i.style)[0];r.indexOf(s)<0&&r.push(s),this.styles.push(i)}}),this.normalizeLists=!this.tags.some(i=>{if(!/^(ul|ol)\b/.test(i.tag)||!i.node)return!1;let s=e.nodes[i.node];return s.contentMatch.matchType(s)})}parse(e,t={}){let r=new _t(this,t,!1);return r.addAll(e,A.none,t.from,t.to),r.finish()}parseSlice(e,t={}){let r=new _t(this,t,!0);return r.addAll(e,A.none,t.from,t.to),x.maxOpen(r.finish())}matchTag(e,t,r){for(let i=r?this.tags.indexOf(r)+1:0;ie.length&&(l.charCodeAt(e.length)!=61||l.slice(e.length+1)!=t))){if(o.getAttrs){let a=o.getAttrs(t);if(a===!1)continue;o.attrs=a||void 0}return o}}}static schemaRules(e){let t=[];function r(i){let s=i.priority==null?50:i.priority,o=0;for(;o{r(o=ci(o)),o.mark||o.ignore||o.clearMark||(o.mark=i)})}for(let i in e.nodes){let s=e.nodes[i].spec.parseDOM;s&&s.forEach(o=>{r(o=ci(o)),o.node||o.ignore||o.mark||(o.node=i)})}return t}static fromSchema(e){return e.cached.domParser||(e.cached.domParser=new n(e,n.schemaRules(e)))}},Oi={address:!0,article:!0,aside:!0,blockquote:!0,body:!0,canvas:!0,dd:!0,div:!0,dl:!0,fieldset:!0,figcaption:!0,figure:!0,footer:!0,form:!0,h1:!0,h2:!0,h3:!0,h4:!0,h5:!0,h6:!0,header:!0,hgroup:!0,hr:!0,li:!0,noscript:!0,ol:!0,output:!0,p:!0,pre:!0,section:!0,table:!0,tfoot:!0,ul:!0},Pl={head:!0,noscript:!0,object:!0,script:!0,style:!0,title:!0},Ai={ol:!0,ul:!0},pt=1,In=2,ut=4;function ai(n,e,t){return e!=null?(e?pt:0)|(e==="full"?In:0):n&&n.whitespace=="pre"?pt|In:t&~ut}var Ye=class{constructor(e,t,r,i,s,o){this.type=e,this.attrs=t,this.marks=r,this.solid=i,this.options=o,this.content=[],this.activeMarks=A.none,this.match=s||(o&ut?null:e.contentMatch)}findWrapping(e){if(!this.match){if(!this.type)return[];let t=this.type.contentMatch.fillBefore(b.from(e));if(t)this.match=this.type.contentMatch.matchFragment(t);else{let r=this.type.contentMatch,i;return(i=r.findWrapping(e.type))?(this.match=r,i):null}}return this.match.findWrapping(e.type)}finish(e){if(!(this.options&pt)){let r=this.content[this.content.length-1],i;if(r&&r.isText&&(i=/[ \t\r\n\u000c]+$/.exec(r.text))){let s=r;r.text.length==i[0].length?this.content.pop():this.content[this.content.length-1]=s.withText(s.text.slice(0,s.text.length-i[0].length))}}let t=b.from(this.content);return!e&&this.match&&(t=t.append(this.match.fillBefore(b.empty,!0))),this.type?this.type.create(this.attrs,t,this.marks):t}inlineContext(e){return this.type?this.type.inlineContent:this.content.length?this.content[0].isInline:e.parentNode&&!Oi.hasOwnProperty(e.parentNode.nodeName.toLowerCase())}},_t=class{constructor(e,t,r){this.parser=e,this.options=t,this.isOpen=r,this.open=0,this.localPreserveWS=!1;let i=t.topNode,s,o=ai(null,t.preserveWhitespace,0)|(r?ut:0);i?s=new Ye(i.type,i.attrs,A.none,!0,t.topMatch||i.type.contentMatch,o):r?s=new Ye(null,null,A.none,!0,null,o):s=new Ye(e.schema.topNodeType,null,A.none,!0,null,o),this.nodes=[s],this.find=t.findPositions,this.needsBlock=!1}get top(){return this.nodes[this.open]}addDOM(e,t){e.nodeType==3?this.addTextNode(e,t):e.nodeType==1&&this.addElement(e,t)}addTextNode(e,t){let r=e.nodeValue,i=this.top,s=i.options&In?"full":this.localPreserveWS||(i.options&pt)>0,{schema:o}=this.parser;if(s==="full"||i.inlineContext(e)||/[^ \t\r\n\u000c]/.test(r)){if(s)if(s==="full")r=r.replace(/\r\n?/g,`
+`);else if(o.linebreakReplacement&&/[\r\n]/.test(r)&&this.top.findWrapping(o.linebreakReplacement.create())){let l=r.split(/\r?\n|\r/);for(let a=0;a!a.clearMark(c)):t=t.concat(this.parser.schema.marks[a.mark].create(a.attrs)),a.consuming===!1)l=a;else break}}return t}addElementByRule(e,t,r,i){let s,o;if(t.node)if(o=this.parser.schema.nodes[t.node],o.isLeaf)this.insertNode(o.create(t.attrs),r,e.nodeName=="BR")||this.leafFallback(e,r);else{let a=this.enter(o,t.attrs||null,r,t.preserveWhitespace);a&&(s=!0,r=a)}else{let a=this.parser.schema.marks[t.mark];r=r.concat(a.create(t.attrs))}let l=this.top;if(o&&o.isLeaf)this.findInside(e);else if(i)this.addElement(e,r,i);else if(t.getContent)this.findInside(e),t.getContent(e,this.parser.schema).forEach(a=>this.insertNode(a,r,!1));else{let a=e;typeof t.contentElement=="string"?a=e.querySelector(t.contentElement):typeof t.contentElement=="function"?a=t.contentElement(e):t.contentElement&&(a=t.contentElement),this.findAround(e,a,!0),this.addAll(a,r),this.findAround(e,a,!1)}s&&this.sync(l)&&this.open--}addAll(e,t,r,i){let s=r||0;for(let o=r?e.childNodes[r]:e.firstChild,l=i==null?null:e.childNodes[i];o!=l;o=o.nextSibling,++s)this.findAtPoint(e,s),this.addDOM(o,t);this.findAtPoint(e,s)}findPlace(e,t,r){let i,s;for(let o=this.open,l=0;o>=0;o--){let a=this.nodes[o],c=a.findWrapping(e);if(c&&(!i||i.length>c.length+l)&&(i=c,s=a,!c.length))break;if(a.solid){if(r)break;l+=2}}if(!i)return null;this.sync(s);for(let o=0;o(o.type?o.type.allowsMarkType(c.type):fi(c.type,e))?(a=c.addToSet(a),!1):!0),this.nodes.push(new Ye(e,t,a,i,null,l)),this.open++,r}closeExtra(e=!1){let t=this.nodes.length-1;if(t>this.open){for(;t>this.open;t--)this.nodes[t-1].content.push(this.nodes[t].finish(e));this.nodes.length=this.open+1}}finish(){return this.open=0,this.closeExtra(this.isOpen),this.nodes[0].finish(!!(this.isOpen||this.options.topOpen))}sync(e){for(let t=this.open;t>=0;t--){if(this.nodes[t]==e)return this.open=t,!0;this.localPreserveWS&&(this.nodes[t].options|=pt)}return!1}get currentPos(){this.closeExtra();let e=0;for(let t=this.open;t>=0;t--){let r=this.nodes[t].content;for(let i=r.length-1;i>=0;i--)e+=r[i].nodeSize;t&&e++}return e}findAtPoint(e,t){if(this.find)for(let r=0;r-1)return e.split(/\s*\|\s*/).some(this.matchesContext,this);let t=e.split("/"),r=this.options.context,i=!this.isOpen&&(!r||r.parent.type==this.nodes[0].type),s=-(r?r.depth+1:0)+(i?0:1),o=(l,a)=>{for(;l>=0;l--){let c=t[l];if(c==""){if(l==t.length-1||l==0)continue;for(;a>=s;a--)if(o(l-1,a))return!0;return!1}else{let f=a>0||a==0&&i?this.nodes[a].type:r&&a>=s?r.node(a-s).type:null;if(!f||f.name!=c&&!f.isInGroup(c))return!1;a--}}return!0};return o(t.length-1,this.open)}textblockFromContext(){let e=this.options.context;if(e)for(let t=e.depth;t>=0;t--){let r=e.node(t).contentMatchAt(e.indexAfter(t)).defaultType;if(r&&r.isTextblock&&r.defaultAttrs)return r}for(let t in this.parser.schema.nodes){let r=this.parser.schema.nodes[t];if(r.isTextblock&&r.defaultAttrs)return r}}};function Il(n){for(let e=n.firstChild,t=null;e;e=e.nextSibling){let r=e.nodeType==1?e.nodeName.toLowerCase():null;r&&Ai.hasOwnProperty(r)&&t?(t.appendChild(e),e=t):r=="li"?t=e:r&&(t=null)}}function zl(n,e){return(n.matches||n.msMatchesSelector||n.webkitMatchesSelector||n.mozMatchesSelector).call(n,e)}function ci(n){let e={};for(let t in n)e[t]=n[t];return e}function fi(n,e){let t=e.schema.nodes;for(let r in t){let i=t[r];if(!i.allowsMarkType(n))continue;let s=[],o=l=>{s.push(l);for(let a=0;a{if(s.length||o.marks.length){let l=0,a=0;for(;l=0;i--){let s=this.serializeMark(e.marks[i],e.isInline,t);s&&((s.contentDOM||s.dom).appendChild(r),r=s.dom)}return r}serializeMark(e,t,r={}){let i=this.marks[e.type.name];return i&&$t(Vt(r),i(e,t),null,e.attrs)}static renderSpec(e,t,r=null,i){return typeof t=="string"?{dom:e.createTextNode(t)}:$t(e,t,r,i)}static fromSchema(e){return e.cached.domSerializer||(e.cached.domSerializer=new n(this.nodesFromSchema(e),this.marksFromSchema(e)))}static nodesFromSchema(e){let t=di(e.nodes);return t.text||(t.text=r=>r.text),t}static marksFromSchema(e){return di(e.marks)}};function di(n){let e={};for(let t in n){let r=n[t].spec.toDOM;r&&(e[t]=r)}return e}function Vt(n){return n.document||window.document}var ui=new WeakMap;function Bl(n){let e=ui.get(n);return e===void 0&&ui.set(n,e=Fl(n)),e}function Fl(n){let e=null;function t(r){if(r&&typeof r=="object")if(Array.isArray(r))if(typeof r[0]=="string")e||(e=[]),e.push(r);else for(let i=0;i-1)throw new RangeError("Using an array from an attribute object as a DOM spec. This may be an attempted cross site scripting attack.");let o=i.indexOf(" ");o>0&&(t=i.slice(0,o),i=i.slice(o+1));let l,a=t?n.createElementNS(t,i):n.createElement(i),c=e[1],f=1;if(c&&typeof c=="object"&&c.nodeType==null&&!Array.isArray(c)){f=2;for(let d in c)if(c[d]!=null){let u=d.indexOf(" ");u>0?a.setAttributeNS(d.slice(0,u),d.slice(u+1),c[d]):d=="style"&&a.style?a.style.cssText=c[d]:a.setAttribute(d,c[d])}}for(let d=f;df)throw new RangeError("Content hole must be the only child of its parent node");return{dom:a,contentDOM:a}}else if(typeof u=="string")a.appendChild(n.createTextNode(u));else{let{dom:h,contentDOM:p}=$t(n,u,t,r);if(a.appendChild(h),p){if(l)throw new RangeError("Multiple content holes");l=p}}}return{dom:a,contentDOM:l}}var Pi=65535,Ii=Math.pow(2,16);function Ll(n,e){return n+e*Ii}function Di(n){return n&Pi}function Vl(n){return(n-(n&Pi))/Ii}var zi=1,Bi=2,Kt=4,Fi=8,yt=class{constructor(e,t,r){this.pos=e,this.delInfo=t,this.recover=r}get deleted(){return(this.delInfo&Fi)>0}get deletedBefore(){return(this.delInfo&(zi|Kt))>0}get deletedAfter(){return(this.delInfo&(Bi|Kt))>0}get deletedAcross(){return(this.delInfo&Kt)>0}},pe=class n{constructor(e,t=!1){if(this.ranges=e,this.inverted=t,!e.length&&n.empty)return n.empty}recover(e){let t=0,r=Di(e);if(!this.inverted)for(let i=0;ie)break;let c=this.ranges[l+s],f=this.ranges[l+o],d=a+c;if(e<=d){let u=c?e==a?-1:e==d?1:t:t,h=a+i+(u<0?0:f);if(r)return h;let p=e==(t<0?a:d)?null:Ll(l/3,e-a),m=e==a?Bi:e==d?zi:Kt;return(t<0?e!=a:e!=d)&&(m|=Fi),new yt(h,m,p)}i+=f-c}return r?e+i:new yt(e+i,0,null)}touches(e,t){let r=0,i=Di(t),s=this.inverted?2:1,o=this.inverted?1:2;for(let l=0;le)break;let c=this.ranges[l+s],f=a+c;if(e<=f&&l==i*3)return!0;r+=this.ranges[l+o]-c}return!1}forEach(e){let t=this.inverted?2:1,r=this.inverted?1:2;for(let i=0,s=0;i=0;t--){let i=e.getMirror(t);this.appendMap(e._maps[t].invert(),i!=null&&i>t?r-i-1:void 0)}}invert(){let e=new n;return e.appendMappingInverted(this),e}map(e,t=1){if(this.mirror)return this._map(e,t,!0);for(let r=this.from;rs&&a!o.isAtom||!l.type.allowsMarkType(this.mark.type)?o:o.mark(this.mark.addToSet(o.marks)),i),t.openStart,t.openEnd);return $.fromReplace(e,this.from,this.to,s)}invert(){return new me(this.from,this.to,this.mark)}map(e){let t=e.mapResult(this.from,1),r=e.mapResult(this.to,-1);return t.deleted&&r.deleted||t.pos>=r.pos?null:new n(t.pos,r.pos,this.mark)}merge(e){return e instanceof n&&e.mark.eq(this.mark)&&this.from<=e.to&&this.to>=e.from?new n(Math.min(this.from,e.from),Math.max(this.to,e.to),this.mark):null}toJSON(){return{stepType:"addMark",mark:this.mark.toJSON(),from:this.from,to:this.to}}static fromJSON(e,t){if(typeof t.from!="number"||typeof t.to!="number")throw new RangeError("Invalid input for AddMarkStep.fromJSON");return new n(t.from,t.to,e.markFromJSON(t.mark))}};F.jsonID("addMark",bt);var me=class n extends F{constructor(e,t,r){super(),this.from=e,this.to=t,this.mark=r}apply(e){let t=e.slice(this.from,this.to),r=new x(Wn(t.content,i=>i.mark(this.mark.removeFromSet(i.marks)),e),t.openStart,t.openEnd);return $.fromReplace(e,this.from,this.to,r)}invert(){return new bt(this.from,this.to,this.mark)}map(e){let t=e.mapResult(this.from,1),r=e.mapResult(this.to,-1);return t.deleted&&r.deleted||t.pos>=r.pos?null:new n(t.pos,r.pos,this.mark)}merge(e){return e instanceof n&&e.mark.eq(this.mark)&&this.from<=e.to&&this.to>=e.from?new n(Math.min(this.from,e.from),Math.max(this.to,e.to),this.mark):null}toJSON(){return{stepType:"removeMark",mark:this.mark.toJSON(),from:this.from,to:this.to}}static fromJSON(e,t){if(typeof t.from!="number"||typeof t.to!="number")throw new RangeError("Invalid input for RemoveMarkStep.fromJSON");return new n(t.from,t.to,e.markFromJSON(t.mark))}};F.jsonID("removeMark",me);var xt=class n extends F{constructor(e,t){super(),this.pos=e,this.mark=t}apply(e){let t=e.nodeAt(this.pos);if(!t)return $.fail("No node at mark step's position");let r=t.type.create(t.attrs,null,this.mark.addToSet(t.marks));return $.fromReplace(e,this.pos,this.pos+1,new x(b.from(r),0,t.isLeaf?0:1))}invert(e){let t=e.nodeAt(this.pos);if(t){let r=this.mark.addToSet(t.marks);if(r.length==t.marks.length){for(let i=0;ir.pos?null:new n(t.pos,r.pos,i,s,this.slice,this.insert,this.structure)}toJSON(){let e={stepType:"replaceAround",from:this.from,to:this.to,gapFrom:this.gapFrom,gapTo:this.gapTo,insert:this.insert};return this.slice.size&&(e.slice=this.slice.toJSON()),this.structure&&(e.structure=!0),e}static fromJSON(e,t){if(typeof t.from!="number"||typeof t.to!="number"||typeof t.gapFrom!="number"||typeof t.gapTo!="number"||typeof t.insert!="number")throw new RangeError("Invalid input for ReplaceAroundStep.fromJSON");return new n(t.from,t.to,t.gapFrom,t.gapTo,x.fromJSON(e,t.slice),t.insert,!!t.structure)}};F.jsonID("replaceAround",z);function Vn(n,e,t){let r=n.resolve(e),i=t-e,s=r.depth;for(;i>0&&s>0&&r.indexAfter(s)==r.node(s).childCount;)s--,i--;if(i>0){let o=r.node(s).maybeChild(r.indexAfter(s));for(;i>0;){if(!o||o.isLeaf)return!0;o=o.firstChild,i--}}return!1}function $l(n,e,t,r){let i=[],s=[],o,l;n.doc.nodesBetween(e,t,(a,c,f)=>{if(!a.isInline)return;let d=a.marks;if(!r.isInSet(d)&&f.type.allowsMarkType(r.type)){let u=Math.max(c,e),h=Math.min(c+a.nodeSize,t),p=r.addToSet(d);for(let m=0;mn.step(a)),s.forEach(a=>n.step(a))}function Wl(n,e,t,r){let i=[],s=0;n.doc.nodesBetween(e,t,(o,l)=>{if(!o.isInline)return;s++;let a=null;if(r instanceof ht){let c=o.marks,f;for(;f=r.isInSet(c);)(a||(a=[])).push(f),c=f.removeFromSet(c)}else r?r.isInSet(o.marks)&&(a=[r]):a=o.marks;if(a&&a.length){let c=Math.min(l+o.nodeSize,t);for(let f=0;fn.step(new me(o.from,o.to,o.style)))}function Hn(n,e,t,r=t.contentMatch,i=!0){let s=n.doc.nodeAt(e),o=[],l=e+1;for(let a=0;a=0;a--)n.step(o[a])}function Hl(n,e,t){return(e==0||n.canReplace(e,n.childCount))&&(t==n.childCount||n.canReplace(0,t))}function ge(n){let t=n.parent.content.cutByIndex(n.startIndex,n.endIndex);for(let r=n.depth,i=0,s=0;;--r){let o=n.$from.node(r),l=n.$from.index(r)+i,a=n.$to.indexAfter(r)-s;if(rt;p--)m||r.index(p)>0?(m=!0,f=b.from(r.node(p).copy(f)),d++):a--;let u=b.empty,h=0;for(let p=s,m=!1;p>t;p--)m||i.after(p+1)=0;o--){if(r.size){let l=t[o].type.contentMatch.matchFragment(r);if(!l||!l.validEnd)throw new RangeError("Wrapper type given to Transform.wrap does not form valid content of its parent wrapper")}r=b.from(t[o].type.create(t[o].attrs,r))}let i=e.start,s=e.end;n.step(new z(i,s,i,s,new x(r,0,0),t.length,!0))}function ql(n,e,t,r,i){if(!r.isTextblock)throw new RangeError("Type given to setBlockType should be a textblock");let s=n.steps.length;n.doc.nodesBetween(e,t,(o,l)=>{let a=typeof i=="function"?i(o):i;if(o.isTextblock&&!o.hasMarkup(r,a)&&Ul(n.doc,n.mapping.slice(s).map(l),r)){let c=null;if(r.schema.linebreakReplacement){let h=r.whitespace=="pre",p=!!r.contentMatch.matchType(r.schema.linebreakReplacement);h&&!p?c=!1:!h&&p&&(c=!0)}c===!1&&Vi(n,o,l,s),Hn(n,n.mapping.slice(s).map(l,1),r,void 0,c===null);let f=n.mapping.slice(s),d=f.map(l,1),u=f.map(l+o.nodeSize,1);return n.step(new z(d,u,d+1,u-1,new x(b.from(r.create(a,null,o.marks)),0,0),1,!0)),c===!0&&Li(n,o,l,s),!1}})}function Li(n,e,t,r){e.forEach((i,s)=>{if(i.isText){let o,l=/\r?\n|\r/g;for(;o=l.exec(i.text);){let a=n.mapping.slice(r).map(t+1+s+o.index);n.replaceWith(a,a+1,e.type.schema.linebreakReplacement.create())}}})}function Vi(n,e,t,r){e.forEach((i,s)=>{if(i.type==i.type.schema.linebreakReplacement){let o=n.mapping.slice(r).map(t+1+s);n.replaceWith(o,o+1,e.type.schema.text(`
+`))}})}function Ul(n,e,t){let r=n.resolve(e),i=r.index();return r.parent.canReplaceWith(i,i+1,t)}function Yl(n,e,t,r,i){let s=n.doc.nodeAt(e);if(!s)throw new RangeError("No node at given position");t||(t=s.type);let o=t.create(r,null,i||s.marks);if(s.isLeaf)return n.replaceWith(e,e+s.nodeSize,o);if(!t.validContent(s.content))throw new RangeError("Invalid content for node type "+t.name);n.step(new z(e,e+s.nodeSize,e+1,e+s.nodeSize-1,new x(b.from(o),0,0),1,!0))}function te(n,e,t=1,r){let i=n.resolve(e),s=i.depth-t,o=r&&r[r.length-1]||i.parent;if(s<0||i.parent.type.spec.isolating||!i.parent.canReplace(i.index(),i.parent.childCount)||!o.type.validContent(i.parent.content.cutByIndex(i.index(),i.parent.childCount)))return!1;for(let c=i.depth-1,f=t-2;c>s;c--,f--){let d=i.node(c),u=i.index(c);if(d.type.spec.isolating)return!1;let h=d.content.cutByIndex(u,d.childCount),p=r&&r[f+1];p&&(h=h.replaceChild(0,p.type.create(p.attrs)));let m=r&&r[f]||d;if(!d.canReplace(u+1,d.childCount)||!m.type.validContent(h))return!1}let l=i.indexAfter(s),a=r&&r[0];return i.node(s).canReplaceWith(l,l,a?a.type:i.node(s+1).type)}function Gl(n,e,t=1,r){let i=n.doc.resolve(e),s=b.empty,o=b.empty;for(let l=i.depth,a=i.depth-t,c=t-1;l>a;l--,c--){s=b.from(i.node(l).copy(s));let f=r&&r[c];o=b.from(f?f.type.create(f.attrs,o):i.node(l).copy(o))}n.step(new W(e,e,new x(s.append(o),t,t),!0))}function re(n,e){let t=n.resolve(e),r=t.index();return $i(t.nodeBefore,t.nodeAfter)&&t.parent.canReplace(r,r+1)}function Xl(n,e){e.content.size||n.type.compatibleContent(e.type);let t=n.contentMatchAt(n.childCount),{linebreakReplacement:r}=n.type.schema;for(let i=0;i0?(s=r.node(i+1),l++,o=r.node(i).maybeChild(l)):(s=r.node(i).maybeChild(l-1),o=r.node(i+1)),s&&!s.isTextblock&&$i(s,o)&&r.node(i).canReplace(l,l+1))return e;if(i==0)break;e=t<0?r.before(i):r.after(i)}}function Ql(n,e,t){let r=null,{linebreakReplacement:i}=n.doc.type.schema,s=n.doc.resolve(e-t),o=s.node().type;if(i&&o.inlineContent){let f=o.whitespace=="pre",d=!!o.contentMatch.matchType(i);f&&!d?r=!1:!f&&d&&(r=!0)}let l=n.steps.length;if(r===!1){let f=n.doc.resolve(e+t);Vi(n,f.node(),f.before(),l)}o.inlineContent&&Hn(n,e+t-1,o,s.node().contentMatchAt(s.index()),r==null);let a=n.mapping.slice(l),c=a.map(e-t);if(n.step(new W(c,a.map(e+t,-1),x.empty,!0)),r===!0){let f=n.doc.resolve(c);Li(n,f.node(),f.before(),n.steps.length)}return n}function Zl(n,e,t){let r=n.resolve(e);if(r.parent.canReplaceWith(r.index(),r.index(),t))return e;if(r.parentOffset==0)for(let i=r.depth-1;i>=0;i--){let s=r.index(i);if(r.node(i).canReplaceWith(s,s,t))return r.before(i+1);if(s>0)return null}if(r.parentOffset==r.parent.content.size)for(let i=r.depth-1;i>=0;i--){let s=r.indexAfter(i);if(r.node(i).canReplaceWith(s,s,t))return r.after(i+1);if(s=0;o--){let l=o==r.depth?0:r.pos<=(r.start(o+1)+r.end(o+1))/2?-1:1,a=r.index(o)+(l>0?1:0),c=r.node(o),f=!1;if(s==1)f=c.canReplace(a,a,i);else{let d=c.contentMatchAt(a).findWrapping(i.firstChild.type);f=d&&c.canReplaceWith(a,a,d[0])}if(f)return l==0?r.pos:l<0?r.before(o+1):r.after(o+1)}return null}function St(n,e,t=e,r=x.empty){if(e==t&&!r.size)return null;let i=n.resolve(e),s=n.resolve(t);return Hi(i,s,r)?new W(e,t,r):new $n(i,s,r).fit()}function Hi(n,e,t){return!t.openStart&&!t.openEnd&&n.start()==e.start()&&n.parent.canReplace(n.index(),e.index(),t.content)}var $n=class{constructor(e,t,r){this.$from=e,this.$to=t,this.unplaced=r,this.frontier=[],this.placed=b.empty;for(let i=0;i<=e.depth;i++){let s=e.node(i);this.frontier.push({type:s.type,match:s.contentMatchAt(e.indexAfter(i))})}for(let i=e.depth;i>0;i--)this.placed=b.from(e.node(i).copy(this.placed))}get depth(){return this.frontier.length-1}fit(){for(;this.unplaced.size;){let c=this.findFittable();c?this.placeNodes(c):this.openMore()||this.dropNode()}let e=this.mustMoveInline(),t=this.placed.size-this.depth-this.$from.depth,r=this.$from,i=this.close(e<0?this.$to:r.doc.resolve(e));if(!i)return null;let s=this.placed,o=r.depth,l=i.depth;for(;o&&l&&s.childCount==1;)s=s.firstChild.content,o--,l--;let a=new x(s,o,l);return e>-1?new z(r.pos,e,this.$to.pos,this.$to.end(),a,t):a.size||r.pos!=this.$to.pos?new W(r.pos,i.pos,a):null}findFittable(){let e=this.unplaced.openStart;for(let t=this.unplaced.content,r=0,i=this.unplaced.openEnd;r1&&(i=0),s.type.spec.isolating&&i<=r){e=r;break}t=s.content}for(let t=1;t<=2;t++)for(let r=t==1?e:this.unplaced.openStart;r>=0;r--){let i,s=null;r?(s=Bn(this.unplaced.content,r-1).firstChild,i=s.content):i=this.unplaced.content;let o=i.firstChild;for(let l=this.depth;l>=0;l--){let{type:a,match:c}=this.frontier[l],f,d=null;if(t==1&&(o?c.matchType(o.type)||(d=c.fillBefore(b.from(o),!1)):s&&a.compatibleContent(s.type)))return{sliceDepth:r,frontierDepth:l,parent:s,inject:d};if(t==2&&o&&(f=c.findWrapping(o.type)))return{sliceDepth:r,frontierDepth:l,parent:s,wrap:f};if(s&&c.matchType(s.type))break}}}openMore(){let{content:e,openStart:t,openEnd:r}=this.unplaced,i=Bn(e,t);return!i.childCount||i.firstChild.isLeaf?!1:(this.unplaced=new x(e,t+1,Math.max(r,i.size+t>=e.size-r?t+1:0)),!0)}dropNode(){let{content:e,openStart:t,openEnd:r}=this.unplaced,i=Bn(e,t);if(i.childCount<=1&&t>0){let s=e.size-t<=t+i.size;this.unplaced=new x(mt(e,t-1,1),t-1,s?t-1:r)}else this.unplaced=new x(mt(e,t,1),t,r)}placeNodes({sliceDepth:e,frontierDepth:t,parent:r,inject:i,wrap:s}){for(;this.depth>t;)this.closeFrontierNode();if(s)for(let m=0;m1||a==0||m.content.size)&&(d=g,f.push(Ji(m.mark(u.allowedMarks(m.marks)),c==1?a:0,c==l.childCount?h:-1)))}let p=c==l.childCount;p||(h=-1),this.placed=gt(this.placed,t,b.from(f)),this.frontier[t].match=d,p&&h<0&&r&&r.type==this.frontier[this.depth].type&&this.frontier.length>1&&this.closeFrontierNode();for(let m=0,g=l;m1&&i==this.$to.end(--r);)++i;return i}findCloseLevel(e){e:for(let t=Math.min(this.depth,e.depth);t>=0;t--){let{match:r,type:i}=this.frontier[t],s=t=0;l--){let{match:a,type:c}=this.frontier[l],f=Fn(e,l,c,a,!0);if(!f||f.childCount)continue e}return{depth:t,fit:o,move:s?e.doc.resolve(e.after(t+1)):e}}}}close(e){let t=this.findCloseLevel(e);if(!t)return null;for(;this.depth>t.depth;)this.closeFrontierNode();t.fit.childCount&&(this.placed=gt(this.placed,t.depth,t.fit)),e=t.move;for(let r=t.depth+1;r<=e.depth;r++){let i=e.node(r),s=i.type.contentMatch.fillBefore(i.content,!0,e.index(r));this.openFrontierNode(i.type,i.attrs,s)}return e}openFrontierNode(e,t=null,r){let i=this.frontier[this.depth];i.match=i.match.matchType(e),this.placed=gt(this.placed,this.depth,b.from(e.create(t,r))),this.frontier.push({type:e,match:e.contentMatch})}closeFrontierNode(){let t=this.frontier.pop().match.fillBefore(b.empty,!0);t.childCount&&(this.placed=gt(this.placed,this.frontier.length,t))}};function mt(n,e,t){return e==0?n.cutByIndex(t,n.childCount):n.replaceChild(0,n.firstChild.copy(mt(n.firstChild.content,e-1,t)))}function gt(n,e,t){return e==0?n.append(t):n.replaceChild(n.childCount-1,n.lastChild.copy(gt(n.lastChild.content,e-1,t)))}function Bn(n,e){for(let t=0;t1&&(r=r.replaceChild(0,Ji(r.firstChild,e-1,r.childCount==1?t-1:0))),e>0&&(r=n.type.contentMatch.fillBefore(r).append(r),t<=0&&(r=r.append(n.type.contentMatch.matchFragment(r).fillBefore(b.empty,!0)))),n.copy(r)}function Fn(n,e,t,r,i){let s=n.node(e),o=i?n.indexAfter(e):n.index(e);if(o==s.childCount&&!t.compatibleContent(s.type))return null;let l=r.fillBefore(s.content,!0,o);return l&&!ea(t,s.content,o)?l:null}function ea(n,e,t){for(let r=t;r0;u--,h--){let p=i.node(u).type.spec;if(p.defining||p.definingAsContext||p.isolating)break;o.indexOf(u)>-1?l=u:i.before(u)==h&&o.splice(1,0,-u)}let a=o.indexOf(l),c=[],f=r.openStart;for(let u=r.content,h=0;;h++){let p=u.firstChild;if(c.push(p),h==r.openStart)break;u=p.content}for(let u=f-1;u>=0;u--){let h=c[u],p=ta(h.type);if(p&&!h.sameMarkup(i.node(Math.abs(l)-1)))f=u;else if(p||!h.type.isTextblock)break}for(let u=r.openStart;u>=0;u--){let h=(u+f+1)%(r.openStart+1),p=c[h];if(p)for(let m=0;m=0&&(n.replace(e,t,r),!(n.steps.length>d));u--){let h=o[u];h<0||(e=i.before(h),t=s.after(h))}}function ji(n,e,t,r,i){if(er){let s=i.contentMatchAt(0),o=s.fillBefore(n).append(n);n=o.append(s.matchFragment(o).fillBefore(b.empty,!0))}return n}function ra(n,e,t,r){if(!r.isInline&&e==t&&n.doc.resolve(e).parent.content.size){let i=Zl(n.doc,e,r.type);i!=null&&(e=t=i)}n.replaceRange(e,t,new x(b.from(r),0,0))}function ia(n,e,t){let r=n.doc.resolve(e),i=n.doc.resolve(t);if(r.parent.isTextblock&&i.parent.isTextblock&&r.start()!=i.start()&&r.parentOffset==0&&i.parentOffset==0){let o=r.sharedDepth(t),l=!1;for(let a=r.depth;a>o;a--)r.node(a).type.spec.isolating&&(l=!0);for(let a=i.depth;a>o;a--)i.node(a).type.spec.isolating&&(l=!0);if(!l){for(let a=r.depth;a>0&&e==r.start(a);a--)e=r.before(a);for(let a=i.depth;a>0&&t==i.start(a);a--)t=i.before(a);r=n.doc.resolve(e),i=n.doc.resolve(t)}}let s=_i(r,i);for(let o=0;o0&&(a||r.node(l-1).canReplace(r.index(l-1),i.indexAfter(l-1))))return n.delete(r.before(l),i.after(l))}for(let o=1;o<=r.depth&&o<=i.depth;o++)if(e-r.start(o)==r.depth-o&&t>r.end(o)&&i.end(o)-t!=i.depth-o&&r.start(o-1)==i.start(o-1)&&r.node(o-1).canReplace(r.index(o-1),i.index(o-1)))return n.delete(r.before(o),t);n.delete(e,t)}function _i(n,e){let t=[],r=Math.min(n.depth,e.depth);for(let i=r;i>=0;i--){let s=n.start(i);if(se.pos+(e.depth-i)||n.node(i).type.spec.isolating||e.node(i).type.spec.isolating)break;(s==e.start(i)||i==n.depth&&i==e.depth&&n.parent.inlineContent&&e.parent.inlineContent&&i&&e.start(i-1)==s-1)&&t.push(i)}return t}var qt=class n extends F{constructor(e,t,r){super(),this.pos=e,this.attr=t,this.value=r}apply(e){let t=e.nodeAt(this.pos);if(!t)return $.fail("No node at attribute step's position");let r=Object.create(null);for(let s in t.attrs)r[s]=t.attrs[s];r[this.attr]=this.value;let i=t.type.create(r,null,t.marks);return $.fromReplace(e,this.pos,this.pos+1,new x(b.from(i),0,t.isLeaf?0:1))}getMap(){return pe.empty}invert(e){return new n(this.pos,this.attr,e.nodeAt(this.pos).attrs[this.attr])}map(e){let t=e.mapResult(this.pos,1);return t.deletedAfter?null:new n(t.pos,this.attr,this.value)}toJSON(){return{stepType:"attr",pos:this.pos,attr:this.attr,value:this.value}}static fromJSON(e,t){if(typeof t.pos!="number"||typeof t.attr!="string")throw new RangeError("Invalid input for AttrStep.fromJSON");return new n(t.pos,t.attr,t.value)}};F.jsonID("attr",qt);var Ut=class n extends F{constructor(e,t){super(),this.attr=e,this.value=t}apply(e){let t=Object.create(null);for(let i in e.attrs)t[i]=e.attrs[i];t[this.attr]=this.value;let r=e.type.create(t,e.content,e.marks);return $.ok(r)}getMap(){return pe.empty}invert(e){return new n(this.attr,e.attrs[this.attr])}map(e){return this}toJSON(){return{stepType:"docAttr",attr:this.attr,value:this.value}}static fromJSON(e,t){if(typeof t.attr!="string")throw new RangeError("Invalid input for DocAttrStep.fromJSON");return new n(t.attr,t.value)}};F.jsonID("docAttr",Ut);var Qe=class extends Error{};Qe=function n(e){let t=Error.call(this,e);return t.__proto__=n.prototype,t};Qe.prototype=Object.create(Error.prototype);Qe.prototype.constructor=Qe;Qe.prototype.name="TransformError";var Ze=class{constructor(e){this.doc=e,this.steps=[],this.docs=[],this.mapping=new Ln}get before(){return this.docs.length?this.docs[0]:this.doc}step(e){let t=this.maybeStep(e);if(t.failed)throw new Qe(t.failed);return this}maybeStep(e){let t=e.apply(this.doc);return t.failed||this.addStep(e,t.doc),t}get docChanged(){return this.steps.length>0}changedRange(){let e=1e9,t=-1e9;for(let r=0;r{e=Math.min(e,l),t=Math.max(t,a)})}return e==1e9?null:{from:e,to:t}}addStep(e,t){this.docs.push(this.doc),this.steps.push(e),this.mapping.appendMap(e.getMap()),this.doc=t}replace(e,t=e,r=x.empty){let i=St(this.doc,e,t,r);return i&&this.step(i),this}replaceWith(e,t,r){return this.replace(e,t,new x(b.from(r),0,0))}delete(e,t){return this.replace(e,t,x.empty)}insert(e,t){return this.replaceWith(e,e,t)}replaceRange(e,t,r){return na(this,e,t,r),this}replaceRangeWith(e,t,r){return ra(this,e,t,r),this}deleteRange(e,t){return ia(this,e,t),this}lift(e,t){return Jl(this,e,t),this}join(e,t=1){return Ql(this,e,t),this}wrap(e,t){return Kl(this,e,t),this}setBlockType(e,t=e,r,i=null){return ql(this,e,t,r,i),this}setNodeMarkup(e,t,r=null,i){return Yl(this,e,t,r,i),this}setNodeAttribute(e,t,r){return this.step(new qt(e,t,r)),this}setDocAttribute(e,t){return this.step(new Ut(e,t)),this}addNodeMark(e,t){return this.step(new xt(e,t)),this}removeNodeMark(e,t){let r=this.doc.nodeAt(e);if(!r)throw new RangeError("No node at position "+e);if(t instanceof A)t.isInSet(r.marks)&&this.step(new Xe(e,t));else{let i=r.marks,s,o=[];for(;s=t.isInSet(i);)o.push(new Xe(e,s)),i=s.removeFromSet(i);for(let l=o.length-1;l>=0;l--)this.step(o[l])}return this}split(e,t=1,r){return Gl(this,e,t,r),this}addMark(e,t,r){return $l(this,e,t,r),this}removeMark(e,t,r){return Wl(this,e,t,r),this}clearIncompatible(e,t,r){return Hn(this,e,t,r),this}};var Jn=Object.create(null),E=class{constructor(e,t,r){this.$anchor=e,this.$head=t,this.ranges=r||[new Gt(e.min(t),e.max(t))]}get anchor(){return this.$anchor.pos}get head(){return this.$head.pos}get from(){return this.$from.pos}get to(){return this.$to.pos}get $from(){return this.ranges[0].$from}get $to(){return this.ranges[0].$to}get empty(){let e=this.ranges;for(let t=0;t=0;s--){let o=t<0?tt(e.node(0),e.node(s),e.before(s+1),e.index(s),t,r):tt(e.node(0),e.node(s),e.after(s+1),e.index(s)+1,t,r);if(o)return o}return null}static near(e,t=1){return this.findFrom(e,t)||this.findFrom(e,-t)||new U(e.node(0))}static atStart(e){return tt(e,e,0,0,1)||new U(e)}static atEnd(e){return tt(e,e,e.content.size,e.childCount,-1)||new U(e)}static fromJSON(e,t){if(!t||!t.type)throw new RangeError("Invalid input for Selection.fromJSON");let r=Jn[t.type];if(!r)throw new RangeError(`No selection type ${t.type} defined`);return r.fromJSON(e,t)}static jsonID(e,t){if(e in Jn)throw new RangeError("Duplicate use of selection JSON ID "+e);return Jn[e]=t,t.prototype.jsonID=e,t}getBookmark(){return T.between(this.$anchor,this.$head).getBookmark()}};E.prototype.visible=!0;var Gt=class{constructor(e,t){this.$from=e,this.$to=t}},Ki=!1;function qi(n){!Ki&&!n.parent.inlineContent&&(Ki=!0,console.warn("TextSelection endpoint not pointing into a node with inline content ("+n.parent.type.name+")"))}var T=class n extends E{constructor(e,t=e){qi(e),qi(t),super(e,t)}get $cursor(){return this.$anchor.pos==this.$head.pos?this.$head:null}map(e,t){let r=e.resolve(t.map(this.head));if(!r.parent.inlineContent)return E.near(r);let i=e.resolve(t.map(this.anchor));return new n(i.parent.inlineContent?i:r,r)}replace(e,t=x.empty){if(super.replace(e,t),t==x.empty){let r=this.$from.marksAcross(this.$to);r&&e.ensureMarks(r)}}eq(e){return e instanceof n&&e.anchor==this.anchor&&e.head==this.head}getBookmark(){return new Xt(this.anchor,this.head)}toJSON(){return{type:"text",anchor:this.anchor,head:this.head}}static fromJSON(e,t){if(typeof t.anchor!="number"||typeof t.head!="number")throw new RangeError("Invalid input for TextSelection.fromJSON");return new n(e.resolve(t.anchor),e.resolve(t.head))}static create(e,t,r=t){let i=e.resolve(t);return new this(i,r==t?i:e.resolve(r))}static between(e,t,r){let i=e.pos-t.pos;if((!r||i)&&(r=i>=0?1:-1),!t.parent.inlineContent){let s=E.findFrom(t,r,!0)||E.findFrom(t,-r,!0);if(s)t=s.$head;else return E.near(t,r)}return e.parent.inlineContent||(i==0?e=t:(e=(E.findFrom(e,-r,!0)||E.findFrom(e,r,!0)).$anchor,e.pos0?0:1);i>0?o=0;o+=i){let l=e.child(o);if(l.isAtom){if(!s&&C.isSelectable(l))return C.create(n,t-(i<0?l.nodeSize:0))}else{let a=tt(n,l,t+i,i<0?l.childCount:0,i,s);if(a)return a}t+=l.nodeSize*i}return null}function Ui(n,e,t){let r=n.steps.length-1;if(r{o==null&&(o=f)}),n.setSelection(E.near(n.doc.resolve(o),t))}var Yi=1,Yt=2,Gi=4,Kn=class extends Ze{constructor(e){super(e.doc),this.curSelectionFor=0,this.updated=0,this.meta=Object.create(null),this.time=Date.now(),this.curSelection=e.selection,this.storedMarks=e.storedMarks}get selection(){return this.curSelectionFor0}setStoredMarks(e){return this.storedMarks=e,this.updated|=Yt,this}ensureMarks(e){return A.sameSet(this.storedMarks||this.selection.$from.marks(),e)||this.setStoredMarks(e),this}addStoredMark(e){return this.ensureMarks(e.addToSet(this.storedMarks||this.selection.$head.marks()))}removeStoredMark(e){return this.ensureMarks(e.removeFromSet(this.storedMarks||this.selection.$head.marks()))}get storedMarksSet(){return(this.updated&Yt)>0}addStep(e,t){super.addStep(e,t),this.updated=this.updated&~Yt,this.storedMarks=null}setTime(e){return this.time=e,this}replaceSelection(e){return this.selection.replace(this,e),this}replaceSelectionWith(e,t=!0){let r=this.selection;return t&&(e=e.mark(this.storedMarks||(r.empty?r.$from.marks():r.$from.marksAcross(r.$to)||A.none))),r.replaceWith(this,e),this}deleteSelection(){return this.selection.replace(this),this}insertText(e,t,r){let i=this.doc.type.schema;if(t==null)return e?this.replaceSelectionWith(i.text(e),!0):this.deleteSelection();{if(r==null&&(r=t),!e)return this.deleteRange(t,r);let s=this.storedMarks;if(!s){let o=this.doc.resolve(t);s=r==t?o.marks():o.marksAcross(this.doc.resolve(r))}return this.replaceRangeWith(t,r,i.text(e,s)),!this.selection.empty&&this.selection.to==t+e.length&&this.setSelection(E.near(this.selection.$to)),this}}setMeta(e,t){return this.meta[typeof e=="string"?e:e.key]=t,this}getMeta(e){return this.meta[typeof e=="string"?e:e.key]}get isGeneric(){for(let e in this.meta)return!1;return!0}scrollIntoView(){return this.updated|=Gi,this}get scrolledIntoView(){return(this.updated&Gi)>0}};function Xi(n,e){return!e||!n?n:n.bind(e)}var Le=class{constructor(e,t,r){this.name=e,this.init=Xi(t.init,r),this.apply=Xi(t.apply,r)}},oa=[new Le("doc",{init(n){return n.doc||n.schema.topNodeType.createAndFill()},apply(n){return n.doc}}),new Le("selection",{init(n,e){return n.selection||E.atStart(e.doc)},apply(n){return n.selection}}),new Le("storedMarks",{init(n){return n.storedMarks||null},apply(n,e,t,r){return r.selection.$cursor?n.storedMarks:null}}),new Le("scrollToSelection",{init(){return 0},apply(n,e){return n.scrolledIntoView?e+1:e}})],kt=class{constructor(e,t){this.schema=e,this.plugins=[],this.pluginsByKey=Object.create(null),this.fields=oa.slice(),t&&t.forEach(r=>{if(this.pluginsByKey[r.key])throw new RangeError("Adding different instances of a keyed plugin ("+r.key+")");this.plugins.push(r),this.pluginsByKey[r.key]=r,r.spec.state&&this.fields.push(new Le(r.key,r.spec.state,r))})}},Mt=class n{constructor(e){this.config=e}get schema(){return this.config.schema}get plugins(){return this.config.plugins}apply(e){return this.applyTransaction(e).state}filterTransaction(e,t=-1){for(let r=0;rr.toJSON())),e&&typeof e=="object")for(let r in e){if(r=="doc"||r=="selection")throw new RangeError("The JSON fields `doc` and `selection` are reserved");let i=e[r],s=i.spec.state;s&&s.toJSON&&(t[r]=s.toJSON.call(i,this[i.key]))}return t}static fromJSON(e,t,r){if(!t)throw new RangeError("Invalid input for EditorState.fromJSON");if(!e.schema)throw new RangeError("Required config field 'schema' missing");let i=new kt(e.schema,e.plugins),s=new n(i);return i.fields.forEach(o=>{if(o.name=="doc")s.doc=Q.fromJSON(e.schema,t.doc);else if(o.name=="selection")s.selection=E.fromJSON(s.doc,t.selection);else if(o.name=="storedMarks")t.storedMarks&&(s.storedMarks=t.storedMarks.map(e.schema.markFromJSON));else{if(r)for(let l in r){let a=r[l],c=a.spec.state;if(a.key==o.name&&c&&c.fromJSON&&Object.prototype.hasOwnProperty.call(t,l)){s[o.name]=c.fromJSON.call(a,e,t[l],s);return}}s[o.name]=o.init(e,s)}}),s}};function Qi(n,e,t){for(let r in n){let i=n[r];i instanceof Function?i=i.bind(e):r=="handleDOMEvents"&&(i=Qi(i,e,{})),t[r]=i}return t}var I=class{constructor(e){this.spec=e,this.props={},e.props&&Qi(e.props,this,this.props),this.key=e.key?e.key.key:Zi("plugin")}getState(e){return e[this.key]}},jn=Object.create(null);function Zi(n){return n in jn?n+"$"+ ++jn[n]:(jn[n]=0,n+"$")}var L=class{constructor(e="key"){this.key=Zi(e)}get(e){return e.config.pluginsByKey[this.key]}getState(e){return e[this.key]}};var ts=(n,e)=>n.selection.empty?!1:(e&&e(n.tr.deleteSelection().scrollIntoView()),!0);function ns(n,e){let{$cursor:t}=n.selection;return!t||(e?!e.endOfTextblock("backward",n):t.parentOffset>0)?null:t}var Un=(n,e,t)=>{let r=ns(n,t);if(!r)return!1;let i=Gn(r);if(!i){let o=r.blockRange(),l=o&&ge(o);return l==null?!1:(e&&e(n.tr.lift(o,l).scrollIntoView()),!0)}let s=i.nodeBefore;if(us(n,i,e,-1))return!0;if(r.parent.content.size==0&&(nt(s,"end")||C.isSelectable(s)))for(let o=r.depth;;o--){let l=St(n.doc,r.before(o),r.after(o),x.empty);if(l&&l.slice.size1)break}return s.isAtom&&i.depth==r.depth-1?(e&&e(n.tr.delete(i.pos-s.nodeSize,i.pos).scrollIntoView()),!0):!1},rs=(n,e,t)=>{let r=ns(n,t);if(!r)return!1;let i=Gn(r);return i?ss(n,i,e):!1},is=(n,e,t)=>{let r=ls(n,t);if(!r)return!1;let i=Zn(r);return i?ss(n,i,e):!1};function ss(n,e,t){let r=e.nodeBefore,i=r,s=e.pos-1;for(;!i.isTextblock;s--){if(i.type.spec.isolating)return!1;let f=i.lastChild;if(!f)return!1;i=f}let o=e.nodeAfter,l=o,a=e.pos+1;for(;!l.isTextblock;a++){if(l.type.spec.isolating)return!1;let f=l.firstChild;if(!f)return!1;l=f}let c=St(n.doc,s,a,x.empty);if(!c||c.from!=s||c instanceof W&&c.slice.size>=a-s)return!1;if(t){let f=n.tr.step(c);f.setSelection(T.create(f.doc,s)),t(f.scrollIntoView())}return!0}function nt(n,e,t=!1){for(let r=n;r;r=e=="start"?r.firstChild:r.lastChild){if(r.isTextblock)return!0;if(t&&r.childCount!=1)return!1}return!1}var Yn=(n,e,t)=>{let{$head:r,empty:i}=n.selection,s=r;if(!i)return!1;if(r.parent.isTextblock){if(t?!t.endOfTextblock("backward",n):r.parentOffset>0)return!1;s=Gn(r)}let o=s&&s.nodeBefore;return!o||!C.isSelectable(o)?!1:(e&&e(n.tr.setSelection(C.create(n.doc,s.pos-o.nodeSize)).scrollIntoView()),!0)};function Gn(n){if(!n.parent.type.spec.isolating)for(let e=n.depth-1;e>=0;e--){if(n.index(e)>0)return n.doc.resolve(n.before(e+1));if(n.node(e).type.spec.isolating)break}return null}function ls(n,e){let{$cursor:t}=n.selection;return!t||(e?!e.endOfTextblock("forward",n):t.parentOffset{let r=ls(n,t);if(!r)return!1;let i=Zn(r);if(!i)return!1;let s=i.nodeAfter;if(us(n,i,e,1))return!0;if(r.parent.content.size==0&&(nt(s,"start")||C.isSelectable(s))){let o=St(n.doc,r.before(),r.after(),x.empty);if(o&&o.slice.size{let{$head:r,empty:i}=n.selection,s=r;if(!i)return!1;if(r.parent.isTextblock){if(t?!t.endOfTextblock("forward",n):r.parentOffset=0;e--){let t=n.node(e);if(n.index(e)+1{let t=n.selection,r=t instanceof C,i;if(r){if(t.node.isTextblock||!re(n.doc,t.from))return!1;i=t.from}else if(i=Fe(n.doc,t.from,-1),i==null)return!1;if(e){let s=n.tr.join(i);r&&s.setSelection(C.create(s.doc,i-n.doc.resolve(i).nodeBefore.nodeSize)),e(s.scrollIntoView())}return!0},cs=(n,e)=>{let t=n.selection,r;if(t instanceof C){if(t.node.isTextblock||!re(n.doc,t.to))return!1;r=t.to}else if(r=Fe(n.doc,t.to,1),r==null)return!1;return e&&e(n.tr.join(r).scrollIntoView()),!0},fs=(n,e)=>{let{$from:t,$to:r}=n.selection,i=t.blockRange(r),s=i&&ge(i);return s==null?!1:(e&&e(n.tr.lift(i,s).scrollIntoView()),!0)},er=(n,e)=>{let{$head:t,$anchor:r}=n.selection;return!t.parent.type.spec.code||!t.sameParent(r)?!1:(e&&e(n.tr.insertText(`
+`).scrollIntoView()),!0)};function tr(n){for(let e=0;e{let{$head:t,$anchor:r}=n.selection;if(!t.parent.type.spec.code||!t.sameParent(r))return!1;let i=t.node(-1),s=t.indexAfter(-1),o=tr(i.contentMatchAt(s));if(!o||!i.canReplaceWith(s,s,o))return!1;if(e){let l=t.after(),a=n.tr.replaceWith(l,l,o.createAndFill());a.setSelection(E.near(a.doc.resolve(l),1)),e(a.scrollIntoView())}return!0},rr=(n,e)=>{let t=n.selection,{$from:r,$to:i}=t;if(t instanceof U||r.parent.inlineContent||i.parent.inlineContent)return!1;let s=tr(i.parent.contentMatchAt(i.indexAfter()));if(!s||!s.isTextblock)return!1;if(e){let o=(!r.parentOffset&&i.index(){let{$cursor:t}=n.selection;if(!t||t.parent.content.size)return!1;if(t.depth>1&&t.after()!=t.end(-1)){let s=t.before();if(te(n.doc,s))return e&&e(n.tr.split(s).scrollIntoView()),!0}let r=t.blockRange(),i=r&&ge(r);return i==null?!1:(e&&e(n.tr.lift(r,i).scrollIntoView()),!0)};function la(n){return(e,t)=>{let{$from:r,$to:i}=e.selection;if(e.selection instanceof C&&e.selection.node.isBlock)return!r.parentOffset||!te(e.doc,r.pos)?!1:(t&&t(e.tr.split(r.pos).scrollIntoView()),!0);if(!r.depth)return!1;let s=[],o,l,a=!1,c=!1;for(let h=r.depth;;h--)if(r.node(h).isBlock){a=r.end(h)==r.pos+(r.depth-h),c=r.start(h)==r.pos-(r.depth-h),l=tr(r.node(h-1).contentMatchAt(r.indexAfter(h-1)));let m=n&&n(i.parent,a,r);s.unshift(m||(a&&l?{type:l}:null)),o=h;break}else{if(h==1)return!1;s.unshift(null)}let f=e.tr;(e.selection instanceof T||e.selection instanceof U)&&f.deleteSelection();let d=f.mapping.map(r.pos),u=te(f.doc,d,s.length,s);if(u||(s[0]=l?{type:l}:null,u=te(f.doc,d,s.length,s)),!u)return!1;if(f.split(d,s.length,s),!a&&c&&r.node(o).type!=l){let h=f.mapping.map(r.before(o)),p=f.doc.resolve(h);l&&r.node(o-1).canReplaceWith(p.index(),p.index()+1,l)&&f.setNodeMarkup(f.mapping.map(r.before(o)),l)}return t&&t(f.scrollIntoView()),!0}}var aa=la();var ds=(n,e)=>{let{$from:t,to:r}=n.selection,i,s=t.sharedDepth(r);return s==0?!1:(i=t.before(s),e&&e(n.tr.setSelection(C.create(n.doc,i))),!0)},ca=(n,e)=>(e&&e(n.tr.setSelection(new U(n.doc))),!0);function fa(n,e,t){let r=e.nodeBefore,i=e.nodeAfter,s=e.index();return!r||!i||!r.type.compatibleContent(i.type)?!1:!r.content.size&&e.parent.canReplace(s-1,s)?(t&&t(n.tr.delete(e.pos-r.nodeSize,e.pos).scrollIntoView()),!0):!e.parent.canReplace(s,s+1)||!(i.isTextblock||re(n.doc,e.pos))?!1:(t&&t(n.tr.join(e.pos).scrollIntoView()),!0)}function us(n,e,t,r){let i=e.nodeBefore,s=e.nodeAfter,o,l,a=i.type.spec.isolating||s.type.spec.isolating;if(!a&&fa(n,e,t))return!0;let c=!a&&e.parent.canReplace(e.index(),e.index()+1);if(c&&(o=(l=i.contentMatchAt(i.childCount)).findWrapping(s.type))&&l.matchType(o[0]||s.type).validEnd){if(t){let h=e.pos+s.nodeSize,p=b.empty;for(let y=o.length-1;y>=0;y--)p=b.from(o[y].create(null,p));p=b.from(i.copy(p));let m=n.tr.step(new z(e.pos-1,h,e.pos,h,new x(p,1,0),o.length,!0)),g=m.doc.resolve(h+2*o.length);g.nodeAfter&&g.nodeAfter.type==i.type&&re(m.doc,g.pos)&&m.join(g.pos),t(m.scrollIntoView())}return!0}let f=s.type.spec.isolating||r>0&&a?null:E.findFrom(e,1),d=f&&f.$from.blockRange(f.$to),u=d&&ge(d);if(u!=null&&u>=e.depth)return t&&t(n.tr.lift(d,u).scrollIntoView()),!0;if(c&&nt(s,"start",!0)&&nt(i,"end")){let h=i,p=[];for(;p.push(h),!h.isTextblock;)h=h.lastChild;let m=s,g=1;for(;!m.isTextblock;m=m.firstChild)g++;if(h.canReplace(h.childCount,h.childCount,m.content)){if(t){let y=b.empty;for(let k=p.length-1;k>=0;k--)y=b.from(p[k].copy(y));let S=n.tr.step(new z(e.pos-p.length,e.pos+s.nodeSize,e.pos+g,e.pos+s.nodeSize-g,new x(y,p.length,0),0,!0));t(S.scrollIntoView())}return!0}}return!1}function hs(n){return function(e,t){let r=e.selection,i=n<0?r.$from:r.$to,s=i.depth;for(;i.node(s).isInline;){if(!s)return!1;s--}return i.node(s).isTextblock?(t&&t(e.tr.setSelection(T.create(e.doc,n<0?i.start(s):i.end(s)))),!0):!1}}var sr=hs(-1),or=hs(1);function ps(n,e=null){return function(t,r){let{$from:i,$to:s}=t.selection,o=i.blockRange(s),l=o&&et(o,n,e);return l?(r&&r(t.tr.wrap(o,l).scrollIntoView()),!0):!1}}function lr(n,e=null){return function(t,r){let i=!1;for(let s=0;s{if(i)return!1;if(!(!a.isTextblock||a.hasMarkup(n,e)))if(a.type==n)i=!0;else{let f=t.doc.resolve(c),d=f.index();i=f.parent.canReplaceWith(d,d+1,n)}})}if(!i)return!1;if(r){let s=t.tr;for(let o=0;o=2&&e.$from.node(e.depth-1).type.compatibleContent(t)&&e.startIndex==0){if(e.$from.index(e.depth-1)==0)return!1;let a=o.resolve(e.start-2);s=new ze(a,a,e.depth),e.endIndex=0;f--)s=b.from(t[f].type.create(t[f].attrs,s));n.step(new z(e.start-(r?2:0),e.end,e.start,e.end,new x(s,0,0),t.length,!0));let o=0;for(let f=0;fo.childCount>0&&o.firstChild.type==n);return s?t?r.node(s.depth-1).type==n?pa(e,t,n,s):ma(e,t,s):!0:!1}}function pa(n,e,t,r){let i=n.tr,s=r.end,o=r.$to.end(r.depth);sm;p--)h-=i.child(p).nodeSize,r.delete(h-1,h+1);let s=r.doc.resolve(t.start),o=s.nodeAfter;if(r.mapping.map(t.end)!=t.start+s.nodeAfter.nodeSize)return!1;let l=t.startIndex==0,a=t.endIndex==i.childCount,c=s.node(-1),f=s.index(-1);if(!c.canReplace(f+(l?0:1),f+1,o.content.append(a?b.empty:b.from(i))))return!1;let d=s.pos,u=d+o.nodeSize;return r.step(new z(d-(l?1:0),u+(a?1:0),d+1,u-1,new x((l?b.empty:b.from(i.copy(b.empty))).append(a?b.empty:b.from(i.copy(b.empty))),l?0:1,a?0:1),l?0:1)),e(r.scrollIntoView()),!0}function ys(n){return function(e,t){let{$from:r,$to:i}=e.selection,s=r.blockRange(i,c=>c.childCount>0&&c.firstChild.type==n);if(!s)return!1;let o=s.startIndex;if(o==0)return!1;let l=s.parent,a=l.child(o-1);if(a.type!=n)return!1;if(t){let c=a.lastChild&&a.lastChild.type==l.type,f=b.from(c?n.create():null),d=new x(b.from(n.create(null,b.from(l.type.create(null,f)))),c?3:1,0),u=s.start,h=s.end;t(e.tr.step(new z(u-(c?3:1),h,u,h,d,1,!0)).scrollIntoView())}return!0}}var H=function(n){for(var e=0;;e++)if(n=n.previousSibling,!n)return e},ot=function(n){let e=n.assignedSlot||n.parentNode;return e&&e.nodeType==11?e.host:e},pr=null,be=function(n,e,t){let r=pr||(pr=document.createRange());return r.setEnd(n,t??n.nodeValue.length),r.setStart(n,e||0),r},ga=function(){pr=null},Ke=function(n,e,t,r){return t&&(bs(n,e,t,r,-1)||bs(n,e,t,r,1))},ya=/^(img|br|input|textarea|hr)$/i;function bs(n,e,t,r,i){for(var s;;){if(n==t&&e==r)return!0;if(e==(i<0?0:se(n))){let o=n.parentNode;if(!o||o.nodeType!=1||At(n)||ya.test(n.nodeName)||n.contentEditable=="false")return!1;e=H(n)+(i<0?0:1),n=o}else if(n.nodeType==1){let o=n.childNodes[e+(i<0?-1:0)];if(o.nodeType==1&&o.contentEditable=="false")if(!((s=o.pmViewDesc)===null||s===void 0)&&s.ignoreForSelection)e+=i;else return!1;else n=o,e=i<0?se(n):0}else return!1}}function se(n){return n.nodeType==3?n.nodeValue.length:n.childNodes.length}function ba(n,e){for(;;){if(n.nodeType==3&&e)return n;if(n.nodeType==1&&e>0){if(n.contentEditable=="false")return null;n=n.childNodes[e-1],e=se(n)}else if(n.parentNode&&!At(n))e=H(n),n=n.parentNode;else return null}}function xa(n,e){for(;;){if(n.nodeType==3&&e2),ie=lt||(de?/Mac/.test(de.platform):!1),Gs=de?/Win/.test(de.platform):!1,xe=/Android \d/.test(De),Dt=!!xs&&"webkitFontSmoothing"in xs.documentElement.style,wa=Dt?+(/\bAppleWebKit\/(\d+)/.exec(navigator.userAgent)||[0,0])[1]:0;function Ca(n){let e=n.defaultView&&n.defaultView.visualViewport;return e?{left:0,right:e.width,top:0,bottom:e.height}:{left:0,right:n.documentElement.clientWidth,top:0,bottom:n.documentElement.clientHeight}}function ye(n,e){return typeof n=="number"?n:n[e]}function Ta(n){let e=n.getBoundingClientRect(),t=e.width/n.offsetWidth||1,r=e.height/n.offsetHeight||1;return{left:e.left,right:e.left+n.clientWidth*t,top:e.top,bottom:e.top+n.clientHeight*r}}function Ss(n,e,t){if(!br(e)&&e.left==0)return;let r=n.someProp("scrollThreshold")||0,i=n.someProp("scrollMargin")||5,s=n.dom.ownerDocument;for(let o=t||n.dom;o;){if(o.nodeType!=1){o=ot(o);continue}let l=o,a=l==s.body,c=a?Ca(s):Ta(l),f=0,d=0;if(e.topc.bottom-ye(r,"bottom")&&(d=e.bottom-e.top>c.bottom-c.top?e.top+ye(i,"top")-c.top:e.bottom-c.bottom+ye(i,"bottom")),e.leftc.right-ye(r,"right")&&(f=e.right-c.right+ye(i,"right")),f||d)if(a)s.defaultView.scrollBy(f,d);else{let h=l.scrollLeft,p=l.scrollTop;d&&(l.scrollTop+=d),f&&(l.scrollLeft+=f);let m=l.scrollLeft-h,g=l.scrollTop-p;e={left:e.left-m,top:e.top-g,right:e.right-m,bottom:e.bottom-g}}let u=a?"fixed":getComputedStyle(o).position;if(/^(fixed|sticky)$/.test(u))break;o=u=="absolute"?o.offsetParent:ot(o)}}function Na(n){let e=n.dom.getBoundingClientRect(),t=Math.max(0,e.top),r,i;for(let s=(e.left+e.right)/2,o=t+1;o=t-20){r=l,i=a.top;break}}return{refDOM:r,refTop:i,stack:Xs(n.dom)}}function Xs(n){let e=[],t=n.ownerDocument;for(let r=n;r&&(e.push({dom:r,top:r.scrollTop,left:r.scrollLeft}),n!=t);r=ot(r));return e}function Ea({refDOM:n,refTop:e,stack:t}){let r=n?n.getBoundingClientRect().top:0;Qs(t,r==0?0:r-e)}function Qs(n,e){for(let t=0;t=l){o=Math.max(p.bottom,o),l=Math.min(p.top,l);let m=p.left>e.left?p.left-e.left:p.right=(p.left+p.right)/2?1:0));continue}}else p.top>e.top&&!a&&p.left<=e.left&&p.right>=e.left&&(a=f,c={left:Math.max(p.left,Math.min(p.right,e.left)),top:p.top});!t&&(e.left>=p.right&&e.top>=p.top||e.left>=p.left&&e.top>=p.bottom)&&(s=d+1)}}return!t&&a&&(t=a,i=c,r=0),t&&t.nodeType==3?Oa(t,i):!t||r&&t.nodeType==1?{node:n,offset:s}:Zs(t,i)}function Oa(n,e){let t=n.nodeValue.length,r=document.createRange(),i;for(let s=0;s=(o.left+o.right)/2?1:0)};break}}return r.detach(),i||{node:n,offset:0}}function Ir(n,e){return n.left>=e.left-1&&n.left<=e.right+1&&n.top>=e.top-1&&n.top<=e.bottom+1}function Aa(n,e){let t=n.parentNode;return t&&/^li$/i.test(t.nodeName)&&e.left(o.left+o.right)/2?1:-1}return n.docView.posFromDOM(r,i,s)}function Ra(n,e,t,r){let i=-1;for(let s=e,o=!1;s!=n.dom;){let l=n.docView.nearestDesc(s,!0),a;if(!l)return null;if(l.dom.nodeType==1&&(l.node.isBlock&&l.parent||!l.contentDOM)&&((a=l.dom.getBoundingClientRect()).width||a.height)&&(l.node.isBlock&&l.parent&&!/^T(R|BODY|HEAD|FOOT)$/.test(l.dom.nodeName)&&(!o&&a.left>r.left||a.top>r.top?i=l.posBefore:(!o&&a.right-1?i:n.docView.posFromDOM(e,t,-1)}function eo(n,e,t){let r=n.childNodes.length;if(r&&t.tope.top&&i++}let c;Dt&&i&&r.nodeType==1&&(c=r.childNodes[i-1]).nodeType==1&&c.contentEditable=="false"&&c.getBoundingClientRect().top>=e.top&&i--,r==n.dom&&i==r.childNodes.length-1&&r.lastChild.nodeType==1&&e.top>r.lastChild.getBoundingClientRect().bottom?l=n.state.doc.content.size:(i==0||r.nodeType!=1||r.childNodes[i-1].nodeName!="BR")&&(l=Ra(n,r,i,e))}l==null&&(l=Da(n,o,e));let a=n.docView.nearestDesc(o,!0);return{pos:l,inside:a?a.posAtStart-a.border:-1}}function br(n){return n.top=0&&i==r.nodeValue.length?(a--,f=1):t<0?a--:c++,wt(Ne(be(r,a,c),f),f<0)}if(!n.state.doc.resolve(e-(s||0)).parent.inlineContent){if(s==null&&i&&(t<0||i==se(r))){let a=r.childNodes[i-1];if(a.nodeType==1)return cr(a.getBoundingClientRect(),!1)}if(s==null&&i=0)}if(s==null&&i&&(t<0||i==se(r))){let a=r.childNodes[i-1],c=a.nodeType==3?be(a,se(a)-(o?0:1)):a.nodeType==1&&(a.nodeName!="BR"||!a.nextSibling)?a:null;if(c)return wt(Ne(c,1),!1)}if(s==null&&i=0)}function wt(n,e){if(n.width==0)return n;let t=e?n.left:n.right;return{top:n.top,bottom:n.bottom,left:t,right:t}}function cr(n,e){if(n.height==0)return n;let t=e?n.top:n.bottom;return{top:t,bottom:t,left:n.left,right:n.right}}function no(n,e,t){let r=n.state,i=n.root.activeElement;r!=e&&n.updateState(e),i!=n.dom&&n.focus();try{return t()}finally{r!=e&&n.updateState(r),i!=n.dom&&i&&i.focus()}}function za(n,e,t){let r=e.selection,i=t=="up"?r.$from:r.$to;return no(n,e,()=>{let{node:s}=n.docView.domFromPos(i.pos,t=="up"?-1:1);for(;;){let l=n.docView.nearestDesc(s,!0);if(!l)break;if(l.node.isBlock){s=l.contentDOM||l.dom;break}s=l.dom.parentNode}let o=to(n,i.pos,1);for(let l=s.firstChild;l;l=l.nextSibling){let a;if(l.nodeType==1)a=l.getClientRects();else if(l.nodeType==3)a=be(l,0,l.nodeValue.length).getClientRects();else continue;for(let c=0;cf.top+1&&(t=="up"?o.top-f.top>(f.bottom-o.top)*2:f.bottom-o.bottom>(o.bottom-f.top)*2))return!1}}return!0})}var Ba=/[\u0590-\u08ac]/;function Fa(n,e,t){let{$head:r}=e.selection;if(!r.parent.isTextblock)return!1;let i=r.parentOffset,s=!i,o=i==r.parent.content.size,l=n.domSelection();return l?!Ba.test(r.parent.textContent)||!l.modify?t=="left"||t=="backward"?s:o:no(n,e,()=>{let{focusNode:a,focusOffset:c,anchorNode:f,anchorOffset:d}=n.domSelectionRange(),u=l.caretBidiLevel;l.modify("move",t,"character");let h=r.depth?n.docView.domAfterPos(r.before()):n.dom,{focusNode:p,focusOffset:m}=n.domSelectionRange(),g=p&&!h.contains(p.nodeType==1?p:p.parentNode)||a==p&&c==m;try{l.collapse(f,d),a&&(a!=f||c!=d)&&l.extend&&l.extend(a,c)}catch{}return u!=null&&(l.caretBidiLevel=u),g}):r.pos==r.start()||r.pos==r.end()}var ks=null,Ms=null,ws=!1;function La(n,e,t){return ks==e&&Ms==t?ws:(ks=e,Ms=t,ws=t=="up"||t=="down"?za(n,e,t):Fa(n,e,t))}var le=0,Cs=1,$e=2,ce=3,qe=class{constructor(e,t,r,i){this.parent=e,this.children=t,this.dom=r,this.contentDOM=i,this.dirty=le,r.pmViewDesc=this}matchesWidget(e){return!1}matchesMark(e){return!1}matchesNode(e,t,r){return!1}matchesHack(e){return!1}parseRule(e){return null}stopEvent(e){return!1}get size(){let e=0;for(let t=0;tH(this.contentDOM);else if(this.contentDOM&&this.contentDOM!=this.dom&&this.dom.contains(this.contentDOM))i=e.compareDocumentPosition(this.contentDOM)&2;else if(this.dom.firstChild){if(t==0)for(let s=e;;s=s.parentNode){if(s==this.dom){i=!1;break}if(s.previousSibling)break}if(i==null&&t==e.childNodes.length)for(let s=e;;s=s.parentNode){if(s==this.dom){i=!0;break}if(s.nextSibling)break}}return i??r>0?this.posAtEnd:this.posAtStart}nearestDesc(e,t=!1){for(let r=!0,i=e;i;i=i.parentNode){let s=this.getDesc(i),o;if(s&&(!t||s.node))if(r&&(o=s.nodeDOM)&&!(o.nodeType==1?o.contains(e.nodeType==1?e:e.parentNode):o==e))r=!1;else return s}}getDesc(e){let t=e.pmViewDesc;for(let r=t;r;r=r.parent)if(r==this)return t}posFromDOM(e,t,r){for(let i=e;i;i=i.parentNode){let s=this.getDesc(i);if(s)return s.localPosFromDOM(e,t,r)}return-1}descAt(e){for(let t=0,r=0;te||o instanceof en){i=e-s;break}s=l}if(i)return this.children[r].domFromPos(i-this.children[r].border,t);for(let s;r&&!(s=this.children[r-1]).size&&s instanceof Qt&&s.side>=0;r--);if(t<=0){let s,o=!0;for(;s=r?this.children[r-1]:null,!(!s||s.dom.parentNode==this.contentDOM);r--,o=!1);return s&&t&&o&&!s.border&&!s.domAtom?s.domFromPos(s.size,t):{node:this.contentDOM,offset:s?H(s.dom)+1:0}}else{let s,o=!0;for(;s=r=f&&t<=c-a.border&&a.node&&a.contentDOM&&this.contentDOM.contains(a.contentDOM))return a.parseRange(e,t,f);e=o;for(let d=l;d>0;d--){let u=this.children[d-1];if(u.size&&u.dom.parentNode==this.contentDOM&&!u.emptyChildAt(1)){i=H(u.dom)+1;break}e-=u.size}i==-1&&(i=0)}if(i>-1&&(c>t||l==this.children.length-1)){t=c;for(let f=l+1;fp&&ot){let p=l;l=a,a=p}let h=document.createRange();h.setEnd(a.node,a.offset),h.setStart(l.node,l.offset),c.removeAllRanges(),c.addRange(h)}}ignoreMutation(e){return!this.contentDOM&&e.type!="selection"}get contentLost(){return this.contentDOM&&this.contentDOM!=this.dom&&!this.dom.contains(this.contentDOM)}markDirty(e,t){for(let r=0,i=0;i=r:er){let l=r+s.border,a=o-s.border;if(e>=l&&t<=a){this.dirty=e==r||t==o?$e:Cs,e==l&&t==a&&(s.contentLost||s.dom.parentNode!=this.contentDOM)?s.dirty=ce:s.markDirty(e-l,t-l);return}else s.dirty=s.dom==s.contentDOM&&s.dom.parentNode==this.contentDOM&&!s.children.length?$e:ce}r=o}this.dirty=$e}markParentsDirty(){let e=1;for(let t=this.parent;t;t=t.parent,e++){let r=e==1?$e:Cs;t.dirty{if(!s)return i;if(s.parent)return s.parent.posBeforeChild(s)})),!t.type.spec.raw){if(o.nodeType!=1){let l=document.createElement("span");l.appendChild(o),o=l}o.contentEditable="false",o.classList.add("ProseMirror-widget")}super(e,[],o,null),this.widget=t,this.widget=t,s=this}matchesWidget(e){return this.dirty==le&&e.type.eq(this.widget.type)}parseRule(){return{ignore:!0}}stopEvent(e){let t=this.widget.spec.stopEvent;return t?t(e):!1}ignoreMutation(e){return e.type!="selection"||this.widget.spec.ignoreSelection}destroy(){this.widget.type.destroy(this.dom),super.destroy()}get domAtom(){return!0}get ignoreForSelection(){return!!this.widget.type.spec.relaxedSide}get side(){return this.widget.type.side}},xr=class extends qe{constructor(e,t,r,i){super(e,[],t,null),this.textDOM=r,this.text=i}get size(){return this.text.length}localPosFromDOM(e,t){return e!=this.textDOM?this.posAtStart+(t?this.size:0):this.posAtStart+t}domFromPos(e){return{node:this.textDOM,offset:e}}ignoreMutation(e){return e.type==="characterData"&&e.target.nodeValue==e.oldValue}},He=class n extends qe{constructor(e,t,r,i,s){super(e,[],r,i),this.mark=t,this.spec=s}static create(e,t,r,i){let s=i.nodeViews[t.type.name],o=s&&s(t,i,r);return(!o||!o.dom)&&(o=he.renderSpec(document,t.type.spec.toDOM(t,r),null,t.attrs)),new n(e,t,o.dom,o.contentDOM||o.dom,o)}parseRule(){return this.dirty&ce||this.mark.type.spec.reparseInView?null:{mark:this.mark.type.name,attrs:this.mark.attrs,contentElement:this.contentDOM}}matchesMark(e){return this.dirty!=ce&&this.mark.eq(e)}markDirty(e,t){if(super.markDirty(e,t),this.dirty!=le){let r=this.parent;for(;!r.node;)r=r.parent;r.dirty0&&(s=wr(s,0,e,r));for(let l=0;l{if(!a)return o;if(a.parent)return a.parent.posBeforeChild(a)},r,i),f=c&&c.dom,d=c&&c.contentDOM;if(t.isText){if(!f)f=document.createTextNode(t.text);else if(f.nodeType!=3)throw new RangeError("Text must be rendered as a DOM text node")}else f||({dom:f,contentDOM:d}=he.renderSpec(document,t.type.spec.toDOM(t),null,t.attrs));!d&&!t.isText&&f.nodeName!="BR"&&(f.hasAttribute("contenteditable")||(f.contentEditable="false"),t.type.spec.draggable&&(f.draggable=!0));let u=f;return f=so(f,r,t),c?a=new Sr(e,t,r,i,f,d||null,u,c):t.isText?new Zt(e,t,r,i,f,u):new n(e,t,r,i,f,d||null,u)}parseRule(e){if(this.node.type.spec.reparseInView)return null;let t={node:this.node.type.name,attrs:this.node.attrs};if(this.node.type.whitespace=="pre"&&(t.preserveWhitespace="full"),!this.contentDOM)t.getContent=()=>this.node.content;else if(!this.contentLost)t.contentElement=this.contentDOM;else{for(let r=this.children.length-1;r>=0;r--){let i=this.children[r];if(this.dom.contains(i.dom.parentNode)){t.contentElement=i.dom.parentNode;break}}if(!t.contentElement){let r=e&&e.find(i=>i.nodeType==1&&e.indexOf(i.parentNode)<0&&this.dom.contains(i));r?t.contentElement=r:t.getContent=()=>b.empty}}return t}matchesNode(e,t,r){return this.dirty==le&&e.eq(this.node)&&tn(t,this.outerDeco)&&r.eq(this.innerDeco)}get size(){return this.node.nodeSize}get border(){return this.node.isLeaf?0:1}updateChildren(e,t){let r=this.node.inlineContent,i=t,s=e.composing?this.localCompositionInfo(e,t):null,o=s&&s.pos>-1?s:null,l=s&&s.pos<0,a=new Mr(this,o&&o.node,e);Ha(this.node,this.innerDeco,(c,f,d)=>{c.spec.marks?a.syncToMarks(c.spec.marks,r,e,f):c.type.side>=0&&!d&&a.syncToMarks(f==this.node.childCount?A.none:this.node.child(f).marks,r,e,f),a.placeWidget(c,e,i)},(c,f,d,u)=>{a.syncToMarks(c.marks,r,e,u);let h;a.findNodeMatch(c,f,d,u)||l&&e.state.selection.from>i&&e.state.selection.to-1&&a.updateNodeAt(c,f,d,h,e)||a.updateNextNode(c,f,d,e,u,i)||a.addNode(c,f,d,e,i),i+=c.nodeSize}),a.syncToMarks([],r,e,0),this.node.isTextblock&&a.addTextblockHacks(),a.destroyRest(),(a.changed||this.dirty==$e)&&(o&&this.protectLocalComposition(e,o),ro(this.contentDOM,this.children,e),lt&&Ja(this.dom))}localCompositionInfo(e,t){let{from:r,to:i}=e.state.selection;if(!(e.state.selection instanceof T)||rt+this.node.content.size)return null;let s=e.input.compositionNode;if(!s||!this.dom.contains(s.parentNode))return null;if(this.node.inlineContent){let o=s.nodeValue,l=ja(this.node.content,o,r-t,i-t);return l<0?null:{node:s,pos:l,text:o}}else return{node:s,pos:-1,text:""}}protectLocalComposition(e,{node:t,pos:r,text:i}){if(this.getDesc(t))return;let s=t;for(;s.parentNode!=this.contentDOM;s=s.parentNode){for(;s.previousSibling;)s.parentNode.removeChild(s.previousSibling);for(;s.nextSibling;)s.parentNode.removeChild(s.nextSibling);s.pmViewDesc&&(s.pmViewDesc=void 0)}let o=new xr(this,s,t,i);e.input.compositionNodes.push(o),this.children=wr(this.children,r,r+i.length,e,o)}update(e,t,r,i){return this.dirty==ce||!e.sameMarkup(this.node)?!1:(this.updateInner(e,t,r,i),!0)}updateInner(e,t,r,i){this.updateOuterDeco(t),this.node=e,this.innerDeco=r,this.contentDOM&&this.updateChildren(i,this.posAtStart),this.dirty=le}updateOuterDeco(e){if(tn(e,this.outerDeco))return;let t=this.nodeDOM.nodeType!=1,r=this.dom;this.dom=io(this.dom,this.nodeDOM,kr(this.outerDeco,this.node,t),kr(e,this.node,t)),this.dom!=r&&(r.pmViewDesc=void 0,this.dom.pmViewDesc=this),this.outerDeco=e}selectNode(){this.nodeDOM.nodeType==1&&(this.nodeDOM.classList.add("ProseMirror-selectednode"),(this.contentDOM||!this.node.type.spec.draggable)&&(this.nodeDOM.draggable=!0))}deselectNode(){this.nodeDOM.nodeType==1&&(this.nodeDOM.classList.remove("ProseMirror-selectednode"),(this.contentDOM||!this.node.type.spec.draggable)&&this.nodeDOM.removeAttribute("draggable"))}get domAtom(){return this.node.isAtom}};function Ts(n,e,t,r,i){so(r,e,n);let s=new Oe(void 0,n,e,t,r,r,r);return s.contentDOM&&s.updateChildren(i,0),s}var Zt=class n extends Oe{constructor(e,t,r,i,s,o){super(e,t,r,i,s,null,o)}parseRule(){let e=this.nodeDOM.parentNode;for(;e&&e!=this.dom&&!e.pmIsDeco;)e=e.parentNode;return{skip:e||!0}}update(e,t,r,i){return this.dirty==ce||this.dirty!=le&&!this.inParent()||!e.sameMarkup(this.node)?!1:(this.updateOuterDeco(t),(this.dirty!=le||e.text!=this.node.text)&&e.text!=this.nodeDOM.nodeValue&&(this.nodeDOM.nodeValue=e.text,i.trackWrites==this.nodeDOM&&(i.trackWrites=null)),this.node=e,this.dirty=le,!0)}inParent(){let e=this.parent.contentDOM;for(let t=this.nodeDOM;t;t=t.parentNode)if(t==e)return!0;return!1}domFromPos(e){return{node:this.nodeDOM,offset:e}}localPosFromDOM(e,t,r){return e==this.nodeDOM?this.posAtStart+Math.min(t,this.node.text.length):super.localPosFromDOM(e,t,r)}ignoreMutation(e){return e.type!="characterData"&&e.type!="selection"}slice(e,t,r){let i=this.node.cut(e,t),s=document.createTextNode(i.text);return new n(this.parent,i,this.outerDeco,this.innerDeco,s,s)}markDirty(e,t){super.markDirty(e,t),this.dom!=this.nodeDOM&&(e==0||t==this.nodeDOM.nodeValue.length)&&(this.dirty=ce)}get domAtom(){return!1}isText(e){return this.node.text==e}},en=class extends qe{parseRule(){return{ignore:!0}}matchesHack(e){return this.dirty==le&&this.dom.nodeName==e}get domAtom(){return!0}get ignoreForCoords(){return this.dom.nodeName=="IMG"}},Sr=class extends Oe{constructor(e,t,r,i,s,o,l,a){super(e,t,r,i,s,o,l),this.spec=a}update(e,t,r,i){if(this.dirty==ce)return!1;if(this.spec.update&&(this.node.type==e.type||this.spec.multiType)){let s=this.spec.update(e,t,r);return s&&this.updateInner(e,t,r,i),s}else return!this.contentDOM&&!e.isLeaf?!1:super.update(e,t,r,i)}selectNode(){this.spec.selectNode?this.spec.selectNode():super.selectNode()}deselectNode(){this.spec.deselectNode?this.spec.deselectNode():super.deselectNode()}setSelection(e,t,r,i){this.spec.setSelection?this.spec.setSelection(e,t,r.root):super.setSelection(e,t,r,i)}destroy(){this.spec.destroy&&this.spec.destroy(),super.destroy()}stopEvent(e){return this.spec.stopEvent?this.spec.stopEvent(e):!1}ignoreMutation(e){return this.spec.ignoreMutation?this.spec.ignoreMutation(e):super.ignoreMutation(e)}};function ro(n,e,t){let r=n.firstChild,i=!1;for(let s=0;s>1,l=Math.min(o,e.length);for(;s-1)a>this.index&&(this.changed=!0,this.destroyBetween(this.index,a)),this.top=this.top.children[this.index];else{let f=He.create(this.top,e[o],t,r);this.top.children.splice(this.index,0,f),this.top=f,this.changed=!0}this.index=0,o++}}findNodeMatch(e,t,r,i){let s=-1,o;if(i>=this.preMatch.index&&(o=this.preMatch.matches[i-this.preMatch.index]).parent==this.top&&o.matchesNode(e,t,r))s=this.top.children.indexOf(o,this.index);else for(let l=this.index,a=Math.min(this.top.children.length,l+5);l0;){let l;for(;;)if(r){let c=t.children[r-1];if(c instanceof He)t=c,r=c.children.length;else{l=c,r--;break}}else{if(t==e)break e;r=t.parent.children.indexOf(t),t=t.parent}let a=l.node;if(a){if(a!=n.child(i-1))break;--i,s.set(l,i),o.push(l)}}return{index:i,matched:s,matches:o.reverse()}}function Wa(n,e){return n.type.side-e.type.side}function Ha(n,e,t,r){let i=e.locals(n),s=0;if(i.length==0){for(let c=0;cs;)l.push(i[o++]);let p=s+u.nodeSize;if(u.isText){let g=p;o!g.inline):l.slice();r(u,m,e.forChild(s,u),h),s=p}}function Ja(n){if(n.nodeName=="UL"||n.nodeName=="OL"){let e=n.style.cssText;n.style.cssText=e+"; list-style: square !important",window.getComputedStyle(n).listStyle,n.style.cssText=e}}function ja(n,e,t,r){for(let i=0,s=0;i=t){if(s>=r&&a.slice(r-e.length-l,r-l)==e)return r-e.length;let c=l=0&&c+e.length+l>=t)return l+c;if(t==r&&a.length>=r+e.length-l&&a.slice(r-l,r-l+e.length)==e)return r}}return-1}function wr(n,e,t,r,i){let s=[];for(let o=0,l=0;o=t||f<=e?s.push(a):(ct&&s.push(a.slice(t-c,a.size,r)))}return s}function zr(n,e=null){let t=n.domSelectionRange(),r=n.state.doc;if(!t.focusNode)return null;let i=n.docView.nearestDesc(t.focusNode),s=i&&i.size==0,o=n.docView.posFromDOM(t.focusNode,t.focusOffset,1);if(o<0)return null;let l=r.resolve(o),a,c;if(fn(t)){for(a=o;i&&!i.node;)i=i.parent;let d=i.node;if(i&&d.isAtom&&C.isSelectable(d)&&i.parent&&!(d.isInline&&Sa(t.focusNode,t.focusOffset,i.dom))){let u=i.posBefore;c=new C(o==u?l:r.resolve(u))}}else{if(t instanceof n.dom.ownerDocument.defaultView.Selection&&t.rangeCount>1){let d=o,u=o;for(let h=0;h{(t.anchorNode!=r||t.anchorOffset!=i)&&(e.removeEventListener("selectionchange",n.input.hideSelectionGuard),setTimeout(()=>{(!oo(n)||n.state.selection.visible)&&n.dom.classList.remove("ProseMirror-hideselection")},20))})}function Ka(n){let e=n.domSelection();if(!e)return;let t=n.cursorWrapper.dom,r=t.nodeName=="IMG";r?e.collapse(t.parentNode,H(t)+1):e.collapse(t,0),!r&&!n.state.selection.visible&&Z&&ve<=11&&(t.disabled=!0,t.disabled=!1)}function lo(n,e){if(e instanceof C){let t=n.docView.descAt(e.from);t!=n.lastSelectedViewDesc&&(As(n),t&&t.selectNode(),n.lastSelectedViewDesc=t)}else As(n)}function As(n){n.lastSelectedViewDesc&&(n.lastSelectedViewDesc.parent&&n.lastSelectedViewDesc.deselectNode(),n.lastSelectedViewDesc=void 0)}function Br(n,e,t,r){return n.someProp("createSelectionBetween",i=>i(n,e,t))||T.between(e,t,r)}function Ds(n){return n.editable&&!n.hasFocus()?!1:ao(n)}function ao(n){let e=n.domSelectionRange();if(!e.anchorNode)return!1;try{return n.dom.contains(e.anchorNode.nodeType==3?e.anchorNode.parentNode:e.anchorNode)&&(n.editable||n.dom.contains(e.focusNode.nodeType==3?e.focusNode.parentNode:e.focusNode))}catch{return!1}}function qa(n){let e=n.docView.domFromPos(n.state.selection.anchor,0),t=n.domSelectionRange();return Ke(e.node,e.offset,t.anchorNode,t.anchorOffset)}function Cr(n,e){let{$anchor:t,$head:r}=n.selection,i=e>0?t.max(r):t.min(r),s=i.parent.inlineContent?i.depth?n.doc.resolve(e>0?i.after():i.before()):null:i;return s&&E.findFrom(s,e)}function Ee(n,e){return n.dispatch(n.state.tr.setSelection(e).scrollIntoView()),!0}function Rs(n,e,t){let r=n.state.selection;if(r instanceof T)if(t.indexOf("s")>-1){let{$head:i}=r,s=i.textOffset?null:e<0?i.nodeBefore:i.nodeAfter;if(!s||s.isText||!s.isLeaf)return!1;let o=n.state.doc.resolve(i.pos+s.nodeSize*(e<0?-1:1));return Ee(n,new T(r.$anchor,o))}else if(r.empty){if(n.endOfTextblock(e>0?"forward":"backward")){let i=Cr(n.state,e);return i&&i instanceof C?Ee(n,i):!1}else if(!(ie&&t.indexOf("m")>-1)){let i=r.$head,s=i.textOffset?null:e<0?i.nodeBefore:i.nodeAfter,o;if(!s||s.isText)return!1;let l=e<0?i.pos-s.nodeSize:i.pos;return s.isAtom||(o=n.docView.descAt(l))&&!o.contentDOM?C.isSelectable(s)?Ee(n,new C(e<0?n.state.doc.resolve(i.pos-s.nodeSize):i)):Dt?Ee(n,new T(n.state.doc.resolve(e<0?l:l+s.nodeSize))):!1:!1}}else return!1;else{if(r instanceof C&&r.node.isInline)return Ee(n,new T(e>0?r.$to:r.$from));{let i=Cr(n.state,e);return i?Ee(n,i):!1}}}function nn(n){return n.nodeType==3?n.nodeValue.length:n.childNodes.length}function Tt(n,e){let t=n.pmViewDesc;return t&&t.size==0&&(e<0||n.nextSibling||n.nodeName!="BR")}function it(n,e){return e<0?Ua(n):Ya(n)}function Ua(n){let e=n.domSelectionRange(),t=e.focusNode,r=e.focusOffset;if(!t)return;let i,s,o=!1;for(oe&&t.nodeType==1&&r0){if(t.nodeType!=1)break;{let l=t.childNodes[r-1];if(Tt(l,-1))i=t,s=--r;else if(l.nodeType==3)t=l,r=t.nodeValue.length;else break}}else{if(co(t))break;{let l=t.previousSibling;for(;l&&Tt(l,-1);)i=t.parentNode,s=H(l),l=l.previousSibling;if(l)t=l,r=nn(t);else{if(t=t.parentNode,t==n.dom)break;r=0}}}o?Tr(n,t,r):i&&Tr(n,i,s)}function Ya(n){let e=n.domSelectionRange(),t=e.focusNode,r=e.focusOffset;if(!t)return;let i=nn(t),s,o;for(;;)if(r{n.state==i&&ke(n)},50)}function Ps(n,e){let t=n.state.doc.resolve(e);if(!(J||Gs)&&t.parent.inlineContent){let i=n.coordsAtPos(e);if(e>t.start()){let s=n.coordsAtPos(e-1),o=(s.top+s.bottom)/2;if(o>i.top&&o1)return s.lefti.top&&o1)return s.left>i.left?"ltr":"rtl"}}return getComputedStyle(n.dom).direction=="rtl"?"rtl":"ltr"}function Is(n,e,t){let r=n.state.selection;if(r instanceof T&&!r.empty||t.indexOf("s")>-1||ie&&t.indexOf("m")>-1)return!1;let{$from:i,$to:s}=r;if(!i.parent.inlineContent||n.endOfTextblock(e<0?"up":"down")){let o=Cr(n.state,e);if(o&&o instanceof C)return Ee(n,o)}if(!i.parent.inlineContent){let o=e<0?i:s,l=r instanceof U?E.near(o,e):E.findFrom(o,e);return l?Ee(n,l):!1}return!1}function zs(n,e){if(!(n.state.selection instanceof T))return!0;let{$head:t,$anchor:r,empty:i}=n.state.selection;if(!t.sameParent(r))return!0;if(!i)return!1;if(n.endOfTextblock(e>0?"forward":"backward"))return!0;let s=!t.textOffset&&(e<0?t.nodeBefore:t.nodeAfter);if(s&&!s.isText){let o=n.state.tr;return e<0?o.delete(t.pos-s.nodeSize,t.pos):o.delete(t.pos,t.pos+s.nodeSize),n.dispatch(o),!0}return!1}function Bs(n,e,t){n.domObserver.stop(),e.contentEditable=t,n.domObserver.start()}function Qa(n){if(!K||n.state.selection.$head.parentOffset>0)return!1;let{focusNode:e,focusOffset:t}=n.domSelectionRange();if(e&&e.nodeType==1&&t==0&&e.firstChild&&e.firstChild.contentEditable=="false"){let r=e.firstChild;Bs(n,r,"true"),setTimeout(()=>Bs(n,r,"false"),20)}return!1}function Za(n){let e="";return n.ctrlKey&&(e+="c"),n.metaKey&&(e+="m"),n.altKey&&(e+="a"),n.shiftKey&&(e+="s"),e}function ec(n,e){let t=e.keyCode,r=Za(e);if(t==8||ie&&t==72&&r=="c")return zs(n,-1)||it(n,-1);if(t==46&&!e.shiftKey||ie&&t==68&&r=="c")return zs(n,1)||it(n,1);if(t==13||t==27)return!0;if(t==37||ie&&t==66&&r=="c"){let i=t==37?Ps(n,n.state.selection.from)=="ltr"?-1:1:-1;return Rs(n,i,r)||it(n,i)}else if(t==39||ie&&t==70&&r=="c"){let i=t==39?Ps(n,n.state.selection.from)=="ltr"?1:-1:1;return Rs(n,i,r)||it(n,i)}else{if(t==38||ie&&t==80&&r=="c")return Is(n,-1,r)||it(n,-1);if(t==40||ie&&t==78&&r=="c")return Qa(n)||Is(n,1,r)||it(n,1);if(r==(ie?"m":"c")&&(t==66||t==73||t==89||t==90))return!0}return!1}function Fr(n,e){n.someProp("transformCopied",h=>{e=h(e,n)});let t=[],{content:r,openStart:i,openEnd:s}=e;for(;i>1&&s>1&&r.childCount==1&&r.firstChild.childCount==1;){i--,s--;let h=r.firstChild;t.push(h.type.name,h.attrs!=h.type.defaultAttrs?h.attrs:null),r=h.content}let o=n.someProp("clipboardSerializer")||he.fromSchema(n.state.schema),l=go(),a=l.createElement("div");a.appendChild(o.serializeFragment(r,{document:l}));let c=a.firstChild,f,d=0;for(;c&&c.nodeType==1&&(f=mo[c.nodeName.toLowerCase()]);){for(let h=f.length-1;h>=0;h--){let p=l.createElement(f[h]);for(;a.firstChild;)p.appendChild(a.firstChild);a.appendChild(p),d++}c=a.firstChild}c&&c.nodeType==1&&c.setAttribute("data-pm-slice",`${i} ${s}${d?` -${d}`:""} ${JSON.stringify(t)}`);let u=n.someProp("clipboardTextSerializer",h=>h(e,n))||e.content.textBetween(0,e.content.size,`
+
+`);return{dom:a,text:u,slice:e}}function fo(n,e,t,r,i){let s=i.parent.type.spec.code,o,l;if(!t&&!e)return null;let a=!!e&&(r||s||!t);if(a){if(n.someProp("transformPastedText",u=>{e=u(e,s||r,n)}),s)return l=new x(b.from(n.state.schema.text(e.replace(/\r\n?/g,`
+`))),0,0),n.someProp("transformPasted",u=>{l=u(l,n,!0)}),l;let d=n.someProp("clipboardTextParser",u=>u(e,i,r,n));if(d)l=d;else{let u=i.marks(),{schema:h}=n.state,p=he.fromSchema(h);o=document.createElement("div"),e.split(/(?:\r\n?|\n)+/).forEach(m=>{let g=o.appendChild(document.createElement("p"));m&&g.appendChild(p.serializeNode(h.text(m,u)))})}}else n.someProp("transformPastedHTML",d=>{t=d(t,n)}),o=ic(t),Dt&&sc(o);let c=o&&o.querySelector("[data-pm-slice]"),f=c&&/^(\d+) (\d+)(?: -(\d+))? (.*)/.exec(c.getAttribute("data-pm-slice")||"");if(f&&f[3])for(let d=+f[3];d>0;d--){let u=o.firstChild;for(;u&&u.nodeType!=1;)u=u.nextSibling;if(!u)break;o=u}if(l||(l=(n.someProp("clipboardParser")||n.someProp("domParser")||fe.fromSchema(n.state.schema)).parseSlice(o,{preserveWhitespace:!!(a||f),context:i,ruleFromNode(u){return u.nodeName=="BR"&&!u.nextSibling&&u.parentNode&&!tc.test(u.parentNode.nodeName)?{ignore:!0}:null}})),f)l=oc(Fs(l,+f[1],+f[2]),f[4]);else if(l=x.maxOpen(nc(l.content,i),!0),l.openStart||l.openEnd){let d=0,u=0;for(let h=l.content.firstChild;d{l=d(l,n,a)}),l}var tc=/^(a|abbr|acronym|b|cite|code|del|em|i|ins|kbd|label|output|q|ruby|s|samp|span|strong|sub|sup|time|u|tt|var)$/i;function nc(n,e){if(n.childCount<2)return n;for(let t=e.depth;t>=0;t--){let i=e.node(t).contentMatchAt(e.index(t)),s,o=[];if(n.forEach(l=>{if(!o)return;let a=i.findWrapping(l.type),c;if(!a)return o=null;if(c=o.length&&s.length&&ho(a,s,l,o[o.length-1],0))o[o.length-1]=c;else{o.length&&(o[o.length-1]=po(o[o.length-1],s.length));let f=uo(l,a);o.push(f),i=i.matchType(f.type),s=a}}),o)return b.from(o)}return n}function uo(n,e,t=0){for(let r=e.length-1;r>=t;r--)n=e[r].create(null,b.from(n));return n}function ho(n,e,t,r,i){if(i1&&(s=0),i=t&&(l=e<0?o.contentMatchAt(0).fillBefore(l,s<=i).append(l):l.append(o.contentMatchAt(o.childCount).fillBefore(b.empty,!0))),n.replaceChild(e<0?0:n.childCount-1,o.copy(l))}function Fs(n,e,t){return et})),dr.createHTML(n)):n}function ic(n){let e=/^(\s* ]*>)*/.exec(n);e&&(n=n.slice(e[0].length));let t=go(),r=t.body,i=/<([a-z][^>\s]+)/i.exec(n),s;if((s=i&&mo[i[1].toLowerCase()])&&(n=s.map(o=>"<"+o+">").join("")+n+s.map(o=>""+o+">").reverse().join("")),r.innerHTML=rc(n),s)for(let o=0;o=0;l-=2){let a=t.nodes[r[l]];if(!a||a.hasRequiredAttrs())break;i=b.from(a.create(r[l+1],i)),s++,o++}return new x(i,s,o)}var Y={},G={},lc={touchstart:!0,touchmove:!0},Er=class{constructor(){this.shiftKey=!1,this.mouseDown=null,this.lastKeyCode=null,this.lastKeyCodeTime=0,this.lastClick={time:0,x:0,y:0,type:"",button:0},this.lastSelectionOrigin=null,this.lastSelectionTime=0,this.lastIOSEnter=0,this.lastIOSEnterFallbackTimeout=-1,this.lastFocus=0,this.lastTouch=0,this.lastChromeDelete=0,this.composing=!1,this.compositionNode=null,this.composingTimeout=-1,this.compositionNodes=[],this.compositionEndedAt=-2e8,this.compositionID=1,this.badSafariComposition=!1,this.compositionPendingChanges=0,this.domChangeCount=0,this.eventHandlers=Object.create(null),this.hideSelectionGuard=null}};function ac(n){for(let e in Y){let t=Y[e];n.dom.addEventListener(e,n.input.eventHandlers[e]=r=>{fc(n,r)&&!Lr(n,r)&&(n.editable||!(r.type in G))&&t(n,r)},lc[e]?{passive:!0}:void 0)}K&&n.dom.addEventListener("input",()=>null),vr(n)}function Se(n,e){n.input.lastSelectionOrigin=e,n.input.lastSelectionTime=Date.now()}function cc(n){n.input.mouseDown&&n.input.mouseDown.done(),n.domObserver.stop();for(let e in n.input.eventHandlers)n.dom.removeEventListener(e,n.input.eventHandlers[e]);clearTimeout(n.input.composingTimeout),clearTimeout(n.input.lastIOSEnterFallbackTimeout)}function vr(n){n.someProp("handleDOMEvents",e=>{for(let t in e)n.input.eventHandlers[t]||n.dom.addEventListener(t,n.input.eventHandlers[t]=r=>Lr(n,r))})}function Lr(n,e){return n.someProp("handleDOMEvents",t=>{let r=t[e.type];return r?r(n,e)||e.defaultPrevented:!1})}function fc(n,e){if(!e.bubbles)return!0;if(e.defaultPrevented)return!1;for(let t=e.target;t!=n.dom;t=t.parentNode)if(!t||t.nodeType==11||t.pmViewDesc&&t.pmViewDesc.stopEvent(e))return!1;return!0}function dc(n,e){!Lr(n,e)&&Y[e.type]&&(n.editable||!(e.type in G))&&Y[e.type](n,e)}G.keydown=(n,e)=>{let t=e;if(n.input.shiftKey=t.keyCode==16||t.shiftKey,!xo(n)&&(n.input.lastKeyCode=t.keyCode,n.input.lastKeyCodeTime=Date.now(),!(xe&&J&&t.keyCode==13)))if(t.keyCode!=229&&n.domObserver.forceFlush(),lt&&t.keyCode==13&&!t.ctrlKey&&!t.altKey&&!t.metaKey){let r=Date.now();n.input.lastIOSEnter=r,n.input.lastIOSEnterFallbackTimeout=setTimeout(()=>{n.input.lastIOSEnter==r&&(n.someProp("handleKeyDown",i=>i(n,Ve(13,"Enter"))),n.input.lastIOSEnter=0)},200)}else n.someProp("handleKeyDown",r=>r(n,t))||ec(n,t)?t.preventDefault():Se(n,"key")};G.keyup=(n,e)=>{e.keyCode==16&&(n.input.shiftKey=!1)};G.keypress=(n,e)=>{let t=e;if(xo(n)||!t.charCode||t.ctrlKey&&!t.altKey||ie&&t.metaKey)return;if(n.someProp("handleKeyPress",i=>i(n,t))){t.preventDefault();return}let r=n.state.selection;if(!(r instanceof T)||!r.$from.sameParent(r.$to)){let i=String.fromCharCode(t.charCode),s=()=>n.state.tr.insertText(i).scrollIntoView();!/[\r\n]/.test(i)&&!n.someProp("handleTextInput",o=>o(n,r.$from.pos,r.$to.pos,i,s))&&n.dispatch(s()),t.preventDefault()}};function Rt(n){return{left:n.clientX,top:n.clientY}}function uc(n,e){let t=e.x-n.clientX,r=e.y-n.clientY;return t*t+r*r<100}function Vr(n,e,t,r,i){if(r==-1)return!1;let s=n.state.doc.resolve(r);for(let o=s.depth+1;o>0;o--)if(n.someProp(e,l=>o>s.depth?l(n,t,s.nodeAfter,s.before(o),i,!0):l(n,t,s.node(o),s.before(o),i,!1)))return!0;return!1}function Pt(n,e,t){if(n.focused||n.focus(),n.state.selection.eq(e))return;let r=n.state.tr.setSelection(e);t=="pointer"&&r.setMeta("pointer",!0),n.dispatch(r)}function hc(n,e){if(e==-1)return!1;let t=n.state.doc.resolve(e),r=t.nodeAfter;return r&&r.isAtom&&C.isSelectable(r)?(Pt(n,new C(t),"pointer"),!0):!1}function pc(n,e){if(e==-1)return!1;let t=n.state.selection,r,i;t instanceof C&&(r=t.node);let s=n.state.doc.resolve(e);for(let o=s.depth+1;o>0;o--){let l=o>s.depth?s.nodeAfter:s.node(o);if(C.isSelectable(l)){r&&t.$from.depth>0&&o>=t.$from.depth&&s.before(t.$from.depth+1)==t.$from.pos?i=s.before(t.$from.depth):i=s.before(o);break}}return i!=null?(Pt(n,C.create(n.state.doc,i),"pointer"),!0):!1}function mc(n,e,t,r,i){return Vr(n,"handleClickOn",e,t,r)||n.someProp("handleClick",s=>s(n,e,r))||(i?pc(n,t):hc(n,t))}function gc(n,e,t,r){return Vr(n,"handleDoubleClickOn",e,t,r)||n.someProp("handleDoubleClick",i=>i(n,e,r))}function yc(n,e,t,r){return Vr(n,"handleTripleClickOn",e,t,r)||n.someProp("handleTripleClick",i=>i(n,e,r))||bc(n,t,r)}function bc(n,e,t){if(t.button!=0)return!1;let r=yo(n,e,!0),i=n.state.doc;return r?(Pt(n,r,"pointer"),r instanceof T&&i.eq(n.state.doc)&&(n.input.mouseDown=new Ar(n,r)),!0):!1}function yo(n,e,t){let r=n.state.doc;if(e==-1)return r.inlineContent?T.create(r,0,r.content.size):null;let i=r.resolve(e);for(let s=i.depth+1;s>0;s--){let o=s>i.depth?i.nodeAfter:i.node(s),l=i.before(s);if(o.inlineContent)return T.create(r,l+1,l+1+o.content.size);if(t&&C.isSelectable(o))return C.create(r,l)}return null}function $r(n){return sn(n)}var bo=ie?"metaKey":"ctrlKey";Y.mousedown=(n,e)=>{let t=e;n.input.shiftKey=t.shiftKey;let r=$r(n),i=Date.now(),s="singleClick";i-n.input.lastClick.time<500&&uc(t,n.input.lastClick)&&!t[bo]&&n.input.lastClick.button==t.button&&(n.input.lastClick.type=="singleClick"?s="doubleClick":n.input.lastClick.type=="doubleClick"&&(s="tripleClick")),n.input.lastClick={time:i,x:t.clientX,y:t.clientY,type:s,button:t.button},n.input.mouseDown&&n.input.mouseDown.done();let o=n.posAtCoords(Rt(t));o&&(s=="singleClick"?n.input.mouseDown=new Or(n,o,t,!!r):(s=="doubleClick"?gc:yc)(n,o.pos,o.inside,t)?t.preventDefault():Se(n,"pointer"))};var rn=class{constructor(e){this.view=e,this.mightDrag=null,e.root.addEventListener("mouseup",this.up=this.up.bind(this)),e.root.addEventListener("mousemove",this.move=this.move.bind(this))}up(e){this.done()}move(e){e.buttons==0&&this.done()}done(){this.view.root.removeEventListener("mouseup",this.up),this.view.root.removeEventListener("mousemove",this.move),this.view.input.mouseDown==this&&(this.view.input.mouseDown=null)}delaySelUpdate(){return!1}},Or=class extends rn{constructor(e,t,r,i){super(e),this.pos=t,this.event=r,this.flushed=i,this.delayedSelectionSync=!1,this.startDoc=e.state.doc,this.selectNode=!!r[bo],this.allowDefault=r.shiftKey;let s,o;if(t.inside>-1)s=e.state.doc.nodeAt(t.inside),o=t.inside;else{let f=e.state.doc.resolve(t.pos);s=f.parent,o=f.depth?f.before():0}let l=i?null:r.target,a=l?e.docView.nearestDesc(l,!0):null;this.target=a&&a.nodeDOM.nodeType==1?a.nodeDOM:null;let{selection:c}=e.state;r.button==0&&(s.type.spec.draggable&&s.type.spec.selectable!==!1||c instanceof C&&c.from<=o&&c.to>o)&&(this.mightDrag={node:s,pos:o,addAttr:!!(this.target&&!this.target.draggable),setUneditable:!!(this.target&&oe&&!this.target.hasAttribute("contentEditable"))}),this.target&&this.mightDrag&&(this.mightDrag.addAttr||this.mightDrag.setUneditable)&&(this.view.domObserver.stop(),this.mightDrag.addAttr&&(this.target.draggable=!0),this.mightDrag.setUneditable&&setTimeout(()=>{this.view.input.mouseDown==this&&this.target.setAttribute("contentEditable","false")},20),this.view.domObserver.start()),Se(e,"pointer")}done(){super.done(),this.mightDrag&&this.target&&(this.view.domObserver.stop(),this.mightDrag.addAttr&&this.target.removeAttribute("draggable"),this.mightDrag.setUneditable&&this.target.removeAttribute("contentEditable"),this.view.domObserver.start()),this.delayedSelectionSync&&setTimeout(()=>{this.view.isDestroyed||ke(this.view)})}up(e){if(this.done(),!this.view.dom.contains(e.target))return;let t=this.pos;this.view.state.doc!=this.startDoc&&(t=this.view.posAtCoords(Rt(e))),this.updateAllowDefault(e),this.allowDefault||!t?Se(this.view,"pointer"):mc(this.view,t.pos,t.inside,e,this.selectNode)?e.preventDefault():e.button==0&&(this.flushed||K&&this.mightDrag&&!this.mightDrag.node.isAtom||J&&!this.view.state.selection.visible&&Math.min(Math.abs(t.pos-this.view.state.selection.from),Math.abs(t.pos-this.view.state.selection.to))<=2)?(Pt(this.view,E.near(this.view.state.doc.resolve(t.pos)),"pointer"),e.preventDefault()):Se(this.view,"pointer")}move(e){this.updateAllowDefault(e),Se(this.view,"pointer"),super.move(e)}updateAllowDefault(e){!this.allowDefault&&(Math.abs(this.event.x-e.clientX)>4||Math.abs(this.event.y-e.clientY)>4)&&(this.allowDefault=!0)}delaySelUpdate(){return this.allowDefault?(this.delayedSelectionSync=!0,!0):!1}},Ar=class extends rn{constructor(e,t){super(e),this.startSelection=t,this.startDoc=e.state.doc}move(e){if(e.buttons==0||this.view.isDestroyed||!this.view.state.doc.eq(this.startDoc)){this.done();return}e.preventDefault(),Se(this.view,"pointer");let t=this.view.posAtCoords(Rt(e)),r=t&&yo(this.view,t.inside,!1);if(!r)return;let{doc:i}=this.view.state,s=this.startSelection,[o,l]=r.from{n.input.lastTouch=Date.now(),$r(n),Se(n,"pointer")};Y.touchmove=n=>{n.input.lastTouch=Date.now(),Se(n,"pointer")};Y.contextmenu=n=>$r(n);function xo(n,e){return n.composing?!0:K&&Math.abs(Date.now()-n.input.compositionEndedAt)<500?(n.input.compositionEndedAt=-2e8,!0):!1}var xc=xe?5e3:-1;G.compositionstart=G.compositionupdate=n=>{if(!n.composing){n.domObserver.flush();let{state:e}=n,t=e.selection.$to;if(e.selection instanceof T&&(e.storedMarks||!t.textOffset&&t.parentOffset&&t.nodeBefore.marks.some(r=>r.type.spec.inclusive===!1)||J&&Gs&&Sc(n)))n.markCursor=n.state.storedMarks||t.marks(),sn(n,!0),n.markCursor=null;else if(sn(n,!e.selection.empty),oe&&e.selection.empty&&t.parentOffset&&!t.textOffset&&t.nodeBefore.marks.length){let r=n.domSelectionRange();for(let i=r.focusNode,s=r.focusOffset;i&&i.nodeType==1&&s!=0;){let o=s<0?i.lastChild:i.childNodes[s-1];if(!o)break;if(o.nodeType==3){let l=n.domSelection();l&&l.collapse(o,o.nodeValue.length);break}else i=o,s=-1}}n.input.composing=!0}So(n,xc)};function Sc(n){let{focusNode:e,focusOffset:t}=n.domSelectionRange();if(!e||e.nodeType!=1||t>=e.childNodes.length)return!1;let r=e.childNodes[t];return r.nodeType==1&&r.contentEditable=="false"}G.compositionend=(n,e)=>{n.composing&&(n.input.composing=!1,n.input.compositionEndedAt=Date.now(),n.input.compositionPendingChanges=n.domObserver.pendingRecords().length?n.input.compositionID:0,n.input.compositionNode=null,n.input.badSafariComposition?n.domObserver.forceFlush():n.input.compositionPendingChanges&&Promise.resolve().then(()=>n.domObserver.flush()),n.input.compositionID++,So(n,20))};function So(n,e){clearTimeout(n.input.composingTimeout),e>-1&&(n.input.composingTimeout=setTimeout(()=>sn(n),e))}function ko(n){for(n.composing&&(n.input.composing=!1,n.input.compositionEndedAt=Date.now());n.input.compositionNodes.length>0;)n.input.compositionNodes.pop().markParentsDirty()}function kc(n){let e=n.domSelectionRange();if(!e.focusNode)return null;let t=ba(e.focusNode,e.focusOffset),r=xa(e.focusNode,e.focusOffset);if(t&&r&&t!=r){let i=r.pmViewDesc,s=n.domObserver.lastChangedTextNode;if(t==s||r==s)return s;if(!i||!i.isText(r.nodeValue))return r;if(n.input.compositionNode==r){let o=t.pmViewDesc;if(!(!o||!o.isText(t.nodeValue)))return r}}return t||r}function sn(n,e=!1){if(!(xe&&n.domObserver.flushingSoon>=0)){if(n.domObserver.forceFlush(),ko(n),e||n.docView&&n.docView.dirty){let t=zr(n),r=n.state.selection;return t&&!t.eq(r)?n.dispatch(n.state.tr.setSelection(t)):(n.markCursor||e)&&!r.$from.node(r.$from.sharedDepth(r.to)).inlineContent?n.dispatch(n.state.tr.deleteSelection()):n.updateState(n.state),!0}return!1}}function Mc(n,e){if(!n.dom.parentNode)return;let t=n.dom.parentNode.appendChild(document.createElement("div"));t.appendChild(e),t.style.cssText="position: fixed; left: -10000px; top: 10px";let r=getSelection(),i=document.createRange();i.selectNodeContents(e),n.dom.blur(),r.removeAllRanges(),r.addRange(i),setTimeout(()=>{t.parentNode&&t.parentNode.removeChild(t),n.focus()},50)}var Nt=Z&&ve<15||lt&&wa<604;Y.copy=G.cut=(n,e)=>{let t=e,r=n.state.selection,i=t.type=="cut";if(r.empty)return;let s=Nt?null:t.clipboardData,o=r.content(),{dom:l,text:a}=Fr(n,o);s?(t.preventDefault(),s.clearData(),s.setData("text/html",l.innerHTML),s.setData("text/plain",a)):Mc(n,l),i&&n.dispatch(n.state.tr.deleteSelection().scrollIntoView().setMeta("uiEvent","cut"))};function wc(n){return n.openStart==0&&n.openEnd==0&&n.content.childCount==1?n.content.firstChild:null}function Cc(n,e){if(!n.dom.parentNode)return;let t=n.input.shiftKey||n.state.selection.$from.parent.type.spec.code,r=n.dom.parentNode.appendChild(document.createElement(t?"textarea":"div"));t||(r.contentEditable="true"),r.style.cssText="position: fixed; left: -10000px; top: 10px",r.focus();let i=n.input.shiftKey&&n.input.lastKeyCode!=45;setTimeout(()=>{n.focus(),r.parentNode&&r.parentNode.removeChild(r),t?Et(n,r.value,null,i,e):Et(n,r.textContent,r.innerHTML,i,e)},50)}function Et(n,e,t,r,i){let s=fo(n,e,t,r,n.state.selection.$from);if(n.someProp("handlePaste",a=>a(n,i,s||x.empty)))return!0;if(!s)return!1;let o=wc(s),l=o?n.state.tr.replaceSelectionWith(o,r):n.state.tr.replaceSelection(s);return n.dispatch(l.scrollIntoView().setMeta("paste",!0).setMeta("uiEvent","paste")),!0}function Mo(n){let e=n.getData("text/plain")||n.getData("Text");if(e)return e;let t=n.getData("text/uri-list");return t?t.replace(/\r?\n/g," "):""}G.paste=(n,e)=>{let t=e;if(n.composing&&!xe)return;let r=Nt?null:t.clipboardData,i=n.input.shiftKey&&n.input.lastKeyCode!=45;r&&Et(n,Mo(r),r.getData("text/html"),i,t)?t.preventDefault():Cc(n,t)};var on=class{constructor(e,t,r){this.slice=e,this.move=t,this.node=r}},Tc=ie?"altKey":"ctrlKey";function wo(n,e){let t;return n.someProp("dragCopies",r=>{t=t||r(e)}),t!=null?!t:!e[Tc]}Y.dragstart=(n,e)=>{let t=e,r=n.input.mouseDown;if(r&&r.done(),!t.dataTransfer)return;let i=n.state.selection,s=i.empty?null:n.posAtCoords(Rt(t)),o;if(!(s&&s.pos>=i.from&&s.pos<=(i instanceof C?i.to-1:i.to))){if(r&&r.mightDrag)o=C.create(n.state.doc,r.mightDrag.pos);else if(t.target&&t.target.nodeType==1){let d=n.docView.nearestDesc(t.target,!0);d&&d.node.type.spec.draggable&&d!=n.docView&&(o=C.create(n.state.doc,d.posBefore))}}let l=(o||n.state.selection).content(),{dom:a,text:c,slice:f}=Fr(n,l);(!t.dataTransfer.files.length||!J||Ys>120)&&t.dataTransfer.clearData(),t.dataTransfer.setData(Nt?"Text":"text/html",a.innerHTML),t.dataTransfer.effectAllowed="copyMove",Nt||t.dataTransfer.setData("text/plain",c),n.dragging=new on(f,wo(n,t),o)};Y.dragend=n=>{let e=n.dragging;window.setTimeout(()=>{n.dragging==e&&(n.dragging=null)},50)};G.dragover=G.dragenter=(n,e)=>e.preventDefault();G.drop=(n,e)=>{try{Nc(n,e,n.dragging)}finally{n.dragging=null}};function Nc(n,e,t){if(!e.dataTransfer)return;let r=n.posAtCoords(Rt(e));if(!r)return;let i=n.state.doc.resolve(r.pos),s=t&&t.slice;s?n.someProp("transformPasted",h=>{s=h(s,n,!1)}):s=fo(n,Mo(e.dataTransfer),Nt?null:e.dataTransfer.getData("text/html"),!1,i);let o=!!(t&&wo(n,e));if(n.someProp("handleDrop",h=>h(n,e,s||x.empty,o))){e.preventDefault();return}if(!s)return;e.preventDefault();let l=s?Wi(n.state.doc,i.pos,s):i.pos;l==null&&(l=i.pos);let a=n.state.tr;if(o){let{node:h}=t;h?h.replace(a):a.deleteSelection()}let c=a.mapping.map(l),f=s.openStart==0&&s.openEnd==0&&s.content.childCount==1,d=a.doc;if(f?a.replaceRangeWith(c,c,s.content.firstChild):a.replaceRange(c,c,s),a.doc.eq(d))return;let u=a.doc.resolve(c);if(f&&C.isSelectable(s.content.firstChild)&&u.nodeAfter&&u.nodeAfter.sameMarkup(s.content.firstChild))a.setSelection(new C(u));else{let h=a.mapping.map(l);a.mapping.maps[a.mapping.maps.length-1].forEach((p,m,g,y)=>h=y),a.setSelection(Br(n,u,a.doc.resolve(h)))}n.focus(),n.dispatch(a.setMeta("uiEvent","drop"))}Y.focus=n=>{n.input.lastFocus=Date.now(),n.focused||(n.domObserver.stop(),n.dom.classList.add("ProseMirror-focused"),n.domObserver.start(),n.focused=!0,setTimeout(()=>{n.docView&&n.hasFocus()&&!n.domObserver.currentSelection.eq(n.domSelectionRange())&&ke(n)},20))};Y.blur=(n,e)=>{let t=e;n.focused&&(n.domObserver.stop(),n.dom.classList.remove("ProseMirror-focused"),n.domObserver.start(),t.relatedTarget&&n.dom.contains(t.relatedTarget)&&n.domObserver.currentSelection.clear(),n.focused=!1)};Y.beforeinput=(n,e)=>{if(xe&&e.inputType=="deleteContentBackward"){n.domObserver.flushSoon();let{domChangeCount:r}=n.input;setTimeout(()=>{if(n.input.domChangeCount!=r||(n.dom.blur(),n.focus(),n.someProp("handleKeyDown",s=>s(n,Ve(8,"Backspace")))))return;let{$cursor:i}=n.state.selection;i&&i.pos>0&&n.dispatch(n.state.tr.delete(i.pos-1,i.pos).scrollIntoView())},50)}};for(let n in G)Y[n]=G[n];function vt(n,e){if(n==e)return!0;for(let t in n)if(n[t]!==e[t])return!1;for(let t in e)if(!(t in n))return!1;return!0}var ln=class n{constructor(e,t){this.toDOM=e,this.spec=t||je,this.side=this.spec.side||0}map(e,t,r,i){let{pos:s,deleted:o}=e.mapResult(t.from+i,this.side<0?-1:1);return o?null:new Ae(s-r,s-r,this)}valid(){return!0}eq(e){return this==e||e instanceof n&&(this.spec.key&&this.spec.key==e.spec.key||this.toDOM==e.toDOM&&vt(this.spec,e.spec))}destroy(e){this.spec.destroy&&this.spec.destroy(e)}},Je=class n{constructor(e,t){this.attrs=e,this.spec=t||je}map(e,t,r,i){let s=e.map(t.from+i,this.spec.inclusiveStart?-1:1)-r,o=e.map(t.to+i,this.spec.inclusiveEnd?1:-1)-r;return s>=o?null:new Ae(s,o,this)}valid(e,t){return t.from=e&&(!s||s(l.spec))&&r.push(l.copy(l.from+i,l.to+i))}for(let o=0;oe){let l=this.children[o]+1;this.children[o+2].findInner(e-l,t-l,r,i+l,s)}}map(e,t,r){return this==_||e.maps.length==0?this:this.mapInner(e,t,0,0,r||je)}mapInner(e,t,r,i,s){let o;for(let l=0;l{let c=a+r,f;if(f=To(t,l,c)){for(i||(i=this.children.slice());sl&&d.to=e){this.children[l]==e&&(r=this.children[l+2]);break}let s=e+1,o=s+t.content.size;for(let l=0;ls&&a.type instanceof Je){let c=Math.max(s,a.from)-s,f=Math.min(o,a.to)-s;ci.map(e,t,je));return n.from(r)}forChild(e,t){if(t.isLeaf)return ae.empty;let r=[];for(let i=0;it instanceof ae)?e:e.reduce((t,r)=>t.concat(r instanceof ae?r:r.members),[]))}}forEachSet(e){for(let t=0;t{let g=m-p-(h-u);for(let y=0;yS+f-d)continue;let k=l[y]+f-d;h>=k?l[y+1]=u<=k?-2:-1:u>=f&&g&&(l[y]+=g,l[y+1]+=g)}d+=g}),f=t.maps[c].map(f,-1)}let a=!1;for(let c=0;c=r.content.size){a=!0;continue}let u=t.map(n[c+1]+s,-1),h=u-i,{index:p,offset:m}=r.content.findIndex(d),g=r.maybeChild(p);if(g&&m==d&&m+g.nodeSize==h){let y=l[c+2].mapInner(t,g,f+1,n[c]+s+1,o);y!=_?(l[c]=d,l[c+1]=h,l[c+2]=y):(l[c+1]=-2,a=!0)}else a=!0}if(a){let c=vc(l,n,e,t,i,s,o),f=cn(c,r,0,o);e=f.local;for(let d=0;dt&&o.to{let c=To(n,l,a+t);if(c){s=!0;let f=cn(c,l,t+a+1,r);f!=_&&i.push(a,a+l.nodeSize,f)}});let o=Co(s?No(n):n,-t).sort(_e);for(let l=0;l0;)e++;n.splice(e,0,t)}function ur(n){let e=[];return n.someProp("decorations",t=>{let r=t(n.state);r&&r!=_&&e.push(r)}),n.cursorWrapper&&e.push(ae.create(n.state.doc,[n.cursorWrapper.deco])),an.from(e)}var Oc={childList:!0,characterData:!0,characterDataOldValue:!0,attributes:!0,attributeOldValue:!0,subtree:!0},Ac=Z&&ve<=11,Rr=class{constructor(){this.anchorNode=null,this.anchorOffset=0,this.focusNode=null,this.focusOffset=0}set(e){this.anchorNode=e.anchorNode,this.anchorOffset=e.anchorOffset,this.focusNode=e.focusNode,this.focusOffset=e.focusOffset}clear(){this.anchorNode=this.focusNode=null}eq(e){return e.anchorNode==this.anchorNode&&e.anchorOffset==this.anchorOffset&&e.focusNode==this.focusNode&&e.focusOffset==this.focusOffset}},Pr=class{constructor(e,t){this.view=e,this.handleDOMChange=t,this.queue=[],this.flushingSoon=-1,this.observer=null,this.currentSelection=new Rr,this.onCharData=null,this.suppressingSelectionUpdates=!1,this.lastChangedTextNode=null,this.observer=window.MutationObserver&&new window.MutationObserver(r=>{for(let i=0;ii.type=="childList"&&i.removedNodes.length||i.type=="characterData"&&i.oldValue.length>i.target.nodeValue.length)?this.flushSoon():K&&e.composing&&r.some(i=>i.type=="childList"&&i.target.nodeName=="TR")?(e.input.badSafariComposition=!0,this.flushSoon()):this.flush()}),Ac&&(this.onCharData=r=>{this.queue.push({target:r.target,type:"characterData",oldValue:r.prevValue}),this.flushSoon()}),this.onSelectionChange=this.onSelectionChange.bind(this)}flushSoon(){this.flushingSoon<0&&(this.flushingSoon=window.setTimeout(()=>{this.flushingSoon=-1,this.flush()},20))}forceFlush(){this.flushingSoon>-1&&(window.clearTimeout(this.flushingSoon),this.flushingSoon=-1,this.flush())}start(){this.observer&&(this.observer.takeRecords(),this.observer.observe(this.view.dom,Oc)),this.onCharData&&this.view.dom.addEventListener("DOMCharacterDataModified",this.onCharData),this.connectSelection()}stop(){if(this.observer){let e=this.observer.takeRecords();if(e.length){for(let t=0;tthis.flush(),20)}this.observer.disconnect()}this.onCharData&&this.view.dom.removeEventListener("DOMCharacterDataModified",this.onCharData),this.disconnectSelection()}connectSelection(){this.view.dom.ownerDocument.addEventListener("selectionchange",this.onSelectionChange)}disconnectSelection(){this.view.dom.ownerDocument.removeEventListener("selectionchange",this.onSelectionChange)}suppressSelectionUpdates(){this.suppressingSelectionUpdates=!0,setTimeout(()=>this.suppressingSelectionUpdates=!1,50)}onSelectionChange(){if(Ds(this.view)){if(this.suppressingSelectionUpdates)return ke(this.view);if(Z&&ve<=11&&!this.view.state.selection.empty){let e=this.view.domSelectionRange();if(e.focusNode&&Ke(e.focusNode,e.focusOffset,e.anchorNode,e.anchorOffset))return this.flushSoon()}this.flush()}}setCurSelection(){this.currentSelection.set(this.view.domSelectionRange())}ignoreSelectionChange(e){if(!e.focusNode)return!0;let t=new Set,r;for(let s=e.focusNode;s;s=ot(s))t.add(s);for(let s=e.anchorNode;s;s=ot(s))if(t.has(s)){r=s;break}let i=r&&this.view.docView.nearestDesc(r);if(i&&i.ignoreMutation({type:"selection",target:r.nodeType==3?r.parentNode:r}))return this.setCurSelection(),!0}pendingRecords(){if(this.observer)for(let e of this.observer.takeRecords())this.queue.push(e);return this.queue}flush(){let{view:e}=this;if(!e.docView||this.flushingSoon>-1)return;let t=this.pendingRecords();t.length&&(this.queue=[]);let r=e.domSelectionRange(),i=!this.suppressingSelectionUpdates&&!this.currentSelection.eq(r)&&Ds(e)&&!this.ignoreSelectionChange(r),s=-1,o=-1,l=!1,a=[];if(e.editable)for(let f=0;ff.nodeName=="BR")&&(e.input.lastKeyCode==8||e.input.lastKeyCode==46||J&&(e.composing||e.input.compositionEndedAt>Date.now()-50)&&t.some(f=>f.type=="childList"&&f.removedNodes.length))){for(let f of a)if(f.nodeName=="BR"&&f.parentNode){let d=f.nextSibling;for(;d&&d.nodeType==1;){if(d.contentEditable=="false"){f.parentNode.removeChild(f);break}d=d.firstChild}}}else if(oe&&a.length){let f=a.filter(d=>d.nodeName=="BR");if(f.length==2){let[d,u]=f;d.parentNode&&d.parentNode.parentNode==u.parentNode?u.remove():d.remove()}else{let{focusNode:d}=this.currentSelection;for(let u of f){let h=u.parentNode;h&&h.nodeName=="LI"&&(!d||Pc(e,d)!=h)&&u.remove()}}}let c=null;s<0&&i&&e.input.lastFocus>Date.now()-200&&Math.max(e.input.lastTouch,e.input.lastClick.time)-1||i)&&(s>-1&&(e.docView.markDirty(s,o),Dc(e)),e.input.badSafariComposition&&(e.input.badSafariComposition=!1,Ic(e,a)),this.handleDOMChange(s,o,l,a),e.docView&&e.docView.dirty?e.updateState(e.state):this.currentSelection.eq(r)||ke(e),this.currentSelection.set(r))}registerMutation(e,t){if(t.indexOf(e.target)>-1)return null;let r=this.view.docView.nearestDesc(e.target);if(e.type=="attributes"&&(r==this.view.docView||e.attributeName=="contenteditable"||e.attributeName=="style"&&!e.oldValue&&!e.target.getAttribute("style"))||!r||r.ignoreMutation(e))return null;if(e.type=="childList"){for(let f=0;fs;y--){let S=i.childNodes[y-1],k=S.pmViewDesc;if(S.nodeName=="BR"&&!k){o=y;break}if(!k||k.size)break}let u=n.state.doc,h=n.someProp("domParser")||fe.fromSchema(n.state.schema),p=u.resolve(l),m=null,g=h.parse(i,{topNode:p.parent,topMatch:p.parent.contentMatchAt(p.index()),topOpen:!0,from:s,to:o,preserveWhitespace:p.parent.type.whitespace=="pre"?"full":!0,findPositions:f,ruleFromNode:Bc(r),context:p});if(f&&f[0].pos!=null){let y=f[0].pos,S=f[1]&&f[1].pos;S==null&&(S=y),m={anchor:y+l,head:S+l}}return{doc:g,sel:m,from:l,to:a}}var Bc=n=>e=>{let t=e.pmViewDesc;if(t)return t.parseRule(n);if(e.nodeName=="BR"&&e.parentNode){if(K&&/^(ul|ol)$/i.test(e.parentNode.nodeName)){let r=document.createElement("div");return r.appendChild(document.createElement("li")),{skip:r}}else if(e.parentNode.lastChild==e||K&&/^(tr|table)$/i.test(e.parentNode.nodeName))return{ignore:!0}}else if(e.nodeName=="IMG"&&e.getAttribute("mark-placeholder"))return{ignore:!0};return null},Fc=/^(a|abbr|acronym|b|bd[io]|big|br|button|cite|code|data(list)?|del|dfn|em|i|img|ins|kbd|label|map|mark|meter|output|q|ruby|s|samp|small|span|strong|su[bp]|time|u|tt|var)$/i;function Lc(n,e,t,r,i){let s=n.input.compositionPendingChanges||(n.composing?n.input.compositionID:0);if(n.input.compositionPendingChanges=0,e<0){let w=n.input.lastSelectionTime>Date.now()-50?n.input.lastSelectionOrigin:null,O=zr(n,w);if(O&&!n.state.selection.eq(O)){if(J&&xe&&n.input.lastKeyCode===13&&Date.now()-100q(n,Ve(13,"Enter"))))return;let R=n.state.tr.setSelection(O);w=="pointer"?R.setMeta("pointer",!0):w=="key"&&R.scrollIntoView(),s&&R.setMeta("composition",s),n.dispatch(R)}return}let o=n.state.doc.resolve(e),l=o.sharedDepth(t);e=o.before(l+1),t=n.state.doc.resolve(t).after(l+1);let a=n.state.selection,c=zc(n,e,t,i),f=n.state.doc,d=f.slice(c.from,c.to),u,h;n.input.lastKeyCode===8&&Date.now()-100Date.now()-225||xe)&&i.some(w=>w.nodeType==1&&!Fc.test(w.nodeName))&&(!p||p.endA>=p.endB)&&n.someProp("handleKeyDown",w=>w(n,Ve(13,"Enter")))){n.input.lastIOSEnter=0;return}if(!p)if(r&&a instanceof T&&!a.empty&&a.$head.sameParent(a.$anchor)&&!n.composing&&!(c.sel&&c.sel.anchor!=c.sel.head))p={start:a.from,endA:a.to,endB:a.to};else{if(c.sel){let w=Hs(n,n.state.doc,c.sel);if(w&&!w.eq(n.state.selection)){let O=n.state.tr.setSelection(w);s&&O.setMeta("composition",s),n.dispatch(O)}}return}n.state.selection.fromn.state.selection.from&&p.start<=n.state.selection.from+2&&n.state.selection.from>=c.from?p.start=n.state.selection.from:p.endA=n.state.selection.to-2&&n.state.selection.to<=c.to&&(p.endB+=n.state.selection.to-p.endA,p.endA=n.state.selection.to)),Z&&ve<=11&&p.endB==p.start+1&&p.endA==p.start&&p.start>c.from&&c.doc.textBetween(p.start-c.from-1,p.start-c.from+1)==" \xA0"&&(p.start--,p.endA--,p.endB--);let m=c.doc.resolveNoCache(p.start-c.from),g=c.doc.resolveNoCache(p.endB-c.from),y=f.resolve(p.start),S=m.sameParent(g)&&m.parent.inlineContent&&y.end()>=p.endA;if((lt&&n.input.lastIOSEnter>Date.now()-225&&(!S||i.some(w=>w.nodeName=="DIV"||w.nodeName=="P"))||!S&&m.posw(n,Ve(13,"Enter")))){n.input.lastIOSEnter=0;return}if(n.state.selection.anchor>p.start&&$c(f,p.start,p.endA,m,g)&&n.someProp("handleKeyDown",w=>w(n,Ve(8,"Backspace")))){xe&&J&&n.domObserver.suppressSelectionUpdates();return}J&&p.endB==p.start&&(n.input.lastChromeDelete=Date.now()),xe&&!S&&m.start()!=g.start()&&g.parentOffset==0&&m.depth==g.depth&&c.sel&&c.sel.anchor==c.sel.head&&c.sel.head==p.endA&&(p.endB-=2,g=c.doc.resolveNoCache(p.endB-c.from),setTimeout(()=>{n.someProp("handleKeyDown",function(w){return w(n,Ve(13,"Enter"))})},20));let k=p.start,v=p.endA,N=w=>{let O=w||n.state.tr.replace(k,v,c.doc.slice(p.start-c.from,p.endB-c.from));if(c.sel){let R=Hs(n,O.doc,c.sel);R&&!(J&&n.composing&&R.empty&&(p.start!=p.endB||n.input.lastChromeDeleteke(n),20));let w=N(n.state.tr.delete(k,v)),O=f.resolve(p.start).marksAcross(f.resolve(p.endA));O&&w.ensureMarks(O),n.dispatch(w)}else if(p.endA==p.endB&&(D=Vc(m.parent.content.cut(m.parentOffset,g.parentOffset),y.parent.content.cut(y.parentOffset,p.endA-y.start())))){let w=N(n.state.tr);D.type=="add"?w.addMark(k,v,D.mark):w.removeMark(k,v,D.mark),n.dispatch(w)}else if(m.parent.child(m.index()).isText&&m.index()==g.index()-(g.textOffset?0:1)){let w=m.parent.textBetween(m.parentOffset,g.parentOffset),O=()=>N(n.state.tr.insertText(w,k,v));n.someProp("handleTextInput",R=>R(n,k,v,w,O))||n.dispatch(O())}else n.dispatch(N());else n.dispatch(N())}function Hs(n,e,t){return Math.max(t.anchor,t.head)>e.content.size?null:Br(n,e.resolve(t.anchor),e.resolve(t.head))}function Vc(n,e){let t=n.firstChild.marks,r=e.firstChild.marks,i=t,s=r,o,l,a;for(let f=0;ff.mark(l.addToSet(f.marks));else if(i.length==0&&s.length==1)l=s[0],o="remove",a=f=>f.mark(l.removeFromSet(f.marks));else return null;let c=[];for(let f=0;ft||hr(o,!0,!1)0&&(e||n.indexAfter(r)==n.node(r).childCount);)r--,i++,e=!1;if(t){let s=n.node(r).maybeChild(n.indexAfter(r));for(;s&&!s.isLeaf;)s=s.firstChild,i++}return i}function Wc(n,e,t,r,i){let s=n.findDiffStart(e,t),o=t+n.size,l=t+e.size;if(s==null)return null;let{a,b:c}=n.findDiffEnd(e,o,l);if(i=="end"){let f=Math.max(0,s-Math.min(a,c));r-=a+f-s}if(a=a?s-r:0;s-=f,c=s+(c-a),a=s}else if(c=c?s-r:0;s-=f,a=s+(a-c),c=s}return{start:s,endA:a,endB:c}}var Ot=class{constructor(e,t){this._root=null,this.focused=!1,this.trackWrites=null,this.mounted=!1,this.markCursor=null,this.cursorWrapper=null,this.lastSelectedViewDesc=void 0,this.input=new Er,this.prevDirectPlugins=[],this.pluginViews=[],this.requiresGeckoHackNode=!1,this.dragging=null,this._props=t,this.state=t.state,this.directPlugins=t.plugins||[],this.directPlugins.forEach(qs),this.dispatch=this.dispatch.bind(this),this.dom=e&&e.mount||document.createElement("div"),e&&(e.appendChild?e.appendChild(this.dom):typeof e=="function"?e(this.dom):e.mount&&(this.mounted=!0)),this.editable=_s(this),js(this),this.nodeViews=Ks(this),this.docView=Ts(this.state.doc,Js(this),ur(this),this.dom,this),this.domObserver=new Pr(this,(r,i,s,o)=>Lc(this,r,i,s,o)),this.domObserver.start(),ac(this),this.updatePluginViews()}get composing(){return this.input.composing}get props(){if(this._props.state!=this.state){let e=this._props;this._props={};for(let t in e)this._props[t]=e[t];this._props.state=this.state}return this._props}update(e){e.handleDOMEvents!=this._props.handleDOMEvents&&vr(this);let t=this._props;this._props=e,e.plugins&&(e.plugins.forEach(qs),this.directPlugins=e.plugins),this.updateStateInner(e.state,t)}setProps(e){let t={};for(let r in this._props)t[r]=this._props[r];t.state=this.state;for(let r in e)t[r]=e[r];this.update(t)}updateState(e){this.updateStateInner(e,this._props)}updateStateInner(e,t){var r;let i=this.state,s=!1,o=!1;e.storedMarks&&this.composing&&(ko(this),o=!0),this.state=e;let l=i.plugins!=e.plugins||this._props.plugins!=t.plugins;if(l||this._props.plugins!=t.plugins||this._props.nodeViews!=t.nodeViews){let h=Ks(this);Jc(h,this.nodeViews)&&(this.nodeViews=h,s=!0)}(l||t.handleDOMEvents!=this._props.handleDOMEvents)&&vr(this),this.editable=_s(this),js(this);let a=ur(this),c=Js(this),f=i.plugins!=e.plugins&&!i.doc.eq(e.doc)?"reset":e.scrollToSelection>i.scrollToSelection?"to selection":"preserve",d=s||!this.docView.matchesNode(e.doc,c,a);(d||!e.selection.eq(i.selection))&&(o=!0);let u=f=="preserve"&&o&&this.dom.style.overflowAnchor==null&&Na(this);if(o){this.domObserver.stop();let h=d&&(Z||J)&&!this.composing&&!i.selection.empty&&!e.selection.empty&&Hc(i.selection,e.selection);if(d){let m=J?this.trackWrites=this.domSelectionRange().focusNode:null;this.composing&&(this.input.compositionNode=kc(this)),(s||!this.docView.update(e.doc,c,a,this))&&(this.docView.updateOuterDeco(c),this.docView.destroy(),this.docView=Ts(e.doc,c,a,this.dom,this)),m&&(!this.trackWrites||!this.dom.contains(this.trackWrites))&&(h=!0)}let p=this.input.mouseDown;h||!(p&&this.domObserver.currentSelection.eq(this.domSelectionRange())&&qa(this)&&p.delaySelUpdate())?ke(this,h):(lo(this,e.selection),this.domObserver.setCurSelection()),this.domObserver.start()}this.updatePluginViews(i),!((r=this.dragging)===null||r===void 0)&&r.node&&!i.doc.eq(e.doc)&&this.updateDraggedNode(this.dragging,i),f=="reset"?this.dom.scrollTop=0:f=="to selection"?this.scrollToSelection():u&&Ea(u)}scrollToSelection(){let e=this.domSelectionRange().focusNode;if(!(!e||!this.dom.contains(e.nodeType==1?e:e.parentNode))){if(!this.someProp("handleScrollToSelection",t=>t(this)))if(this.state.selection instanceof C){let t=this.docView.domAfterPos(this.state.selection.from);t.nodeType==1&&Ss(this,t.getBoundingClientRect(),e)}else Ss(this,this.coordsAtPos(this.state.selection.head,1),e)}}destroyPluginViews(){let e;for(;e=this.pluginViews.pop();)e.destroy&&e.destroy()}updatePluginViews(e){if(!e||e.plugins!=this.state.plugins||this.directPlugins!=this.prevDirectPlugins){this.prevDirectPlugins=this.directPlugins,this.destroyPluginViews();for(let t=0;t0&&st.ownerDocument.getSelection()),this._root=t}return e||document}updateRoot(){this._root=null}posAtCoords(e){return Pa(this,e)}coordsAtPos(e,t=1){return to(this,e,t)}domAtPos(e,t=0){return this.docView.domFromPos(e,t)}nodeDOM(e){let t=this.docView.descAt(e);return t?t.nodeDOM:null}posAtDOM(e,t,r=-1){let i=this.docView.posFromDOM(e,t,r);if(i==null)throw new RangeError("DOM position not inside the editor");return i}endOfTextblock(e,t){return La(this,t||this.state,e)}pasteHTML(e,t){return Et(this,"",e,!1,t||new ClipboardEvent("paste"))}pasteText(e,t){return Et(this,e,null,!0,t||new ClipboardEvent("paste"))}serializeForClipboard(e){return Fr(this,e)}destroy(){this.docView&&(cc(this),this.destroyPluginViews(),this.mounted?(this.docView.update(this.state.doc,[],ur(this),this),this.dom.textContent=""):this.dom.parentNode&&this.dom.parentNode.removeChild(this.dom),this.docView.destroy(),this.docView=null,ga())}get isDestroyed(){return this.docView==null}dispatchEvent(e){return dc(this,e)}domSelectionRange(){let e=this.domSelection();return e?K&&this.root.nodeType===11&&ka(this.dom.ownerDocument)==this.dom&&Rc(this,e)||e:{focusNode:null,focusOffset:0,anchorNode:null,anchorOffset:0}}domSelection(){return this.root.getSelection()}};Ot.prototype.dispatch=function(n){let e=this._props.dispatchTransaction;e?e.call(this,n):this.updateState(this.state.apply(n))};function Js(n){let e=Object.create(null);return e.class="ProseMirror",e.contenteditable=String(n.editable),n.someProp("attributes",t=>{if(typeof t=="function"&&(t=t(n.state)),t)for(let r in t)r=="class"?e.class+=" "+t[r]:r=="style"?e.style=(e.style?e.style+";":"")+t[r]:!e[r]&&r!="contenteditable"&&r!="nodeName"&&(e[r]=String(t[r]))}),e.translate||(e.translate="no"),[Ae.node(0,n.state.doc.content.size,e)]}function js(n){if(n.markCursor){let e=document.createElement("img");e.className="ProseMirror-separator",e.setAttribute("mark-placeholder","true"),e.setAttribute("alt",""),n.cursorWrapper={dom:e,deco:Ae.widget(n.state.selection.from,e,{raw:!0,marks:n.markCursor})}}else n.cursorWrapper=null}function _s(n){return!n.someProp("editable",e=>e(n.state)===!1)}function Hc(n,e){let t=Math.min(n.$anchor.sharedDepth(n.head),e.$anchor.sharedDepth(e.head));return n.$anchor.start(t)!=e.$anchor.start(t)}function Ks(n){let e=Object.create(null);function t(r){for(let i in r)Object.prototype.hasOwnProperty.call(e,i)||(e[i]=r[i])}return n.someProp("nodeViews",t),n.someProp("markViews",t),e}function Jc(n,e){let t=0,r=0;for(let i in n){if(n[i]!=e[i])return!0;t++}for(let i in e)r++;return t!=r}function qs(n){if(n.spec.state||n.spec.filterTransaction||n.spec.appendTransaction)throw new RangeError("Plugins passed directly to the view must not have a state component")}var Me={8:"Backspace",9:"Tab",10:"Enter",12:"NumLock",13:"Enter",16:"Shift",17:"Control",18:"Alt",20:"CapsLock",27:"Escape",32:" ",33:"PageUp",34:"PageDown",35:"End",36:"Home",37:"ArrowLeft",38:"ArrowUp",39:"ArrowRight",40:"ArrowDown",44:"PrintScreen",45:"Insert",46:"Delete",59:";",61:"=",91:"Meta",92:"Meta",106:"*",107:"+",108:",",109:"-",110:".",111:"/",144:"NumLock",145:"ScrollLock",160:"Shift",161:"Shift",162:"Control",163:"Control",164:"Alt",165:"Alt",173:"-",186:";",187:"=",188:",",189:"-",190:".",191:"/",192:"`",219:"[",220:"\\",221:"]",222:"'"},un={48:")",49:"!",50:"@",51:"#",52:"$",53:"%",54:"^",55:"&",56:"*",57:"(",59:":",61:"+",173:"_",186:":",187:"+",188:"<",189:"_",190:">",191:"?",192:"~",219:"{",220:"|",221:"}",222:'"'},jc=typeof navigator<"u"&&/Mac/.test(navigator.platform),_c=typeof navigator<"u"&&/MSIE \d|Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(navigator.userAgent);for(B=0;B<10;B++)Me[48+B]=Me[96+B]=String(B);var B;for(B=1;B<=24;B++)Me[B+111]="F"+B;var B;for(B=65;B<=90;B++)Me[B]=String.fromCharCode(B+32),un[B]=String.fromCharCode(B);var B;for(dn in Me)un.hasOwnProperty(dn)||(un[dn]=Me[dn]);var dn;function Eo(n){var e=jc&&n.metaKey&&n.shiftKey&&!n.ctrlKey&&!n.altKey||_c&&n.shiftKey&&n.key&&n.key.length==1||n.key=="Unidentified",t=!e&&n.key||(n.shiftKey?un:Me)[n.keyCode]||n.key||"Unidentified";return t=="Esc"&&(t="Escape"),t=="Del"&&(t="Delete"),t=="Left"&&(t="ArrowLeft"),t=="Up"&&(t="ArrowUp"),t=="Right"&&(t="ArrowRight"),t=="Down"&&(t="ArrowDown"),t}var Kc=typeof navigator<"u"&&/Mac|iP(hone|[oa]d)/.test(navigator.platform),qc=typeof navigator<"u"&&/Win/.test(navigator.platform);function Uc(n){let e=n.split(/-(?!$)/),t=e[e.length-1];t=="Space"&&(t=" ");let r,i,s,o;for(let l=0;l{for(var t in e)Xc(n,t,{get:e[t],enumerable:!0})};function kn(n){let{state:e,transaction:t}=n,{selection:r}=t,{doc:i}=t,{storedMarks:s}=t;return{...e,apply:e.apply.bind(e),applyTransaction:e.applyTransaction.bind(e),plugins:e.plugins,schema:e.schema,reconfigure:e.reconfigure.bind(e),toJSON:e.toJSON.bind(e),get storedMarks(){return s},get selection(){return r},get doc(){return i},get tr(){return r=t.selection,i=t.doc,s=t.storedMarks,t}}}var Mn=class{constructor(n){this.editor=n.editor,this.rawCommands=this.editor.extensionManager.commands,this.customState=n.state}get hasCustomState(){return!!this.customState}get state(){return this.customState||this.editor.state}get commands(){let{rawCommands:n,editor:e,state:t}=this,{view:r}=e,{tr:i}=t,s=this.buildProps(i);return Object.fromEntries(Object.entries(n).map(([o,l])=>[o,(...c)=>{let f=l(...c)(s);return!i.getMeta("preventDispatch")&&!this.hasCustomState&&r.dispatch(i),f}]))}get chain(){return()=>this.createChain()}get can(){return()=>this.createCan()}createChain(n,e=!0){let{rawCommands:t,editor:r,state:i}=this,{view:s}=r,o=[],l=!!n,a=n||i.tr,c=()=>(!l&&e&&!a.getMeta("preventDispatch")&&!this.hasCustomState&&s.dispatch(a),o.every(d=>d===!0)),f={...Object.fromEntries(Object.entries(t).map(([d,u])=>[d,(...p)=>{let m=this.buildProps(a,e),g=u(...p)(m);return o.push(g),f}])),run:c};return f}createCan(n){let{rawCommands:e,state:t}=this,r=!1,i=n||t.tr,s=this.buildProps(i,r);return{...Object.fromEntries(Object.entries(e).map(([l,a])=>[l,(...c)=>a(...c)({...s,dispatch:void 0})])),chain:()=>this.createChain(i,r)}}buildProps(n,e=!0){let{rawCommands:t,editor:r,state:i}=this,{view:s}=r,o={tr:n,editor:r,view:s,state:kn({state:i,transaction:n}),dispatch:e?()=>{}:void 0,chain:()=>this.createChain(n,e),can:()=>this.createCan(n),get commands(){return Object.fromEntries(Object.entries(t).map(([l,a])=>[l,(...c)=>a(...c)(o)]))}};return o}},$o={};Yr($o,{blur:()=>Qc,clearContent:()=>Zc,clearNodes:()=>ef,command:()=>tf,createParagraphNear:()=>nf,cut:()=>rf,deleteCurrentNode:()=>sf,deleteNode:()=>of,deleteRange:()=>lf,deleteSelection:()=>ff,enter:()=>df,exitCode:()=>uf,extendMarkRange:()=>hf,first:()=>pf,focus:()=>gf,forEach:()=>yf,insertContent:()=>bf,insertContentAt:()=>Sf,insertDefaultBlock:()=>kf,joinBackward:()=>Cf,joinDown:()=>wf,joinForward:()=>Tf,joinItemBackward:()=>Nf,joinItemForward:()=>Ef,joinTextblockBackward:()=>vf,joinTextblockForward:()=>Of,joinUp:()=>Mf,keyboardShortcut:()=>Df,lift:()=>Rf,liftEmptyBlock:()=>Pf,liftListItem:()=>If,newlineInCode:()=>zf,resetAttributes:()=>Bf,scrollIntoView:()=>Ff,selectAll:()=>Lf,selectNodeBackward:()=>Vf,selectNodeForward:()=>$f,selectParentNode:()=>Wf,selectTextblockEnd:()=>Hf,selectTextblockStart:()=>Jf,setContent:()=>jf,setMark:()=>fd,setMeta:()=>dd,setNode:()=>ud,setNodeSelection:()=>hd,setTextDirection:()=>pd,setTextSelection:()=>md,sinkListItem:()=>gd,splitBlock:()=>yd,splitListItem:()=>bd,toggleList:()=>Sd,toggleMark:()=>kd,toggleNode:()=>Md,toggleWrap:()=>wd,undoInputRule:()=>Cd,unsetAllMarks:()=>Td,unsetMark:()=>Nd,unsetTextDirection:()=>Ed,updateAttributes:()=>vd,wrapIn:()=>Od,wrapInList:()=>Ad});var Qc=()=>({editor:n,view:e})=>(requestAnimationFrame(()=>{var t;n.isDestroyed||(e.dom.blur(),(t=window?.getSelection())==null||t.removeAllRanges())}),!0),Zc=(n=!0)=>({commands:e})=>e.setContent("",{emitUpdate:n}),ef=()=>({state:n,tr:e,dispatch:t})=>{let{selection:r}=e,{ranges:i}=r;return t&&i.forEach(({$from:s,$to:o})=>{n.doc.nodesBetween(s.pos,o.pos,(l,a)=>{if(l.type.isText)return;let{doc:c,mapping:f}=e,d=c.resolve(f.map(a)),u=c.resolve(f.map(a+l.nodeSize)),h=d.blockRange(u);if(!h)return;let p=ge(h);if(l.type.isTextblock){let{defaultType:m}=d.parent.contentMatchAt(d.index());e.setNodeMarkup(h.start,m)}(p||p===0)&&e.lift(h,p)})}),!0},tf=n=>e=>n(e),nf=()=>({state:n,dispatch:e})=>rr(n,e),rf=(n,e)=>({editor:t,tr:r})=>{let{state:i}=t,s=i.doc.slice(n.from,n.to);r.deleteRange(n.from,n.to);let o=r.mapping.map(e);return r.insert(o,s.content),r.setSelection(new T(r.doc.resolve(Math.max(o-1,0)))),!0},sf=()=>({tr:n,dispatch:e})=>{let{selection:t}=n,r=t.$anchor.node();if(r.content.size>0)return!1;let i=n.selection.$anchor;for(let s=i.depth;s>0;s-=1)if(i.node(s).type===r.type){if(e){let l=i.before(s),a=i.after(s);n.delete(l,a).scrollIntoView()}return!0}return!1};function V(n,e){if(typeof n=="string"){if(!e.nodes[n])throw Error(`There is no node type named '${n}'. Maybe you forgot to add the extension?`);return e.nodes[n]}return n}var of=n=>({tr:e,state:t,dispatch:r})=>{let i=V(n,t.schema),s=e.selection.$anchor;for(let o=s.depth;o>0;o-=1)if(s.node(o).type===i){if(r){let a=s.before(o),c=s.after(o);e.delete(a,c).scrollIntoView()}return!0}return!1},lf=n=>({tr:e,dispatch:t})=>{let{from:r,to:i}=n;return t&&e.delete(r,i),!0},af=n=>n.content?/^text(\*|\+)/.test(n.content):!1,Oo=(n,e,t)=>{if(!n.parent.isInline||t==="left"&&n.pos>n.start()||t==="right"&&n.pos{let r=Oo(n,t,"left"),i=Oo(e,t,"right");return{from:r,to:i}},ff=()=>({state:n,dispatch:e})=>{if(n.selection.empty)return!1;if(e){let t=n.tr,{ranges:r}=n.selection,i=t.steps.length;r.forEach(s=>{let o=t.mapping.slice(i),l=t.doc.resolve(o.map(s.$from.pos)),a=t.doc.resolve(o.map(s.$to.pos)),{from:c,to:f}=cf(l,a,n.schema);t.deleteRange(c,f)}),t.selection.empty||t.setSelection(T.near(t.doc.resolve(t.selection.from))),t.scrollIntoView(),e(t)}return!0},df=()=>({commands:n})=>n.keyboardShortcut("Enter"),uf=()=>({state:n,dispatch:e})=>nr(n,e);function Gr(n){return Object.prototype.toString.call(n)==="[object RegExp]"}function bn(n,e,t={strict:!0}){let r=Object.keys(e);return r.length?r.every(i=>t.strict?e[i]===n[i]:Gr(e[i])?e[i].test(n[i]):e[i]===n[i]):!0}function Wo(n,e,t={}){return n.find(r=>r.type===e&&bn(Object.fromEntries(Object.keys(t).map(i=>[i,r.attrs[i]])),t))}function Ao(n,e,t={}){return!!Wo(n,e,t)}function Xr(n,e,t){if(!n||!e)return;let r=n.parent.childAfter(n.parentOffset);if((!r.node||!r.node.marks.some(c=>c.type===e))&&(r=n.parent.childBefore(n.parentOffset)),!r.node||!r.node.marks.some(c=>c.type===e))return;if(!t){let c=r.node.marks.find(f=>f.type===e);c&&(t=c.attrs)}if(!Wo([...r.node.marks],e,t))return;let s=r.index,o=n.start()+r.offset,l=s+1,a=o+r.node.nodeSize;for(;s>0&&Ao([...n.parent.child(s-1).marks],e,t);)s-=1,o-=n.parent.child(s).nodeSize;for(;l({tr:t,state:r,dispatch:i})=>{let s=we(n,r.schema),{doc:o,selection:l}=t,{$from:a,from:c,to:f}=l;if(i){let d=Xr(a,s,e);if(d&&d.from<=c&&d.to>=f){let u=T.create(o,d.from,d.to);t.setSelection(u)}}return!0},pf=n=>e=>{let t=typeof n=="function"?n(e):n;for(let r=0;r({editor:t,view:r,tr:i,dispatch:s})=>{e={scrollIntoView:!0,...e};let o=()=>{(xn()||Do())&&r.dom.focus(),mf()&&!xn()&&!Do()&&r.dom.focus({preventScroll:!0}),requestAnimationFrame(()=>{t.isDestroyed||(r.focus(),e?.scrollIntoView&&t.commands.scrollIntoView())})};try{if(r.hasFocus()&&n===null||n===!1)return!0}catch{return!1}if(s&&n===null&&!Ho(t.state.selection))return o(),!0;let l=Kr(i.doc,n)||t.state.selection,a=t.state.selection.eq(l);return s&&(a||i.setSelection(l),a&&i.storedMarks&&i.setStoredMarks(i.storedMarks),o()),!0},yf=(n,e)=>t=>n.every((r,i)=>e(r,{...t,index:i})),bf=(n,e)=>({tr:t,commands:r})=>r.insertContentAt({from:t.selection.from,to:t.selection.to},n,e),Jo=n=>{let e=n.childNodes;for(let t=e.length-1;t>=0;t-=1){let r=e[t];r.nodeType===3&&r.nodeValue&&/^(\n\s\s|\n)$/.test(r.nodeValue)?n.removeChild(r):r.nodeType===1&&Jo(r)}return n};function hn(n){if(typeof window>"u")throw new Error("[tiptap error]: there is no window object available, so this function cannot be used");let e=`${n}`,t=new window.DOMParser().parseFromString(e,"text/html").body;return Jo(t)}function at(n,e,t){if(n instanceof Q||n instanceof b)return n;t={slice:!0,parseOptions:{},...t};let r=typeof n=="object"&&n!==null,i=typeof n=="string";if(r)try{if(Array.isArray(n)&&n.length>0)return b.fromArray(n.map(l=>e.nodeFromJSON(l)));let o=e.nodeFromJSON(n);return t.errorOnInvalidContent&&o.check(),o}catch(s){if(t.errorOnInvalidContent)throw new Error("[tiptap error]: Invalid JSON content",{cause:s});return console.warn("[tiptap warn]: Invalid content.","Passed value:",n,"Error:",s),at("",e,t)}if(i){if(t.errorOnInvalidContent){let o=!1,l="",a=new Ge({topNode:e.spec.topNode,marks:e.spec.marks,nodes:e.spec.nodes.append({__tiptap__private__unknown__catch__all__node:{content:"inline*",group:"block",parseDOM:[{tag:"*",getAttrs:c=>(o=!0,l=typeof c=="string"?c:c.outerHTML,null)}]}})});if(t.slice?fe.fromSchema(a).parseSlice(hn(n),t.parseOptions):fe.fromSchema(a).parse(hn(n),t.parseOptions),t.errorOnInvalidContent&&o)throw new Error("[tiptap error]: Invalid HTML content",{cause:new Error(`Invalid element found: ${l}`)})}let s=fe.fromSchema(e);return t.slice?s.parseSlice(hn(n),t.parseOptions).content:s.parse(hn(n),t.parseOptions)}return at("",e,t)}function jo(n,e,t){let r=n.steps.length-1;if(r{o===0&&(o=f)}),n.setSelection(E.near(n.doc.resolve(o),t))}var xf=n=>!("type"in n),Sf=(n,e,t)=>({tr:r,dispatch:i,editor:s})=>{var o;if(i){t={parseOptions:s.options.parseOptions,updateSelection:!0,applyInputRules:!1,applyPasteRules:!1,...t};let l,a=g=>{s.emit("contentError",{editor:s,error:g,disableCollaboration:()=>{"collaboration"in s.storage&&typeof s.storage.collaboration=="object"&&s.storage.collaboration&&(s.storage.collaboration.isDisabled=!0)}})},c={preserveWhitespace:"full",...t.parseOptions};if(!t.errorOnInvalidContent&&!s.options.enableContentCheck&&s.options.emitContentError)try{at(e,s.schema,{parseOptions:c,errorOnInvalidContent:!0})}catch(g){a(g)}try{l=at(e,s.schema,{parseOptions:c,errorOnInvalidContent:(o=t.errorOnInvalidContent)!=null?o:s.options.enableContentCheck})}catch(g){return a(g),!1}let{from:f,to:d}=typeof n=="number"?{from:n,to:n}:{from:n.from,to:n.to},u=!0,h=!0;if((xf(l)?l:[l]).forEach(g=>{g.check(),u=u?g.isText&&g.marks.length===0:!1,h=h?g.isBlock:!1}),f===d&&h){let{parent:g}=r.doc.resolve(f);g.isTextblock&&!g.type.spec.code&&!g.childCount&&(f-=1,d+=1)}let m;if(u){if(Array.isArray(e))m=e.map(g=>g.text||"").join("");else if(e instanceof b){let g="";e.forEach(y=>{y.text&&(g+=y.text)}),m=g}else typeof e=="object"&&e&&e.text?m=e.text:m=e;r.insertText(m,f,d)}else{m=l;let g=r.doc.resolve(f),y=g.node(),S=g.parentOffset===0,k=y.isText||y.isTextblock,v=y.content.size>0;S&&k&&v&&h&&(f=Math.max(0,f-1)),r.replaceWith(f,d,m)}t.updateSelection&&jo(r,r.steps.length-1,-1),t.applyInputRules&&r.setMeta("applyInputRules",{from:f,text:m}),t.applyPasteRules&&r.setMeta("applyPasteRules",{from:f,text:m})}return!0};function _o(n){for(let e=0;e({tr:e,dispatch:t,editor:r})=>{let{pos:i,attrs:s,content:o,updateSelection:l=!0}=n,a;typeof i=="number"?a=e.doc.resolve(i):i?a=i:a=e.selection.$from;let c=_o(a.parent.contentMatchAt(a.index()));if(!c)return!1;let f=Object.keys(c.spec.attrs||{}),d=s?Object.fromEntries(Object.entries(s).filter(([h])=>f.includes(h))):{},u;if(o){let h=at(o,r.schema);u=c.createAndFill(d,h)}else u=c.createAndFill(d);return u?(t&&(e.insert(a.pos,u),l&&jo(e,e.steps.length-1,-1)),!0):!1},Mf=()=>({state:n,dispatch:e})=>as(n,e),wf=()=>({state:n,dispatch:e})=>cs(n,e),Cf=()=>({state:n,dispatch:e})=>Un(n,e),Tf=()=>({state:n,dispatch:e})=>Xn(n,e),Nf=()=>({state:n,dispatch:e,tr:t})=>{try{let r=Fe(n.doc,n.selection.$from.pos,-1);return r==null?!1:(t.join(r,2),e&&e(t),!0)}catch{return!1}},Ef=()=>({state:n,dispatch:e,tr:t})=>{try{let r=Fe(n.doc,n.selection.$from.pos,1);return r==null?!1:(t.join(r,2),e&&e(t),!0)}catch{return!1}},vf=()=>({state:n,dispatch:e})=>rs(n,e),Of=()=>({state:n,dispatch:e})=>is(n,e);function Ko(){return typeof navigator<"u"?/Mac/.test(navigator.platform):!1}function Af(n){let e=n.split(/-(?!$)/),t=e[e.length-1];t==="Space"&&(t=" ");let r,i,s,o;for(let l=0;l({editor:e,view:t,tr:r,dispatch:i})=>{let s=Af(n).split(/-(?!$)/),o=s.find(c=>!["Alt","Ctrl","Meta","Shift"].includes(c)),l=new KeyboardEvent("keydown",{key:o==="Space"?" ":o,altKey:s.includes("Alt"),ctrlKey:s.includes("Ctrl"),metaKey:s.includes("Meta"),shiftKey:s.includes("Shift"),bubbles:!0,cancelable:!0}),a=e.captureTransaction(()=>{t.someProp("handleKeyDown",c=>c(t,l))});return a?.steps.forEach(c=>{let f=c.map(r.mapping);f&&i&&r.maybeStep(f)}),!0};function Ft(n,e,t={}){let{from:r,to:i,empty:s}=n.selection,o=e?V(e,n.schema):null,l=[];n.doc.nodesBetween(r,i,(d,u)=>{if(d.isText)return;let h=Math.max(r,u),p=Math.min(i,u+d.nodeSize);l.push({node:d,from:h,to:p})});let a=i-r,c=l.filter(d=>o?o.name===d.node.type.name:!0).filter(d=>bn(d.node.attrs,t,{strict:!1}));return s?!!c.length:c.reduce((d,u)=>d+u.to-u.from,0)>=a}var Rf=(n,e={})=>({state:t,dispatch:r})=>{let i=V(n,t.schema);return Ft(t,i,e)?fs(t,r):!1},Pf=()=>({state:n,dispatch:e})=>ir(n,e),If=n=>({state:e,dispatch:t})=>{let r=V(n,e.schema);return gs(r)(e,t)},zf=()=>({state:n,dispatch:e})=>er(n,e);function wn(n,e){return e.nodes[n]?"node":e.marks[n]?"mark":null}function Ro(n,e){let t=typeof e=="string"?[e]:e;return Object.keys(n).reduce((r,i)=>(t.includes(i)||(r[i]=n[i]),r),{})}var Bf=(n,e)=>({tr:t,state:r,dispatch:i})=>{let s=null,o=null,l=wn(typeof n=="string"?n:n.name,r.schema);if(!l)return!1;l==="node"&&(s=V(n,r.schema)),l==="mark"&&(o=we(n,r.schema));let a=!1;return t.selection.ranges.forEach(c=>{r.doc.nodesBetween(c.$from.pos,c.$to.pos,(f,d)=>{s&&s===f.type&&(a=!0,i&&t.setNodeMarkup(d,void 0,Ro(f.attrs,e))),o&&f.marks.length&&f.marks.forEach(u=>{o===u.type&&(a=!0,i&&t.addMark(d,d+f.nodeSize,o.create(Ro(u.attrs,e))))})})}),a},Ff=()=>({tr:n,dispatch:e})=>(e&&n.scrollIntoView(),!0),Lf=()=>({tr:n,dispatch:e})=>{if(e){let t=new U(n.doc);n.setSelection(t)}return!0},Vf=()=>({state:n,dispatch:e})=>Yn(n,e),$f=()=>({state:n,dispatch:e})=>Qn(n,e),Wf=()=>({state:n,dispatch:e})=>ds(n,e),Hf=()=>({state:n,dispatch:e})=>or(n,e),Jf=()=>({state:n,dispatch:e})=>sr(n,e);function qr(n,e,t={},r={}){return at(n,e,{slice:!1,parseOptions:t,errorOnInvalidContent:r.errorOnInvalidContent})}var jf=(n,{errorOnInvalidContent:e,emitUpdate:t=!0,parseOptions:r={}}={})=>({editor:i,tr:s,dispatch:o,commands:l})=>{let{doc:a}=s;if(r.preserveWhitespace!=="full"){let c=qr(n,i.schema,r,{errorOnInvalidContent:e??i.options.enableContentCheck});return o&&s.replaceWith(0,a.content.size,c).setMeta("preventUpdate",!t),!0}return o&&s.setMeta("preventUpdate",!t),l.insertContentAt({from:0,to:a.content.size},n,{parseOptions:r,errorOnInvalidContent:e??i.options.enableContentCheck})};function qo(n,e){let t=we(e,n.schema),{from:r,to:i,empty:s}=n.selection,o=[];s?(n.storedMarks&&o.push(...n.storedMarks),o.push(...n.selection.$head.marks())):n.doc.nodesBetween(r,i,a=>{o.push(...a.marks)});let l=o.find(a=>a.type.name===t.name);return l?{...l.attrs}:{}}function _f(n,e){let t=new Ze(n);return e.forEach(r=>{r.steps.forEach(i=>{t.step(i)})}),t}function mh(n,e,t){let r=[];return n.nodesBetween(e.from,e.to,(i,s)=>{t(i)&&r.push({node:i,pos:s})}),r}function Kf(n,e){for(let t=n.depth;t>0;t-=1){let r=n.node(t);if(e(r))return{pos:t>0?n.before(t):0,start:n.start(t),depth:t,node:r}}}function Cn(n){return e=>Kf(e.$from,n)}function M(n,e,t){return n.config[e]===void 0&&n.parent?M(n.parent,e,t):typeof n.config[e]=="function"?n.config[e].bind({...t,parent:n.parent?M(n.parent,e,t):null}):n.config[e]}function Qr(n){return n.map(e=>{let t={name:e.name,options:e.options,storage:e.storage},r=M(e,"addExtensions",t);return r?[e,...Qr(r())]:e}).flat(10)}function Zr(n,e){let t=he.fromSchema(e).serializeFragment(n),i=document.implementation.createHTMLDocument().createElement("div");return i.appendChild(t),i.innerHTML}function Uo(n){return typeof n=="function"}function P(n,e=void 0,...t){return Uo(n)?e?n.bind(e)(...t):n(...t):n}function qf(n={}){return Object.keys(n).length===0&&n.constructor===Object}function ct(n){let e=n.filter(i=>i.type==="extension"),t=n.filter(i=>i.type==="node"),r=n.filter(i=>i.type==="mark");return{baseExtensions:e,nodeExtensions:t,markExtensions:r}}function Yo(n){let e=[],{nodeExtensions:t,markExtensions:r}=ct(n),i=[...t,...r],s={default:null,validate:void 0,rendered:!0,renderHTML:null,parseHTML:null,keepOnSplit:!0,isRequired:!1},o=t.filter(c=>c.name!=="text").map(c=>c.name),l=r.map(c=>c.name),a=[...o,...l];return n.forEach(c=>{let f={name:c.name,options:c.options,storage:c.storage,extensions:i},d=M(c,"addGlobalAttributes",f);if(!d)return;d().forEach(h=>{let p;Array.isArray(h.types)?p=h.types:h.types==="*"?p=a:h.types==="nodes"?p=o:h.types==="marks"?p=l:p=[],p.forEach(m=>{Object.entries(h.attributes).forEach(([g,y])=>{e.push({type:m,name:g,attribute:{...s,...y}})})})})}),i.forEach(c=>{let f={name:c.name,options:c.options,storage:c.storage},d=M(c,"addAttributes",f);if(!d)return;let u=d();Object.entries(u).forEach(([h,p])=>{let m={...s,...p};typeof m?.default=="function"&&(m.default=m.default()),m?.isRequired&&m?.default===void 0&&delete m.default,e.push({type:c.name,name:h,attribute:m})})}),e}function Uf(n){let e=[],t="",r=!1,i=!1,s=0,o=n.length;for(let l=0;l0){s-=1,t+=a;continue}if(a===";"&&s===0){e.push(t),t="";continue}}t+=a}return t&&e.push(t),e}function Po(n){let e=[],t=Uf(n||""),r=t.length;for(let i=0;i!!e).reduce((e,t)=>{let r={...e};return Object.entries(t).forEach(([i,s])=>{if(!r[i]){r[i]=s;return}if(i==="class"){let l=s?String(s).split(" "):[],a=r[i]?r[i].split(" "):[],c=l.filter(f=>!a.includes(f));r[i]=[...a,...c].join(" ")}else if(i==="style"){let l=new Map([...Po(r[i]),...Po(s)]);r[i]=Array.from(l.entries()).map(([a,c])=>`${a}: ${c}`).join("; ")}else r[i]=s}),r},{})}function Sn(n,e){return e.filter(t=>t.type===n.type.name).filter(t=>t.attribute.rendered).map(t=>t.attribute.renderHTML?t.attribute.renderHTML(n.attrs)||{}:{[t.name]:n.attrs[t.name]}).reduce((t,r)=>Yf(t,r),{})}function Gf(n){return typeof n!="string"?n:n.match(/^[+-]?(?:\d*\.)?\d+$/)?Number(n):n==="true"?!0:n==="false"?!1:n}function Io(n,e){return"style"in n?n:{...n,getAttrs:t=>{let r=n.getAttrs?n.getAttrs(t):n.attrs;if(r===!1)return!1;let i=e.reduce((s,o)=>{let l=o.attribute.parseHTML?o.attribute.parseHTML(t):Gf(t.getAttribute(o.name));return l==null?s:{...s,[o.name]:l}},{});return{...r,...i}}}}function zo(n){return Object.fromEntries(Object.entries(n).filter(([e,t])=>e==="attrs"&&qf(t)?!1:t!=null))}function Bo(n){var e,t;let r={};return!((e=n?.attribute)!=null&&e.isRequired)&&"default"in(n?.attribute||{})&&(r.default=n.attribute.default),((t=n?.attribute)==null?void 0:t.validate)!==void 0&&(r.validate=n.attribute.validate),[n.name,r]}function Xf(n,e){var t;let r=Yo(n),{nodeExtensions:i,markExtensions:s}=ct(n),o=(t=i.find(c=>M(c,"topNode")))==null?void 0:t.name,l=Object.fromEntries(i.map(c=>{let f=r.filter(y=>y.type===c.name),d={name:c.name,options:c.options,storage:c.storage,editor:e},u=n.reduce((y,S)=>{let k=M(S,"extendNodeSchema",d);return{...y,...k?k(c):{}}},{}),h=zo({...u,content:P(M(c,"content",d)),marks:P(M(c,"marks",d)),group:P(M(c,"group",d)),inline:P(M(c,"inline",d)),atom:P(M(c,"atom",d)),selectable:P(M(c,"selectable",d)),draggable:P(M(c,"draggable",d)),code:P(M(c,"code",d)),whitespace:P(M(c,"whitespace",d)),linebreakReplacement:P(M(c,"linebreakReplacement",d)),defining:P(M(c,"defining",d)),isolating:P(M(c,"isolating",d)),attrs:Object.fromEntries(f.map(Bo))}),p=P(M(c,"parseHTML",d));p&&(h.parseDOM=p.map(y=>Io(y,f)));let m=M(c,"renderHTML",d);m&&(h.toDOM=y=>m({node:y,HTMLAttributes:Sn(y,f)}));let g=M(c,"renderText",d);return g&&(h.toText=g),[c.name,h]})),a=Object.fromEntries(s.map(c=>{let f=r.filter(g=>g.type===c.name),d={name:c.name,options:c.options,storage:c.storage,editor:e},u=n.reduce((g,y)=>{let S=M(y,"extendMarkSchema",d);return{...g,...S?S(c):{}}},{}),h=zo({...u,inclusive:P(M(c,"inclusive",d)),excludes:P(M(c,"excludes",d)),group:P(M(c,"group",d)),spanning:P(M(c,"spanning",d)),code:P(M(c,"code",d)),attrs:Object.fromEntries(f.map(Bo))}),p=P(M(c,"parseHTML",d));p&&(h.parseDOM=p.map(g=>Io(g,f)));let m=M(c,"renderHTML",d);return m&&(h.toDOM=g=>m({mark:g,HTMLAttributes:Sn(g,f)})),[c.name,h]}));return new Ge({topNode:o,nodes:l,marks:a})}function Qf(n){let e=n.filter((t,r)=>n.indexOf(t)!==r);return Array.from(new Set(e))}function Bt(n){return n.sort((t,r)=>{let i=M(t,"priority")||100,s=M(r,"priority")||100;return i>s?-1:ir.name));return t.length&&console.warn(`[tiptap warn]: Duplicate extension names found: [${t.map(r=>`'${r}'`).join(", ")}]. This can lead to issues.`),e}function Xo(n,e,t){let{from:r,to:i}=e,{blockSeparator:s=`
+
+`,textSerializers:o={}}=t||{},l="";return n.nodesBetween(r,i,(a,c,f,d)=>{var u;a.isBlock&&c>r&&(l+=s);let h=o?.[a.type.name];if(h)return f&&(l+=h({node:a,pos:c,parent:f,index:d,range:e})),!1;a.isText&&(l+=(u=a?.text)==null?void 0:u.slice(Math.max(r,c)-c,i-c))}),l}function Zf(n,e){let t={from:0,to:n.content.size};return Xo(n,t,e)}function Qo(n){return Object.fromEntries(Object.entries(n.nodes).filter(([,e])=>e.spec.toText).map(([e,t])=>[e,t.spec.toText]))}function ed(n,e){let t=V(e,n.schema),{from:r,to:i}=n.selection,s=[];n.doc.nodesBetween(r,i,l=>{s.push(l)});let o=s.reverse().find(l=>l.type.name===t.name);return o?{...o.attrs}:{}}function td(n,e){let t=wn(typeof e=="string"?e:e.name,n.schema);return t==="node"?ed(n,e):t==="mark"?qo(n,e):{}}function nd(n,e=JSON.stringify){let t={};return n.filter(r=>{let i=e(r);return Object.prototype.hasOwnProperty.call(t,i)?!1:t[i]=!0})}function rd(n){let e=nd(n);return e.length===1?e:e.filter((t,r)=>!e.filter((s,o)=>o!==r).some(s=>t.oldRange.from>=s.oldRange.from&&t.oldRange.to<=s.oldRange.to&&t.newRange.from>=s.newRange.from&&t.newRange.to<=s.newRange.to))}function id(n){let{mapping:e,steps:t}=n,r=[];return e.maps.forEach((i,s)=>{let o=[];if(i.ranges.length)i.forEach((l,a)=>{o.push({from:l,to:a})});else{let{from:l,to:a}=t[s];if(l===void 0||a===void 0)return;o.push({from:l,to:a})}o.forEach(({from:l,to:a})=>{let c=e.slice(s).map(l,-1),f=e.slice(s).map(a),d=e.invert().map(c,-1),u=e.invert().map(f);r.push({oldRange:{from:d,to:u},newRange:{from:c,to:f}})})}),rd(r)}function Zo(n,e,t){let r=[];return n===e?t.resolve(n).marks().forEach(i=>{let s=t.resolve(n),o=Xr(s,i.type);o&&r.push({mark:i,...o})}):t.nodesBetween(n,e,(i,s)=>{!i||i?.nodeSize===void 0||r.push(...i.marks.map(o=>({from:s,to:s+i.nodeSize,mark:o})))}),r}var kh=(n,e,t,r=20)=>{let i=n.doc.resolve(t),s=r,o=null;for(;s>0&&o===null;){let l=i.node(s);l?.type.name===e?o=l:s-=1}return[o,s]};function It(n,e){return e.nodes[n]||e.marks[n]||null}function yn(n,e,t){return Object.fromEntries(Object.entries(t).filter(([r])=>{let i=n.find(s=>s.type===e&&s.name===r);return i?i.attribute.keepOnSplit:!1}))}var sd=(n,e=500)=>{let t="",r=n.parentOffset;return n.parent.nodesBetween(Math.max(0,r-e),r,(i,s,o,l)=>{var a,c;let f=((c=(a=i.type.spec).toText)==null?void 0:c.call(a,{node:i,pos:s,parent:o,index:l}))||i.textContent||"%leaf%";t+=i.isAtom&&!i.isText?f:f.slice(0,Math.max(0,r-s))}),t};function Ur(n,e,t={}){let{empty:r,ranges:i}=n.selection,s=e?we(e,n.schema):null;if(r)return!!(n.storedMarks||n.selection.$from.marks()).filter(d=>s?s.name===d.type.name:!0).find(d=>bn(d.attrs,t,{strict:!1}));let o=0,l=[];if(i.forEach(({$from:d,$to:u})=>{let h=d.pos,p=u.pos;n.doc.nodesBetween(h,p,(m,g)=>{if(s&&m.inlineContent&&!m.type.allowsMarkType(s))return!1;if(!m.isText&&!m.marks.length)return;let y=Math.max(h,g),S=Math.min(p,g+m.nodeSize),k=S-y;o+=k,l.push(...m.marks.map(v=>({mark:v,from:y,to:S})))})}),o===0)return!1;let a=l.filter(d=>s?s.name===d.mark.type.name:!0).filter(d=>bn(d.mark.attrs,t,{strict:!1})).reduce((d,u)=>d+u.to-u.from,0),c=l.filter(d=>s?d.mark.type!==s&&d.mark.type.excludes(s):!0).reduce((d,u)=>d+u.to-u.from,0);return(a>0?a+c:a)>=o}function od(n,e,t={}){if(!e)return Ft(n,null,t)||Ur(n,null,t);let r=wn(e,n.schema);return r==="node"?Ft(n,e,t):r==="mark"?Ur(n,e,t):!1}var Mh=(n,e)=>{let{$from:t,$to:r,$anchor:i}=n.selection;if(e){let s=Cn(l=>l.type.name===e)(n.selection);if(!s)return!1;let o=n.doc.resolve(s.pos+1);return i.pos+1===o.end()}return!(r.parentOffset{let{$from:e,$to:t}=n.selection;return!(e.parentOffset>0||e.pos!==t.pos)};function Fo(n,e){return Array.isArray(e)?e.some(t=>(typeof t=="string"?t:t.name)===n.name):e}function Jr(n,e){let{nodeExtensions:t}=ct(e),r=t.find(o=>o.name===n);if(!r)return!1;let i={name:r.name,options:r.options,storage:r.storage},s=P(M(r,"group",i));return typeof s!="string"?!1:s.split(" ").includes("list")}function ei(n,{checkChildren:e=!0,ignoreWhitespace:t=!1}={}){var r;if(t){if(n.type.name==="hardBreak")return!0;if(n.isText)return!/\S/.test((r=n.text)!=null?r:"")}if(n.isText)return!n.text;if(n.isAtom||n.isLeaf)return!1;if(n.content.childCount===0)return!0;if(e){let i=!0;return n.content.forEach(s=>{i!==!1&&(ei(s,{ignoreWhitespace:t,checkChildren:e})||(i=!1))}),i}return!1}function Th(n){return n instanceof C}var el=class tl{constructor(e){this.position=e}static fromJSON(e){return new tl(e.position)}toJSON(){return{position:this.position}}};function ld(n,e){let t=e.mapping.mapResult(n.position);return{position:new el(t.pos),mapResult:t}}function ad(n){return new el(n)}function cd(n,e,t){var r;let{selection:i}=e,s=null;if(Ho(i)&&(s=i.$cursor),s){let l=(r=n.storedMarks)!=null?r:s.marks();return s.parent.type.allowsMarkType(t)&&(!!t.isInSet(l)||!l.some(c=>c.type.excludes(t)))}let{ranges:o}=i;return o.some(({$from:l,$to:a})=>{let c=l.depth===0?n.doc.inlineContent&&n.doc.type.allowsMarkType(t):!1;return n.doc.nodesBetween(l.pos,a.pos,(f,d,u)=>{if(c)return!1;if(f.isInline){let h=!u||u.type.allowsMarkType(t),p=!!t.isInSet(f.marks)||!f.marks.some(m=>m.type.excludes(t));c=h&&p}return!c}),c})}var fd=(n,e={})=>({tr:t,state:r,dispatch:i})=>{let{selection:s}=t,{empty:o,ranges:l}=s,a=we(n,r.schema);if(i)if(o){let c=qo(r,a);t.addStoredMark(a.create({...c,...e}))}else l.forEach(c=>{let f=c.$from.pos,d=c.$to.pos;r.doc.nodesBetween(f,d,(u,h)=>{let p=Math.max(h,f),m=Math.min(h+u.nodeSize,d);u.marks.find(y=>y.type===a)?u.marks.forEach(y=>{a===y.type&&t.addMark(p,m,a.create({...y.attrs,...e}))}):t.addMark(p,m,a.create(e))})});return cd(r,t,a)},dd=(n,e)=>({tr:t})=>(t.setMeta(n,e),!0),ud=(n,e={})=>({state:t,dispatch:r,chain:i})=>{let s=V(n,t.schema),o;return t.selection.$anchor.sameParent(t.selection.$head)&&(o=t.selection.$anchor.parent.attrs),s.isTextblock?i().command(({commands:l})=>lr(s,{...o,...e})(t)?!0:l.clearNodes()).command(({state:l})=>lr(s,{...o,...e})(l,r)).run():(console.warn('[tiptap warn]: Currently "setNode()" only supports text block nodes.'),!1)},hd=n=>({tr:e,dispatch:t})=>{if(t){let{doc:r}=e,i=Ue(n,0,r.content.size),s=C.create(r,i);e.setSelection(s)}return!0},pd=(n,e)=>({tr:t,state:r,dispatch:i})=>{let{selection:s}=r,o,l;return typeof e=="number"?(o=e,l=e):e&&"from"in e&&"to"in e?(o=e.from,l=e.to):(o=s.from,l=s.to),i&&t.doc.nodesBetween(o,l,(a,c)=>{a.isText||t.setNodeMarkup(c,void 0,{...a.attrs,dir:n})}),!0},md=n=>({tr:e,dispatch:t})=>{if(t){let{doc:r}=e,{from:i,to:s}=typeof n=="number"?{from:n,to:n}:n,o=T.atStart(r).from,l=T.atEnd(r).to,a=Ue(i,o,l),c=Ue(s,o,l),f=T.create(r,a,c);e.setSelection(f)}return!0},gd=n=>({state:e,dispatch:t})=>{let r=V(n,e.schema);return ys(r)(e,t)};function Lo(n,e){let t=n.storedMarks||n.selection.$to.parentOffset&&n.selection.$from.marks();if(t){let r=t.filter(i=>e?.includes(i.type.name));n.tr.ensureMarks(r)}}var yd=({keepMarks:n=!0}={})=>({tr:e,state:t,dispatch:r,editor:i})=>{let{selection:s,doc:o}=e,{$from:l,$to:a}=s,c=i.extensionManager.attributes,f=yn(c,l.node().type.name,l.node().attrs);if(s instanceof C&&s.node.isBlock)return!l.parentOffset||!te(o,l.pos)?!1:(r&&(n&&Lo(t,i.extensionManager.splittableMarks),e.split(l.pos).scrollIntoView()),!0);if(!l.parent.isBlock)return!1;let d=a.parentOffset===a.parent.content.size,u=l.depth===0?void 0:_o(l.node(-1).contentMatchAt(l.indexAfter(-1))),h=d&&u?[{type:u,attrs:f}]:void 0,p=te(e.doc,e.mapping.map(l.pos),1,h);if(!h&&!p&&te(e.doc,e.mapping.map(l.pos),1,u?[{type:u}]:void 0)&&(p=!0,h=u?[{type:u,attrs:f}]:void 0),r){if(p&&(s instanceof T&&e.deleteSelection(),e.split(e.mapping.map(l.pos),1,h),u&&!d&&!l.parentOffset&&l.parent.type!==u)){let m=e.mapping.map(l.before()),g=e.doc.resolve(m);l.node(-1).canReplaceWith(g.index(),g.index()+1,u)&&e.setNodeMarkup(e.mapping.map(l.before()),u)}n&&Lo(t,i.extensionManager.splittableMarks),e.scrollIntoView()}return p},bd=(n,e={})=>({tr:t,state:r,dispatch:i,editor:s})=>{var o;let l=V(n,r.schema),{$from:a,$to:c}=r.selection,f=r.selection.node;if(f&&f.isBlock||a.depth<2||!a.sameParent(c))return!1;let d=a.node(-1);if(d.type!==l)return!1;let u=s.extensionManager.attributes;if(a.parent.content.size===0&&a.node(-1).childCount===a.indexAfter(-1)){if(a.depth===2||a.node(-3).type!==l||a.index(-2)!==a.node(-2).childCount-1)return!1;if(i){let y=b.empty,S=a.index(-1)?1:a.index(-2)?2:3;for(let O=a.depth-S;O>=a.depth-3;O-=1)y=b.from(a.node(O).copy(y));let k=a.indexAfter(-1){if(w>-1)return!1;O.isTextblock&&O.content.size===0&&(w=R+1)}),w>-1&&t.setSelection(T.near(t.doc.resolve(w))),t.scrollIntoView()}return!0}let h=c.pos===a.end()?d.contentMatchAt(0).defaultType:null,p={...yn(u,d.type.name,d.attrs),...e},m={...yn(u,a.node().type.name,a.node().attrs),...e};t.delete(a.pos,c.pos);let g=h?[{type:l,attrs:p},{type:h,attrs:m}]:[{type:l,attrs:p}];if(!te(t.doc,a.pos,2))return!1;if(i){let{selection:y,storedMarks:S}=r,{splittableMarks:k}=s.extensionManager,v=S||y.$to.parentOffset&&y.$from.marks();if(t.split(a.pos,2,g).scrollIntoView(),!v||!i)return!0;let N=v.filter(D=>k.includes(D.type.name));t.ensureMarks(N)}return!0};function Vo(n){return!n||n==="1"?null:n}function nl(n,e){return Vo(n)===Vo(e)}var jr=(n,e)=>{let t=Cn(o=>o.type===e)(n.selection);if(!t)return!0;let r=n.doc.resolve(Math.max(0,t.pos-1)).before(t.depth);if(r===void 0)return!0;let i=n.doc.nodeAt(r);return!(t.node.type===i?.type&&re(n.doc,t.pos))||!nl(t.node.attrs.type,i?.attrs.type)||n.join(t.pos),!0},_r=(n,e)=>{let t=Cn(o=>o.type===e)(n.selection);if(!t)return!0;let r=n.doc.resolve(t.start).after(t.depth);if(r===void 0)return!0;let i=n.doc.nodeAt(r);return!(t.node.type===i?.type&&re(n.doc,r))||!nl(t.node.attrs.type,i?.attrs.type)||n.join(r),!0};function xd(n){let e=n.doc,t=e.firstChild;if(!t)return null;let r=e.resolve(1),i=e.resolve(t.nodeSize-1);return T.between(r,i)}var Sd=(n,e,t,r={})=>({editor:i,tr:s,state:o,dispatch:l,chain:a,commands:c,can:f})=>{let{extensions:d,splittableMarks:u}=i.extensionManager,h=V(n,o.schema),p=V(e,o.schema),{selection:m,storedMarks:g}=o,{$from:y,$to:S}=m,k=y.blockRange(S),v=g||m.$to.parentOffset&&m.$from.marks();if(!k)return!1;let N=Cn(ne=>Jr(ne.type.name,d))(m),D=m.from===0&&m.to===o.doc.content.size,w=o.doc.content.content,O=w.length===1?w[0]:null,R=D&&O&&Jr(O.type.name,d)?{node:O,pos:0,depth:0}:null,q=N??R,ft=!!N&&k.depth>=1&&k.depth-N.depth<=1,Re=!!R;if((ft||Re)&&q){if(q.node.type===h)return D&&Re?a().command(({tr:ne,dispatch:ee})=>{let X=xd(ne);return X?(ne.setSelection(X),ee&&ee(ne),!0):!1}).liftListItem(p).run():c.liftListItem(p);if(Jr(q.node.type.name,d)&&h.validContent(q.node.content))return a().command(()=>(s.setNodeMarkup(q.pos,h),!0)).command(()=>jr(s,h)).command(()=>_r(s,h)).run()}return!t||!v||!l?a().command(()=>f().wrapInList(h,r)?!0:c.clearNodes()).wrapInList(h,r).command(()=>jr(s,h)).command(()=>_r(s,h)).run():a().command(()=>{let ne=f().wrapInList(h,r),ee=v.filter(X=>u.includes(X.type.name));return s.ensureMarks(ee),ne?!0:c.clearNodes()}).wrapInList(h,r).command(()=>jr(s,h)).command(()=>_r(s,h)).run()},kd=(n,e={},t={})=>({state:r,commands:i})=>{let{extendEmptyMarkRange:s=!1}=t,o=we(n,r.schema);return Ur(r,o,e)?i.unsetMark(o,{extendEmptyMarkRange:s}):i.setMark(o,e)},Md=(n,e,t={})=>({state:r,commands:i})=>{let s=V(n,r.schema),o=V(e,r.schema),l=Ft(r,s,t),a;return r.selection.$anchor.sameParent(r.selection.$head)&&(a=r.selection.$anchor.parent.attrs),l?i.setNode(o,a):i.setNode(s,{...a,...t})},wd=(n,e={})=>({state:t,commands:r})=>{let i=V(n,t.schema);return Ft(t,i,e)?r.lift(i):r.wrapIn(i,e)},Cd=()=>({state:n,dispatch:e})=>{let t=n.plugins;for(let r=0;r=0;a-=1)o.step(l.steps[a].invert(l.docs[a]));if(s.text){let a=o.doc.resolve(s.from).marks();o.replaceWith(s.from,s.to,n.schema.text(s.text,a))}else o.delete(s.from,s.to)}return!0}}return!1},Td=(n={})=>({tr:e,dispatch:t,editor:r})=>{let{ignoreClearable:i=!1}=n,{selection:s}=e,{empty:o,ranges:l}=s;if(o)return!0;let{nonClearableMarks:a}=r.extensionManager;if(t){let c=Object.values(r.schema.marks).filter(f=>i||!a.includes(f.name));l.forEach(f=>{for(let d of c)e.removeMark(f.$from.pos,f.$to.pos,d)})}return!0},Nd=(n,e={})=>({tr:t,state:r,dispatch:i})=>{var s;let{extendEmptyMarkRange:o=!1}=e,{selection:l}=t,a=we(n,r.schema),{$from:c,empty:f,ranges:d}=l;if(!i)return!0;if(f&&o){let{from:u,to:h}=l,p=(s=c.marks().find(g=>g.type===a))==null?void 0:s.attrs,m=Xr(c,a,p);m&&(u=m.from,h=m.to),t.removeMark(u,h,a)}else d.forEach(u=>{t.removeMark(u.$from.pos,u.$to.pos,a)});return t.removeStoredMark(a),!0},Ed=n=>({tr:e,state:t,dispatch:r})=>{let{selection:i}=t,s,o;return typeof n=="number"?(s=n,o=n):n&&"from"in n&&"to"in n?(s=n.from,o=n.to):(s=i.from,o=i.to),r&&e.doc.nodesBetween(s,o,(l,a)=>{if(l.isText)return;let c={...l.attrs};delete c.dir,e.setNodeMarkup(a,void 0,c)}),!0},vd=(n,e={})=>({tr:t,state:r,dispatch:i})=>{let s=null,o=null,l=wn(typeof n=="string"?n:n.name,r.schema);if(!l)return!1;l==="node"&&(s=V(n,r.schema)),l==="mark"&&(o=we(n,r.schema));let a=!1;return t.selection.ranges.forEach(c=>{let f=c.$from.pos,d=c.$to.pos,u,h,p,m;t.selection.empty?r.doc.nodesBetween(f,d,(g,y)=>{s&&s===g.type&&(a=!0,p=Math.max(y,f),m=Math.min(y+g.nodeSize,d),u=y,h=g)}):r.doc.nodesBetween(f,d,(g,y)=>{y=f&&y<=d&&(s&&s===g.type&&(a=!0,i&&t.setNodeMarkup(y,void 0,{...g.attrs,...e})),o&&g.marks.length&&g.marks.forEach(S=>{if(o===S.type&&(a=!0,i)){let k=Math.max(y,f),v=Math.min(y+g.nodeSize,d);t.addMark(k,v,o.create({...S.attrs,...e}))}}))}),h&&(u!==void 0&&i&&t.setNodeMarkup(u,void 0,{...h.attrs,...e}),o&&h.marks.length&&h.marks.forEach(g=>{o===g.type&&i&&t.addMark(p,m,o.create({...g.attrs,...e}))}))}),a},Od=(n,e={})=>({state:t,dispatch:r})=>{let i=V(n,t.schema);return ps(i,e)(t,r)},Ad=(n,e={})=>({state:t,dispatch:r})=>{let i=V(n,t.schema);return ms(i,e)(t,r)},Dd=class{constructor(){this.callbacks={}}on(n,e){return this.callbacks[n]||(this.callbacks[n]=[]),this.callbacks[n].push(e),this}emit(n,...e){let t=this.callbacks[n];return t&&t.forEach(r=>r.apply(this,e)),this}off(n,e){let t=this.callbacks[n];return t&&(e?this.callbacks[n]=t.filter(r=>r!==e):delete this.callbacks[n]),this}once(n,e){let t=(...r)=>{this.off(n,t),e.apply(this,r)};return this.on(n,t)}removeAllListeners(){this.callbacks={}}};function Jh(n,e){let{selection:t}=n,{$from:r}=t;if(t instanceof C){let s=r.index();return r.parent.canReplaceWith(s,s+1,e)}let i=r.depth;for(;i>=0;){let s=r.index(i);if(r.node(i).contentMatchAt(s).matchType(e))return!0;i-=1}return!1}function Rd(n,e,t){let r=document.querySelector(`style[data-tiptap-style${t?`-${t}`:""}]`);if(r!==null)return r;let i=document.createElement("style");return e&&i.setAttribute("nonce",e),i.setAttribute(`data-tiptap-style${t?`-${t}`:""}`,""),i.innerHTML=n,document.getElementsByTagName("head")[0].appendChild(i),i}function jh(n,e){let t=n.getAttribute("style");if(!t)return null;let r=t.split(";").map(s=>s.trim()).filter(Boolean),i=e.toLowerCase();for(let s=r.length-1;s>=0;s-=1){let o=r[s],l=o.indexOf(":");if(l===-1)continue;if(o.slice(0,l).trim().toLowerCase()===i)return o.slice(l+1).trim()}return null}function Pd(n){return typeof n=="number"}function Id(n){return Object.prototype.toString.call(n).slice(8,-1)}function pn(n){return Id(n)!=="Object"?!1:n.constructor===Object&&Object.getPrototypeOf(n)===Object.prototype}var zd={};Yr(zd,{createAtomBlockMarkdownSpec:()=>Bd,createBlockMarkdownSpec:()=>Fd,createInlineMarkdownSpec:()=>$d,parseAttributes:()=>ti,parseIndentedBlocks:()=>Wd,renderNestedMarkdownContent:()=>Hd,serializeAttributes:()=>ni});function ti(n){if(!n?.trim())return{};let e={},t=[],r=n.replace(/["']([^"']*)["']/g,c=>(t.push(c),`__QUOTED_${t.length-1}__`)),i=r.match(/(?:^|\s)\.([\w-]+)/g);if(i){let c=i.map(f=>f.trim().slice(1));e.class=c.join(" ")}let s=r.match(/(?:^|\s)#([\w-]+)/);s&&(e.id=s[1]);let o=/([a-zA-Z][\w-]*)\s*=\s*(__QUOTED_\d+__)/g;Array.from(r.matchAll(o)).forEach(([,c,f])=>{var d;let u=parseInt(((d=f.match(/__QUOTED_(\d+)__/))==null?void 0:d[1])||"0",10),h=t[u];h&&(e[c]=h.slice(1,-1))});let a=r.replace(/(?:^|\s)\.([\w-]+)/g,"").replace(/(?:^|\s)#([\w-]+)/g,"").replace(/([a-zA-Z][\w-]*)\s*=\s*__QUOTED_\d+__/g,"").trim();return a&&a.split(/\s+/).filter(Boolean).forEach(f=>{f.match(/^[a-zA-Z][\w-]*$/)&&(e[f]=!0)}),e}function ni(n){if(!n||Object.keys(n).length===0)return"";let e=[];return n.class&&String(n.class).split(/\s+/).filter(Boolean).forEach(r=>e.push(`.${r}`)),n.id&&e.push(`#${n.id}`),Object.entries(n).forEach(([t,r])=>{t==="class"||t==="id"||(r===!0?e.push(t):r!==!1&&r!=null&&e.push(`${t}="${String(r)}"`))}),e.join(" ")}function Bd(n){let{nodeName:e,name:t,parseAttributes:r=ti,serializeAttributes:i=ni,defaultAttributes:s={},requiredAttributes:o=[],allowedAttributes:l}=n,a=t||e,c=f=>{if(!l)return f;let d={};return l.forEach(u=>{u in f&&(d[u]=f[u])}),d};return{parseMarkdown:(f,d)=>{let u={...s,...f.attributes};return d.createNode(e,u,[])},markdownTokenizer:{name:e,level:"block",start(f){var d;let u=new RegExp(`^:::${a}(?:\\s|$)`,"m"),h=(d=f.match(u))==null?void 0:d.index;return h!==void 0?h:-1},tokenize(f,d,u){let h=new RegExp(`^:::${a}(?:\\s+\\{([^}]*)\\})?\\s*:::(?:\\n|$)`),p=f.match(h);if(!p)return;let m=p[1]||"",g=r(m);if(!o.find(S=>!(S in g)))return{type:e,raw:p[0],attributes:g}}},renderMarkdown:f=>{let d=c(f.attrs||{}),u=i(d),h=u?` {${u}}`:"";return`:::${a}${h} :::`}}}function Fd(n){let{nodeName:e,name:t,getContent:r,parseAttributes:i=ti,serializeAttributes:s=ni,defaultAttributes:o={},content:l="block",allowedAttributes:a}=n,c=t||e,f=d=>{if(!a)return d;let u={};return a.forEach(h=>{h in d&&(u[h]=d[h])}),u};return{parseMarkdown:(d,u)=>{let h;if(r){let m=r(d);h=typeof m=="string"?[{type:"text",text:m}]:m}else l==="block"?h=u.parseChildren(d.tokens||[]):h=u.parseInline(d.tokens||[]);let p={...o,...d.attributes};return u.createNode(e,p,h)},markdownTokenizer:{name:e,level:"block",start(d){var u;let h=new RegExp(`^:::${c}`,"m"),p=(u=d.match(h))==null?void 0:u.index;return p!==void 0?p:-1},tokenize(d,u,h){var p;let m=new RegExp(`^:::${c}(?:\\s+\\{([^}]*)\\})?\\s*\\n`),g=d.match(m);if(!g)return;let[y,S=""]=g,k=i(S),v=1,N=y.length,D="",w=/^:::([\w-]*)(\s.*)?/gm,O=d.slice(N);for(w.lastIndex=0;;){let R=w.exec(O);if(R===null)break;let q=R.index,ft=R[1];if(!((p=R[2])!=null&&p.endsWith(":::"))){if(ft)v+=1;else if(v-=1,v===0){let Re=O.slice(0,q);D=Re.trim();let ne=d.slice(0,N+q+R[0].length),ee=[];if(D)if(l==="block")for(ee=h.blockTokens(Re),ee.forEach(X=>{X.text&&(!X.tokens||X.tokens.length===0)&&(X.tokens=h.inlineTokens(X.text))});ee.length>0;){let X=ee[ee.length-1];if(X.type==="paragraph"&&(!X.text||X.text.trim()===""))ee.pop();else break}else ee=h.inlineTokens(D);return{type:e,raw:ne,attributes:k,content:D,tokens:ee}}}}}},renderMarkdown:(d,u)=>{let h=f(d.attrs||{}),p=s(h),m=p?` {${p}}`:"",g=u.renderChildren(d.content||[],`
+
+`);return`:::${c}${m}
+
+${g}
+
+:::`}}}function Ld(n){if(!n.trim())return{};let e={},t=/(\w+)=(?:"([^"]*)"|'([^']*)')/g,r=t.exec(n);for(;r!==null;){let[,i,s,o]=r;e[i]=s||o,r=t.exec(n)}return e}function Vd(n){return Object.entries(n).filter(([,e])=>e!=null).map(([e,t])=>`${e}="${t}"`).join(" ")}function $d(n){let{nodeName:e,name:t,getContent:r,parseAttributes:i=Ld,serializeAttributes:s=Vd,defaultAttributes:o={},selfClosing:l=!1,allowedAttributes:a}=n,c=t||e,f=u=>{if(!a)return u;let h={};return a.forEach(p=>{let m=typeof p=="string"?p:p.name,g=typeof p=="string"?void 0:p.skipIfDefault;if(m in u){let y=u[m];if(g!==void 0&&y===g)return;h[m]=y}}),h},d=c.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");return{parseMarkdown:(u,h)=>{let p={...o,...u.attributes};if(l)return h.createNode(e,p);let m=r?r(u):u.content||"";return m?h.createNode(e,p,[h.createTextNode(m)]):h.createNode(e,p,[])},markdownTokenizer:{name:e,level:"inline",start(u){let h=l?new RegExp(`\\[${d}\\s*[^\\]]*\\]`):new RegExp(`\\[${d}\\s*[^\\]]*\\][\\s\\S]*?\\[\\/${d}\\]`),p=u.match(h),m=p?.index;return m!==void 0?m:-1},tokenize(u,h,p){let m=l?new RegExp(`^\\[${d}\\s*([^\\]]*)\\]`):new RegExp(`^\\[${d}\\s*([^\\]]*)\\]([\\s\\S]*?)\\[\\/${d}\\]`),g=u.match(m);if(!g)return;let y="",S="";if(l){let[,v]=g;S=v}else{let[,v,N]=g;S=v,y=N||""}let k=i(S.trim());return{type:e,raw:g[0],content:y.trim(),attributes:k}}},renderMarkdown:u=>{let h="";r?h=r(u):u.content&&u.content.length>0&&(h=u.content.filter(y=>y.type==="text").map(y=>y.text).join(""));let p=f(u.attrs||{}),m=s(p),g=m?` ${m}`:"";return l?`[${c}${g}]`:`[${c}${g}]${h}[/${c}]`}}}function Wd(n,e,t){var r,i,s,o;let l=n.split(`
+`),a=[],c="",f=0,d=e.baseIndentSize||2;for(;f0)break;if(u.trim()===""){f+=1,c=`${c}${u}
+`;continue}else return}let p=e.extractItemData(h),{indentLevel:m,mainContent:g}=p;c=`${c}${u}
+`;let y=[g];for(f+=1;fq.trim()!=="");if(w===-1)break;if((((i=(r=l[f+1+w].match(/^(\s*)/))==null?void 0:r[1])==null?void 0:i.length)||0)>m){y.push(N),c=`${c}${N}
+`,f+=1;continue}else break}if((((o=(s=N.match(/^(\s*)/))==null?void 0:s[1])==null?void 0:o.length)||0)>m)y.push(N),c=`${c}${N}
+`,f+=1;else break}let S,k=y.slice(1);if(k.length>0){let N=k.map(D=>D.slice(m+d)).join(`
+`);N.trim()&&(e.customNestedParser?S=e.customNestedParser(N):S=t.blockTokens(N))}let v=e.createToken(p,S);a.push(v)}if(a.length!==0)return{items:a,raw:c}}function Hd(n,e,t,r){if(!n||!Array.isArray(n.content))return"";let i=typeof t=="function"?t(r):t,[s,...o]=n.content,l=e.renderChildren([s]),a=`${i}${l}`;return o&&o.length>0&&o.forEach((c,f)=>{var d,u;let h=(u=(d=e.renderChild)==null?void 0:d.call(e,c,f+1))!=null?u:e.renderChildren([c]);if(h!=null){let p=h.split(`
+`).map(m=>m?e.indent(m):e.indent("")).join(`
+`);a+=c.type==="paragraph"?`
+
+${p}`:`
+${p}`}}),a}function rl(n,e){let t={...n};return pn(n)&&pn(e)&&Object.keys(e).forEach(r=>{pn(e[r])&&pn(n[r])?t[r]=rl(n[r],e[r]):t[r]=e[r]}),t}function Jd(n,e,t={}){let{state:r}=e,{doc:i,tr:s}=r,o=n;i.descendants((l,a)=>{let c=s.mapping.map(a),f=s.mapping.map(a)+l.nodeSize,d=null;if(l.marks.forEach(h=>{if(h!==o)return!1;d=h}),!d)return;let u=!1;if(Object.keys(t).forEach(h=>{t[h]!==d.attrs[h]&&(u=!0)}),u){let h=n.type.create({...n.attrs,...t});s.removeMark(c,f,n.type),s.addMark(c,f,h)}}),s.docChanged&&e.view.dispatch(s)}var Tn=class{constructor(n){var e;this.find=n.find,this.handler=n.handler,this.undoable=(e=n.undoable)!=null?e:!0}},jd=(n,e)=>{if(Gr(e))return e.exec(n);let t=e(n);if(!t)return null;let r=[t.text];return r.index=t.index,r.input=n,r.data=t.data,t.replaceWith&&(t.text.includes(t.replaceWith)||console.warn('[tiptap warn]: "inputRuleMatch.replaceWith" must be part of "inputRuleMatch.text".'),r.push(t.replaceWith)),r};function mn(n){var e;let{editor:t,from:r,to:i,text:s,rules:o,plugin:l}=n,{view:a}=t;if(a.composing)return!1;let c=a.state.doc.resolve(r);if(c.parent.type.spec.code||(e=c.nodeBefore||c.nodeAfter)!=null&&e.marks.find(u=>u.type.spec.code))return!1;let f=!1,d=sd(c)+s;return o.forEach(u=>{if(f)return;let h=jd(d,u.find);if(!h)return;let p=h[0].length-s.length;if(p>0){let D=c.parentOffset-p;if(D<0||c.parent.textBetween(D,c.parentOffset)!==h[0].slice(0,p))return}let m=a.state.tr,g=kn({state:a.state,transaction:m}),y={from:r-(h[0].length-s.length),to:i},{commands:S,chain:k,can:v}=new Mn({editor:t,state:g});u.handler({state:g,range:y,match:h,commands:S,chain:k,can:v})===null||!m.steps.length||(u.undoable&&m.setMeta(l,{transform:m,from:r,to:i,text:s}),a.dispatch(m),f=!0)}),f}function _d(n){let{editor:e,rules:t}=n,r=new I({state:{init(){return null},apply(i,s,o){let l=i.getMeta(r);if(l)return l;let a=i.getMeta("applyInputRules");return a&&setTimeout(()=>{let{text:f}=a;typeof f=="string"?f=f:f=Zr(b.from(f),o.schema);let{from:d}=a,u=d+f.length;mn({editor:e,from:d,to:u,text:f,rules:t,plugin:r})}),i.selectionSet||i.docChanged?null:s}},props:{handleTextInput(i,s,o,l){return mn({editor:e,from:s,to:o,text:l,rules:t,plugin:r})},handleDOMEvents:{compositionend:i=>(setTimeout(()=>{let{$cursor:s}=i.state.selection;s&&mn({editor:e,from:s.pos,to:s.pos,text:"",rules:t,plugin:r})}),!1)},handleKeyDown(i,s){if(s.key!=="Enter")return!1;let{$cursor:o}=i.state.selection;return o?mn({editor:e,from:o.pos,to:o.pos,text:`
+`,rules:t,plugin:r}):!1}},isInputRules:!0});return r}var ri=class{constructor(n={}){this.type="extendable",this.parent=null,this.child=null,this.name="",this.config={name:this.name},this.config={...this.config,...n},this.name=this.config.name}get options(){return{...P(M(this,"addOptions",{name:this.name}))}}get storage(){return{...P(M(this,"addStorage",{name:this.name,options:this.options}))}}configure(n={}){let e=this.extend({...this.config,addOptions:()=>rl(this.options,n)});return e.name=this.name,e.parent=this.parent,this.child=null,e}extend(n={}){let e=new this.constructor({...this.config,...n});return e.parent=this,this.child=e,e.name="name"in n?n.name:e.parent.name,e}},Kd=class il extends ri{constructor(){super(...arguments),this.type="mark"}static create(e={}){let t=typeof e=="function"?e():e;return new il(t)}static handleExit({editor:e,mark:t}){let{tr:r}=e.state,i=e.state.selection.$from;if(i.pos===i.end()){let o=i.marks();if(!!!o.find(c=>c?.type.name===t.name))return!1;let a=o.find(c=>c?.type.name===t.name);return a&&r.removeStoredMark(a),r.insertText(" ",i.pos),e.view.dispatch(r),!0}return!1}configure(e){return super.configure(e)}extend(e){let t=typeof e=="function"?e():e;return super.extend(t)}},qd=class{constructor(n){this.find=n.find,this.handler=n.handler}},Ud=(n,e,t)=>{if(Gr(e))return[...n.matchAll(e)];let r=e(n,t);return r?r.map(i=>{let s=[i.text];return s.index=i.index,s.input=n,s.data=i.data,i.replaceWith&&(i.text.includes(i.replaceWith)||console.warn('[tiptap warn]: "pasteRuleMatch.replaceWith" must be part of "pasteRuleMatch.text".'),s.push(i.replaceWith)),s}):[]};function Yd(n){let{editor:e,state:t,from:r,to:i,rule:s,pasteEvent:o,dropEvent:l}=n,{commands:a,chain:c,can:f}=new Mn({editor:e,state:t}),d=[];return t.doc.nodesBetween(r,i,(h,p)=>{var m,g,y,S,k;if((g=(m=h.type)==null?void 0:m.spec)!=null&&g.code||!(h.isText||h.isTextblock||h.isInline))return;let v=(k=(S=(y=h.content)==null?void 0:y.size)!=null?S:h.nodeSize)!=null?k:0,N=Math.max(r,p),D=Math.min(i,p+v);if(N>=D)return;let w=h.isText?h.text||"":h.textBetween(N-p,D-p,void 0,"\uFFFC");Ud(w,s.find,o).forEach(R=>{if(R.index===void 0)return;let q=N+R.index+1,ft=q+R[0].length,Re={from:t.tr.mapping.map(q),to:t.tr.mapping.map(ft)},ne=s.handler({state:t,range:Re,match:R,commands:a,chain:c,can:f,pasteEvent:o,dropEvent:l});d.push(ne)})}),d.every(h=>h!==null)}var gn=null,Gd=n=>{var e;let t=new ClipboardEvent("paste",{clipboardData:new DataTransfer});return(e=t.clipboardData)==null||e.setData("text/html",n),t};function Xd(n){let{editor:e,rules:t}=n,r=null,i=!1,s=!1,o=typeof ClipboardEvent<"u"?new ClipboardEvent("paste"):null,l;try{l=typeof DragEvent<"u"?new DragEvent("drop"):null}catch{l=null}let a=({state:f,from:d,to:u,rule:h,pasteEvt:p})=>{let m=f.tr,g=kn({state:f,transaction:m});if(!(!Yd({editor:e,state:g,from:Math.max(d-1,0),to:u.b-1,rule:h,pasteEvent:p,dropEvent:l})||!m.steps.length)){try{l=typeof DragEvent<"u"?new DragEvent("drop"):null}catch{l=null}return o=typeof ClipboardEvent<"u"?new ClipboardEvent("paste"):null,m}};return t.map(f=>new I({view(d){let u=p=>{var m;r=(m=d.dom.parentElement)!=null&&m.contains(p.target)?d.dom.parentElement:null,r&&(gn=e)},h=()=>{gn&&(gn=null)};return window.addEventListener("dragstart",u),window.addEventListener("dragend",h),{destroy(){window.removeEventListener("dragstart",u),window.removeEventListener("dragend",h)}}},props:{handleDOMEvents:{drop:(d,u)=>{if(s=r===d.dom.parentElement,l=u,!s){let h=gn;h?.isEditable&&setTimeout(()=>{let p=h.state.selection;p&&h.commands.deleteRange({from:p.from,to:p.to})},10)}return!1},paste:(d,u)=>{var h;let p=(h=u.clipboardData)==null?void 0:h.getData("text/html");return o=u,i=!!p?.includes("data-pm-slice"),!1}}},appendTransaction:(d,u,h)=>{let p=d[0],m=p.getMeta("uiEvent")==="paste"&&!i,g=p.getMeta("uiEvent")==="drop"&&!s,y=p.getMeta("applyPasteRules"),S=!!y;if(!m&&!g&&!S)return;if(S){let{text:N}=y;typeof N=="string"?N=N:N=Zr(b.from(N),h.schema);let{from:D}=y,w=D+N.length,O=Gd(N);return a({rule:f,state:h,from:D,to:{b:w},pasteEvt:O})}let k=u.doc.content.findDiffStart(h.doc.content),v=u.doc.content.findDiffEnd(h.doc.content);if(!(!Pd(k)||!v||k===v.b))return a({rule:f,state:h,from:k,to:v,pasteEvt:o})}}))}var Nn=class{constructor(n,e){this.splittableMarks=[],this.nonClearableMarks=[],this.editor=e,this.baseExtensions=n,this.extensions=Go(n),this.schema=Xf(this.extensions,e),this.setupExtensions()}get commands(){return this.extensions.reduce((n,e)=>{let t={name:e.name,options:e.options,storage:this.editor.extensionStorage[e.name],editor:this.editor,type:It(e.name,this.schema)},r=M(e,"addCommands",t);return r?{...n,...r()}:n},{})}get plugins(){let{editor:n}=this;return Bt([...this.extensions].reverse()).flatMap(r=>{let i={name:r.name,options:r.options,storage:this.editor.extensionStorage[r.name],editor:n,type:It(r.name,this.schema)},s=[],o=M(r,"addKeyboardShortcuts",i),l={};if(r.type==="mark"&&M(r,"exitable",i)&&(l.ArrowRight=()=>Kd.handleExit({editor:n,mark:r})),o){let u=Object.fromEntries(Object.entries(o()).map(([h,p])=>[h,()=>p({editor:n})]));l={...l,...u}}let a=vo(l);s.push(a);let c=M(r,"addInputRules",i);if(Fo(r,n.options.enableInputRules)&&c){let u=c();if(u&&u.length){let h=_d({editor:n,rules:u}),p=Array.isArray(h)?h:[h];s.push(...p)}}let f=M(r,"addPasteRules",i);if(Fo(r,n.options.enablePasteRules)&&f){let u=f();if(u&&u.length){let h=Xd({editor:n,rules:u});s.push(...h)}}let d=M(r,"addProseMirrorPlugins",i);if(d){let u=d();s.push(...u)}return s})}get attributes(){return Yo(this.extensions)}get nodeViews(){let{editor:n}=this,{nodeExtensions:e}=ct(this.extensions);return Object.fromEntries(e.filter(t=>!!M(t,"addNodeView")).map(t=>{let r=this.attributes.filter(a=>a.type===t.name),i={name:t.name,options:t.options,storage:this.editor.extensionStorage[t.name],editor:n,type:V(t.name,this.schema)},s=M(t,"addNodeView",i);if(!s)return[];let o=s();if(!o)return[];let l=(a,c,f,d,u)=>{let h=Sn(a,r);return o({node:a,view:c,getPos:f,decorations:d,innerDecorations:u,editor:n,extension:t,HTMLAttributes:h})};return[t.name,l]}))}dispatchTransaction(n){let{editor:e}=this;return Bt([...this.extensions].reverse()).reduceRight((r,i)=>{let s={name:i.name,options:i.options,storage:this.editor.extensionStorage[i.name],editor:e,type:It(i.name,this.schema)},o=M(i,"dispatchTransaction",s);return o?l=>{o.call(s,{transaction:l,next:r})}:r},n)}transformPastedHTML(n){let{editor:e}=this;return Bt([...this.extensions]).reduce((r,i)=>{let s={name:i.name,options:i.options,storage:this.editor.extensionStorage[i.name],editor:e,type:It(i.name,this.schema)},o=M(i,"transformPastedHTML",s);return o?(l,a)=>{let c=r(l,a);return o.call(s,c)}:r},n||(r=>r))}get markViews(){let{editor:n}=this,{markExtensions:e}=ct(this.extensions);return Object.fromEntries(e.filter(t=>!!M(t,"addMarkView")).map(t=>{let r=this.attributes.filter(l=>l.type===t.name),i={name:t.name,options:t.options,storage:this.editor.extensionStorage[t.name],editor:n,type:we(t.name,this.schema)},s=M(t,"addMarkView",i);if(!s)return[];let o=(l,a,c)=>{let f=Sn(l,r);return s()({mark:l,view:a,inline:c,editor:n,extension:t,HTMLAttributes:f,updateAttributes:d=>{Jd(l,n,d)}})};return[t.name,o]}))}destroy(){this.extensions.forEach(n=>{let e=n;for(;e.parent;){let t=e.parent;t.child===e&&(t.child=null),e=t}}),this.extensions=[],this.baseExtensions=[],this.schema=null,this.editor=null}setupExtensions(){let n=this.extensions;this.editor.extensionStorage=Object.fromEntries(n.map(e=>[e.name,e.storage])),n.forEach(e=>{var t,r;let i={name:e.name,options:e.options,storage:this.editor.extensionStorage[e.name],editor:this.editor,type:It(e.name,this.schema)};e.type==="mark"&&(((t=P(M(e,"keepOnSplit",i)))==null||t)&&this.splittableMarks.push(e.name),(r=P(M(e,"clearable",i)))==null||r||this.nonClearableMarks.push(e.name));let s=M(e,"onBeforeCreate",i),o=M(e,"onCreate",i),l=M(e,"onUpdate",i),a=M(e,"onSelectionUpdate",i),c=M(e,"onTransaction",i),f=M(e,"onFocus",i),d=M(e,"onBlur",i),u=M(e,"onDestroy",i);s&&this.editor.on("beforeCreate",s),o&&this.editor.on("create",o),l&&this.editor.on("update",l),a&&this.editor.on("selectionUpdate",a),c&&this.editor.on("transaction",c),f&&this.editor.on("focus",f),d&&this.editor.on("blur",d),u&&this.editor.on("destroy",u)})}};Nn.resolve=Go;Nn.sort=Bt;Nn.flatten=Qr;var Qd={};Yr(Qd,{ClipboardTextSerializer:()=>ol,Commands:()=>ll,Delete:()=>al,Drop:()=>cl,Editable:()=>fl,FocusEvents:()=>ul,Keymap:()=>hl,Paste:()=>pl,Tabindex:()=>ml,TextDirection:()=>gl,focusEventsPluginKey:()=>dl});var ue=class sl extends ri{constructor(){super(...arguments),this.type="extension"}static create(e={}){let t=typeof e=="function"?e():e;return new sl(t)}configure(e){return super.configure(e)}extend(e){let t=typeof e=="function"?e():e;return super.extend(t)}},ol=ue.create({name:"clipboardTextSerializer",addOptions(){return{blockSeparator:void 0}},addProseMirrorPlugins(){return[new I({key:new L("clipboardTextSerializer"),props:{clipboardTextSerializer:()=>{let{editor:n}=this,{state:e,schema:t}=n,{doc:r,selection:i}=e,s=Qo(t),{blockSeparator:o}=this.options,l={...o!==void 0?{blockSeparator:o}:{},textSerializers:s};return[...i.ranges].sort((c,f)=>c.$from.pos-f.$from.pos).map(({$from:c,$to:f})=>Xo(r,{from:c.pos,to:f.pos},l)).join(o??`
+
+`)}}})]}}),ll=ue.create({name:"commands",addCommands(){return{...$o}}}),al=ue.create({name:"delete",onUpdate({transaction:n,appendedTransactions:e}){var t,r,i;let s=()=>{var o,l,a,c;if((c=(a=(l=(o=this.editor.options.coreExtensionOptions)==null?void 0:o.delete)==null?void 0:l.filterTransaction)==null?void 0:a.call(l,n))!=null?c:n.getMeta("y-sync$"))return;let f=_f(n.before,[n,...e]);id(f).forEach(h=>{f.mapping.mapResult(h.oldRange.from).deletedAfter&&f.mapping.mapResult(h.oldRange.to).deletedBefore&&f.before.nodesBetween(h.oldRange.from,h.oldRange.to,(p,m)=>{let g=m+p.nodeSize-2,y=h.oldRange.from<=m&&g<=h.oldRange.to;this.editor.emit("delete",{type:"node",node:p,from:m,to:g,newFrom:f.mapping.map(m),newTo:f.mapping.map(g),deletedRange:h.oldRange,newRange:h.newRange,partial:!y,editor:this.editor,transaction:n,combinedTransform:f})})});let u=f.mapping;f.steps.forEach((h,p)=>{var m,g;if(h instanceof me){let y=u.slice(p).map(h.from,-1),S=u.slice(p).map(h.to),k=u.invert().map(y,-1),v=u.invert().map(S),N=y>0?(m=f.doc.nodeAt(y-1))==null?void 0:m.marks.some(w=>w.eq(h.mark)):!1,D=(g=f.doc.nodeAt(S))==null?void 0:g.marks.some(w=>w.eq(h.mark));this.editor.emit("delete",{type:"mark",mark:h.mark,from:h.from,to:h.to,deletedRange:{from:k,to:v},newRange:{from:y,to:S},partial:!!(D||N),editor:this.editor,transaction:n,combinedTransform:f})}})};(i=(r=(t=this.editor.options.coreExtensionOptions)==null?void 0:t.delete)==null?void 0:r.async)==null||i?setTimeout(s,0):s()}}),cl=ue.create({name:"drop",addProseMirrorPlugins(){return[new I({key:new L("tiptapDrop"),props:{handleDrop:(n,e,t,r)=>{this.editor.emit("drop",{editor:this.editor,event:e,slice:t,moved:r})}}})]}}),fl=ue.create({name:"editable",addProseMirrorPlugins(){return[new I({key:new L("editable"),props:{editable:()=>this.editor.options.editable}})]}}),dl=new L("focusEvents"),ul=ue.create({name:"focusEvents",addProseMirrorPlugins(){let{editor:n}=this;return[new I({key:dl,props:{handleDOMEvents:{focus:(e,t)=>{n.isFocused=!0;let r=n.state.tr.setMeta("focus",{event:t}).setMeta("addToHistory",!1);return e.dispatch(r),!1},blur:(e,t)=>{n.isFocused=!1;let r=n.state.tr.setMeta("blur",{event:t}).setMeta("addToHistory",!1);return e.dispatch(r),!1}}}})]}}),hl=ue.create({name:"keymap",addKeyboardShortcuts(){let n=()=>this.editor.commands.first(({commands:o})=>[()=>o.undoInputRule(),()=>o.command(({tr:l})=>{let{selection:a,doc:c}=l,{empty:f,$anchor:d}=a,{pos:u,parent:h}=d,p=d.parent.isTextblock&&u>0?l.doc.resolve(u-1):d,m=p.parent.type.spec.isolating,g=d.pos-d.parentOffset,y=m&&p.parent.childCount===1?g===d.pos:E.atStart(c).from===u;return!f||!h.type.isTextblock||h.textContent.length||!y||y&&d.parent.type.name==="paragraph"?!1:o.clearNodes()}),()=>o.deleteSelection(),()=>o.joinBackward(),()=>o.selectNodeBackward()]),e=()=>this.editor.commands.first(({commands:o})=>[()=>o.deleteSelection(),()=>o.deleteCurrentNode(),()=>o.joinForward(),()=>o.selectNodeForward()]),r={Enter:()=>this.editor.commands.first(({commands:o})=>[()=>o.newlineInCode(),()=>o.createParagraphNear(),()=>o.liftEmptyBlock(),()=>o.splitBlock()]),"Mod-Enter":()=>this.editor.commands.exitCode(),Backspace:n,"Mod-Backspace":n,"Shift-Backspace":n,Delete:e,"Mod-Delete":e,"Mod-a":()=>this.editor.commands.selectAll()},i={...r},s={...r,"Ctrl-h":n,"Alt-Backspace":n,"Ctrl-d":e,"Ctrl-Alt-Backspace":e,"Alt-Delete":e,"Alt-d":e,"Ctrl-a":()=>this.editor.commands.selectTextblockStart(),"Ctrl-e":()=>this.editor.commands.selectTextblockEnd()};return xn()||Ko()?s:i},addProseMirrorPlugins(){return[new I({key:new L("clearDocument"),appendTransaction:(n,e,t)=>{if(n.some(m=>m.getMeta("composition")))return;let r=n.some(m=>m.docChanged)&&!e.doc.eq(t.doc),i=n.some(m=>m.getMeta("preventClearDocument"));if(!r||i)return;let{empty:s,from:o,to:l}=e.selection,a=E.atStart(e.doc).from,c=E.atEnd(e.doc).to;if(s||!(o===a&&l===c)||!ei(t.doc))return;let u=t.tr,h=kn({state:t,transaction:u}),{commands:p}=new Mn({editor:this.editor,state:h});if(p.clearNodes(),!!u.steps.length)return u}})]}}),pl=ue.create({name:"paste",addProseMirrorPlugins(){return[new I({key:new L("tiptapPaste"),props:{handlePaste:(n,e,t)=>{this.editor.emit("paste",{editor:this.editor,event:e,slice:t})}}})]}}),ml=ue.create({name:"tabindex",addOptions(){return{value:void 0}},addProseMirrorPlugins(){return[new I({key:new L("tabindex"),props:{attributes:()=>{var n;return!this.editor.isEditable&&this.options.value===void 0?{}:{tabindex:(n=this.options.value)!=null?n:"0"}}}})]}}),gl=ue.create({name:"textDirection",addOptions(){return{direction:void 0}},addGlobalAttributes(){if(!this.options.direction)return[];let{nodeExtensions:n}=ct(this.extensions);return[{types:n.filter(e=>e.name!=="text").map(e=>e.name),attributes:{dir:{default:this.options.direction,parseHTML:e=>{let t=e.getAttribute("dir");return t&&(t==="ltr"||t==="rtl"||t==="auto")?t:this.options.direction},renderHTML:e=>e.dir?{dir:e.dir}:{}}}}]},addProseMirrorPlugins(){return[new I({key:new L("textDirection"),props:{attributes:()=>{let n=this.options.direction;return n?{dir:n}:{}}}})]}}),Zd=class zt{constructor(e,t,r=!1,i=null){this.currentNode=null,this.actualDepth=null,this.isBlock=r,this.resolvedPos=e,this.editor=t,this.currentNode=i}get name(){return this.node.type.name}get node(){return this.currentNode||this.resolvedPos.node()}get element(){return this.editor.view.domAtPos(this.pos).node}get depth(){var e;return(e=this.actualDepth)!=null?e:this.resolvedPos.depth}get pos(){return this.resolvedPos.pos}get content(){return this.node.content}set content(e){let t=this.from,r=this.to;if(this.isBlock){if(this.content.size===0){console.error(`You can\u2019t set content on a block node. Tried to set content on ${this.name} at ${this.pos}`);return}t=this.from+1,r=this.to-1}this.editor.commands.insertContentAt({from:t,to:r},e)}get attributes(){return this.node.attrs}get textContent(){return this.node.textContent}get size(){return this.node.nodeSize}get from(){return this.isBlock?this.pos:this.resolvedPos.start(this.resolvedPos.depth)}get range(){return{from:this.from,to:this.to}}get to(){return this.isBlock?this.pos+this.size:this.resolvedPos.end(this.resolvedPos.depth)+(this.node.isText?0:1)}get parent(){if(this.depth===0)return null;let e=this.resolvedPos.start(this.resolvedPos.depth-1),t=this.resolvedPos.doc.resolve(e);return new zt(t,this.editor)}get before(){let e=this.resolvedPos.doc.resolve(this.from-(this.isBlock?1:2));return e.depth!==this.depth&&(e=this.resolvedPos.doc.resolve(this.from-3)),new zt(e,this.editor)}get after(){let e=this.resolvedPos.doc.resolve(this.to+(this.isBlock?2:1));return e.depth!==this.depth&&(e=this.resolvedPos.doc.resolve(this.to+3)),new zt(e,this.editor)}get children(){let e=[];return this.node.content.forEach((t,r)=>{let i=t.isBlock&&!t.isTextblock,s=t.isAtom&&!t.isText,o=t.isInline,l=this.pos+r+(s?0:1);if(l<0||l>this.resolvedPos.doc.nodeSize-2)return;let a=this.resolvedPos.doc.resolve(l);if(!i&&!o&&a.depth<=this.depth)return;let c=new zt(a,this.editor,i,i||o?t:null);i&&(c.actualDepth=this.depth+1),e.push(c)}),e}get firstChild(){return this.children[0]||null}get lastChild(){let e=this.children;return e[e.length-1]||null}closest(e,t={}){let r=null,i=this.parent;for(;i&&!r;){if(i.node.type.name===e)if(Object.keys(t).length>0){let s=i.node.attrs,o=Object.keys(t);for(let l=0;l{r&&i.length>0||(o.node.type.name===e&&s.every(a=>t[a]===o.node.attrs[a])&&i.push(o),!(r&&i.length>0)&&(i=i.concat(o.querySelectorAll(e,t,r))))}),i}setAttribute(e){let{tr:t}=this.editor.state;t.setNodeMarkup(this.from,void 0,{...this.node.attrs,...e}),this.editor.view.dispatch(t)}},eu=`.ProseMirror {
+ position: relative;
+}
+
+.ProseMirror {
+ word-wrap: break-word;
+ white-space: pre-wrap;
+ white-space: break-spaces;
+ -webkit-font-variant-ligatures: none;
+ font-variant-ligatures: none;
+ font-feature-settings: "liga" 0; /* the above doesn't seem to work in Edge */
+}
+
+.ProseMirror [contenteditable="false"] {
+ white-space: normal;
+}
+
+.ProseMirror [contenteditable="false"] [contenteditable="true"] {
+ white-space: pre-wrap;
+}
+
+.ProseMirror pre {
+ white-space: pre-wrap;
+}
+
+img.ProseMirror-separator {
+ display: inline !important;
+ border: none !important;
+ margin: 0 !important;
+ width: 0 !important;
+ height: 0 !important;
+}
+
+.ProseMirror-gapcursor {
+ display: none;
+ pointer-events: none;
+ position: absolute;
+ margin: 0;
+}
+
+.ProseMirror-gapcursor:after {
+ content: "";
+ display: block;
+ position: absolute;
+ top: -2px;
+ width: 20px;
+ border-top: 1px solid black;
+ animation: ProseMirror-cursor-blink 1.1s steps(2, start) infinite;
+}
+
+@keyframes ProseMirror-cursor-blink {
+ to {
+ visibility: hidden;
+ }
+}
+
+.ProseMirror-hideselection *::selection {
+ background: transparent;
+}
+
+.ProseMirror-hideselection *::-moz-selection {
+ background: transparent;
+}
+
+.ProseMirror-hideselection * {
+ caret-color: transparent;
+}
+
+.ProseMirror-focused .ProseMirror-gapcursor {
+ display: block;
+}`,ip=class extends Dd{constructor(n={}){super(),this.css=null,this.className="tiptap",this.editorView=null,this.isFocused=!1,this.destroyed=!1,this.isInitialized=!1,this.extensionStorage={},this.instanceId=Math.random().toString(36).slice(2,9),this.options={element:typeof document<"u"?document.createElement("div"):null,content:"",injectCSS:!0,injectNonce:void 0,extensions:[],autofocus:!1,editable:!0,textDirection:void 0,editorProps:{},parseOptions:{},coreExtensionOptions:{},enableInputRules:!0,enablePasteRules:!0,enableCoreExtensions:!0,enableContentCheck:!1,emitContentError:!1,onBeforeCreate:()=>null,onCreate:()=>null,onMount:()=>null,onUnmount:()=>null,onUpdate:()=>null,onSelectionUpdate:()=>null,onTransaction:()=>null,onFocus:()=>null,onBlur:()=>null,onDestroy:()=>null,onContentError:({error:t})=>{throw t},onPaste:()=>null,onDrop:()=>null,onDelete:()=>null,enableExtensionDispatchTransaction:!0},this.isCapturingTransaction=!1,this.capturedTransaction=null,this.utils={getUpdatedPosition:ld,createMappablePosition:ad},this.setOptions(n),this.createExtensionManager(),this.createCommandManager(),this.createSchema(),this.on("beforeCreate",this.options.onBeforeCreate),this.emit("beforeCreate",{editor:this}),this.on("mount",this.options.onMount),this.on("unmount",this.options.onUnmount),this.on("contentError",this.options.onContentError),this.on("create",this.options.onCreate),this.on("update",this.options.onUpdate),this.on("selectionUpdate",this.options.onSelectionUpdate),this.on("transaction",this.options.onTransaction),this.on("focus",this.options.onFocus),this.on("blur",this.options.onBlur),this.on("destroy",this.options.onDestroy),this.on("drop",({event:t,slice:r,moved:i})=>this.options.onDrop(t,r,i)),this.on("paste",({event:t,slice:r})=>this.options.onPaste(t,r)),this.on("delete",this.options.onDelete);let e=this.createDoc();if(!this.editorState){let t=Kr(e,this.options.autofocus);this.editorState=Mt.create({doc:e,schema:this.schema,selection:t||void 0})}this.options.element&&this.mount(this.options.element)}mount(n){if(typeof document>"u")throw new Error("[tiptap error]: The editor cannot be mounted because there is no 'document' defined in this environment.");this.createView(n),this.emit("mount",{editor:this}),this.css&&!document.head.contains(this.css)&&document.head.appendChild(this.css),window.setTimeout(()=>{this.isDestroyed||(this.options.autofocus!==!1&&this.options.autofocus!==null&&this.commands.focus(this.options.autofocus),this.emit("create",{editor:this}),this.isInitialized=!0)},0)}unmount(){if(this.editorView){let n=this.editorView.dom;n?.editor&&delete n.editor,this.editorView.destroy()}if(this.editorView=null,this.isInitialized=!1,this.css&&!document.querySelectorAll(`.${this.className}`).length)try{typeof this.css.remove=="function"?this.css.remove():this.css.parentNode&&this.css.parentNode.removeChild(this.css)}catch(n){console.warn("Failed to remove CSS element:",n)}this.css=null,this.emit("unmount",{editor:this})}get storage(){return this.extensionStorage}get commands(){return this.commandManager.commands}chain(){return this.commandManager.chain()}can(){return this.commandManager.can()}injectCSS(){this.options.injectCSS&&typeof document<"u"&&(this.css=Rd(eu,this.options.injectNonce))}setOptions(n={}){this.options={...this.options,...n},!(!this.editorView||!this.state||this.isDestroyed)&&(this.options.editorProps&&this.view.setProps(this.options.editorProps),this.view.updateState(this.state))}setEditable(n,e=!0){this.setOptions({editable:n}),e&&this.emit("update",{editor:this,transaction:this.state.tr,appendedTransactions:[]})}get isEditable(){return this.options.editable&&this.view&&this.view.editable}get view(){return this.editorView?this.editorView:new Proxy({state:this.editorState,updateState:n=>{this.editorState=n},dispatch:n=>{this.dispatchTransaction(n)},composing:!1,dragging:null,editable:!0,isDestroyed:!1},{get:(n,e)=>{if(this.editorView)return this.editorView[e];if(e==="state")return this.editorState;if(e in n)return Reflect.get(n,e);throw new Error(`[tiptap error]: The editor view is not available. Cannot access view['${e}']. The editor may not be mounted yet.`)}})}get state(){return this.editorView&&(this.editorState=this.view.state),this.editorState}registerPlugin(n,e){let t=Uo(e)?e(n,[...this.state.plugins]):[...this.state.plugins,n],r=this.state.reconfigure({plugins:t});return this.view.updateState(r),r}unregisterPlugin(n){if(this.isDestroyed)return;let e=this.state.plugins,t=e;if([].concat(n).forEach(i=>{let s=typeof i=="string"?`${i}$`:i.key;t=t.filter(o=>!o.key.startsWith(s))}),e.length===t.length)return;let r=this.state.reconfigure({plugins:t});return this.view.updateState(r),r}createExtensionManager(){var n,e,t,r;let s=[...this.options.enableCoreExtensions?[fl,ol.configure({blockSeparator:(e=(n=this.options.coreExtensionOptions)==null?void 0:n.clipboardTextSerializer)==null?void 0:e.blockSeparator}),ll,ul,hl,ml.configure({value:(r=(t=this.options.coreExtensionOptions)==null?void 0:t.tabindex)==null?void 0:r.value}),cl,pl,al,gl.configure({direction:this.options.textDirection})].filter(o=>typeof this.options.enableCoreExtensions=="object"?this.options.enableCoreExtensions[o.name]!==!1:!0):[],...this.options.extensions].filter(o=>["extension","node","mark"].includes(o?.type));this.extensionManager=new Nn(s,this)}createCommandManager(){this.commandManager=new Mn({editor:this})}createSchema(){this.schema=this.extensionManager.schema}createDoc(){let n;try{n=qr(this.options.content,this.schema,this.options.parseOptions,{errorOnInvalidContent:this.options.enableContentCheck})}catch(e){if(!(e instanceof Error)||!["[tiptap error]: Invalid JSON content","[tiptap error]: Invalid HTML content"].includes(e.message))throw e;let t=qr(this.options.content,this.schema,this.options.parseOptions,{errorOnInvalidContent:!1});return this.editorState=Mt.create({doc:t,schema:this.schema,selection:Kr(t,this.options.autofocus)||void 0}),this.emit("contentError",{editor:this,error:e,disableCollaboration:()=>{"collaboration"in this.storage&&typeof this.storage.collaboration=="object"&&this.storage.collaboration&&(this.storage.collaboration.isDisabled=!0),this.options.extensions=this.options.extensions.filter(r=>r.name!=="collaboration"),this.createExtensionManager()}}),this.editorState.doc}return n}createView(n){let{editorProps:e,enableExtensionDispatchTransaction:t}=this.options,r=e.dispatchTransaction||this.dispatchTransaction.bind(this),i=t?this.extensionManager.dispatchTransaction(r):r,s=e.transformPastedHTML,o=this.extensionManager.transformPastedHTML(s);this.editorView=new Ot(n,{...e,attributes:{role:"textbox",...e?.attributes},dispatchTransaction:i,transformPastedHTML:o,state:this.editorState,markViews:this.extensionManager.markViews,nodeViews:this.extensionManager.nodeViews});let l=this.state.reconfigure({plugins:this.extensionManager.plugins});this.view.updateState(l),this.prependClass(),this.injectCSS();let a=this.view.dom;a.editor=this}createNodeViews(){this.view.isDestroyed||this.view.setProps({markViews:this.extensionManager.markViews,nodeViews:this.extensionManager.nodeViews})}prependClass(){this.view.dom.className=`${this.className} ${this.view.dom.className}`}captureTransaction(n){this.isCapturingTransaction=!0,n(),this.isCapturingTransaction=!1;let e=this.capturedTransaction;return this.capturedTransaction=null,e}dispatchTransaction(n){if(this.view.isDestroyed)return;if(this.isCapturingTransaction){if(!this.capturedTransaction){this.capturedTransaction=n;return}n.steps.forEach(c=>{var f;return(f=this.capturedTransaction)==null?void 0:f.step(c)});return}let{state:e,transactions:t}=this.state.applyTransaction(n),r=!this.state.selection.eq(e.selection),i=t.includes(n),s=this.state;if(this.emit("beforeTransaction",{editor:this,transaction:n,nextState:e}),!i)return;this.view.updateState(e),this.emit("transaction",{editor:this,transaction:n,appendedTransactions:t.slice(1)}),r&&this.emit("selectionUpdate",{editor:this,transaction:n});let o=t.findLast(c=>c.getMeta("focus")||c.getMeta("blur")),l=o?.getMeta("focus"),a=o?.getMeta("blur");l&&this.emit("focus",{editor:this,event:l.event,transaction:o}),a&&this.emit("blur",{editor:this,event:a.event,transaction:o}),!(n.getMeta("preventUpdate")||!t.some(c=>c.docChanged)||s.doc.eq(e.doc))&&this.emit("update",{editor:this,transaction:n,appendedTransactions:t.slice(1)})}getAttributes(n){return td(this.state,n)}isActive(n,e){let t=typeof n=="string"?n:null,r=typeof n=="string"?e:n;return od(this.state,t,r)}getJSON(){return this.state.doc.toJSON()}getHTML(){return Zr(this.state.doc.content,this.schema)}getText(n){let{blockSeparator:e=`
+
+`,textSerializers:t={}}=n||{};return Zf(this.state.doc,{blockSeparator:e,textSerializers:{...Qo(this.schema),...t}})}get isEmpty(){return ei(this.state.doc)}destroy(){this.destroyed||(this.destroyed=!0,this.emit("destroy"),this.unmount(),this.removeAllListeners(),this.extensionManager.destroy(),this.extensionManager=null,this.schema=null,this.commandManager=null,this.extensionStorage={})}get isDestroyed(){var n,e;return(e=(n=this.editorView)==null?void 0:n.isDestroyed)!=null?e:!0}$node(n,e){var t;return((t=this.$doc)==null?void 0:t.querySelector(n,e))||null}$nodes(n,e){var t;return((t=this.$doc)==null?void 0:t.querySelectorAll(n,e))||null}$pos(n){let e=this.state.doc.resolve(n),t=n>0&&e.nodeAfter&&!e.nodeAfter.isText&&e.nodeAfter.isAtom?e.nodeAfter:null;return new Zd(e,this,!1,t)}get $doc(){return this.$pos(0)}};function sp(n){return new Tn({find:n.find,handler:({state:e,range:t,match:r})=>{let i=P(n.getAttributes,void 0,r);if(i===!1||i===null)return null;let{tr:s}=e,o=r[r.length-1],l=r[0];if(o){let a=l.search(/\S/),c=t.from+l.indexOf(o),f=c+o.length;if(Zo(t.from,t.to,e.doc).filter(h=>h.mark.type.excluded.find(m=>m===n.type&&m!==h.mark.type)).filter(h=>h.to>c).length)return null;ft.from&&s.delete(t.from+a,c);let u=t.from+a+o.length;s.addMark(t.from+a,u,n.type.create(i||{})),s.removeStoredMark(n.type)}},undoable:n.undoable})}function op(n){return new Tn({find:n.find,handler:({state:e,range:t,match:r})=>{let i=P(n.getAttributes,void 0,r)||{},{tr:s}=e,o=t.from,l=t.to,a=n.type.create(i);if(r[1]){let c=r[0].lastIndexOf(r[1]),f=o+c;f>l?f=l:l=f+r[1].length;let d=r[0][r[0].length-1];s.insertText(d,o+r[0].length-1),s.replaceWith(f,l,a)}else if(r[0]){let c=n.type.isInline?o:o-1;s.insert(c,n.type.create(i)).delete(s.mapping.map(o),s.mapping.map(l))}s.scrollIntoView()},undoable:n.undoable})}function lp(n){return new Tn({find:n.find,handler:({state:e,range:t,match:r})=>{let i=e.doc.resolve(t.from),s=P(n.getAttributes,void 0,r)||{};if(!i.node(-1).canReplaceWith(i.index(-1),i.indexAfter(-1),n.type))return null;e.tr.delete(t.from,t.to).setBlockType(t.from,t.from,n.type,s)},undoable:n.undoable})}function cp(n){return new Tn({find:n.find,handler:({state:e,range:t,match:r,chain:i})=>{let s=P(n.getAttributes,void 0,r)||{},o=e.tr.delete(t.from,t.to),a=o.doc.resolve(t.from).blockRange(),c=a&&et(a,n.type,s);if(!c)return null;if(o.wrap(a,c),n.keepMarks&&n.editor){let{selection:d,storedMarks:u}=e,{splittableMarks:h}=n.editor.extensionManager,p=u||d.$to.parentOffset&&d.$from.marks();if(p){let m=p.filter(g=>h.includes(g.type.name));o.ensureMarks(m)}}if(n.keepAttributes){let d=n.type.name==="bulletList"||n.type.name==="orderedList"?"listItem":"taskList";i().updateAttributes(d,s).run()}let f=o.doc.resolve(t.from-1).nodeBefore;f&&f.type===n.type&&re(o.doc,t.from-1)&&(!n.joinPredicate||n.joinPredicate(r,f))&&o.join(t.from-1)},undoable:n.undoable})}var tu=n=>"touches"in n,fp=class{constructor(n){this.directions=["bottom-left","bottom-right","top-left","top-right"],this.minSize={height:8,width:8},this.preserveAspectRatio=!1,this.classNames={container:"",wrapper:"",handle:"",resizing:""},this.initialWidth=0,this.initialHeight=0,this.aspectRatio=1,this.isResizing=!1,this.activeHandle=null,this.startX=0,this.startY=0,this.startWidth=0,this.startHeight=0,this.isShiftKeyPressed=!1,this.lastEditableState=void 0,this.handleMap=new Map,this.handleMouseMove=l=>{if(!this.isResizing||!this.activeHandle)return;let a=l.clientX-this.startX,c=l.clientY-this.startY;this.handleResize(a,c)},this.handleTouchMove=l=>{if(!this.isResizing||!this.activeHandle)return;let a=l.touches[0];if(!a)return;let c=a.clientX-this.startX,f=a.clientY-this.startY;this.handleResize(c,f)},this.handleMouseUp=()=>{if(!this.isResizing)return;let l=this.element.offsetWidth,a=this.element.offsetHeight;this.onCommit(l,a),this.isResizing=!1,this.activeHandle=null,this.container.dataset.resizeState="false",this.classNames.resizing&&this.container.classList.remove(this.classNames.resizing),document.removeEventListener("mousemove",this.handleMouseMove),document.removeEventListener("mouseup",this.handleMouseUp),document.removeEventListener("keydown",this.handleKeyDown),document.removeEventListener("keyup",this.handleKeyUp)},this.handleKeyDown=l=>{l.key==="Shift"&&(this.isShiftKeyPressed=!0)},this.handleKeyUp=l=>{l.key==="Shift"&&(this.isShiftKeyPressed=!1)};var e,t,r,i,s,o;this.node=n.node,this.editor=n.editor,this.element=n.element,this.element.draggable=!1,this.contentElement=n.contentElement,this.getPos=n.getPos,this.onResize=n.onResize,this.onCommit=n.onCommit,this.onUpdate=n.onUpdate,(e=n.options)!=null&&e.min&&(this.minSize={...this.minSize,...n.options.min}),(t=n.options)!=null&&t.max&&(this.maxSize=n.options.max),(r=n?.options)!=null&&r.directions&&(this.directions=n.options.directions),(i=n.options)!=null&&i.preserveAspectRatio&&(this.preserveAspectRatio=n.options.preserveAspectRatio),(s=n.options)!=null&&s.className&&(this.classNames={container:n.options.className.container||"",wrapper:n.options.className.wrapper||"",handle:n.options.className.handle||"",resizing:n.options.className.resizing||""}),(o=n.options)!=null&&o.createCustomHandle&&(this.createCustomHandle=n.options.createCustomHandle),this.wrapper=this.createWrapper(),this.container=this.createContainer(),this.applyInitialSize(),this.attachHandles(),this.editor.on("update",this.handleEditorUpdate.bind(this))}get dom(){return this.container}get contentDOM(){var n;return(n=this.contentElement)!=null?n:null}handleEditorUpdate(){let n=this.editor.isEditable;n!==this.lastEditableState&&(this.lastEditableState=n,n?n&&this.handleMap.size===0&&this.attachHandles():this.removeHandles())}update(n,e,t){return n.type!==this.node.type?!1:(this.node=n,this.onUpdate?this.onUpdate(n,e,t):!0)}destroy(){this.isResizing&&(this.container.dataset.resizeState="false",this.classNames.resizing&&this.container.classList.remove(this.classNames.resizing),document.removeEventListener("mousemove",this.handleMouseMove),document.removeEventListener("mouseup",this.handleMouseUp),document.removeEventListener("keydown",this.handleKeyDown),document.removeEventListener("keyup",this.handleKeyUp),this.isResizing=!1,this.activeHandle=null),this.editor.off("update",this.handleEditorUpdate.bind(this)),this.container.remove()}createContainer(){let n=document.createElement("div");return n.dataset.resizeContainer="",n.dataset.node=this.node.type.name,n.style.display=this.node.type.isInline?"inline-flex":"flex",this.classNames.container&&(n.className=this.classNames.container),n.appendChild(this.wrapper),n}createWrapper(){let n=document.createElement("div");return n.style.position="relative",n.style.display="block",n.dataset.resizeWrapper="",this.classNames.wrapper&&(n.className=this.classNames.wrapper),n.appendChild(this.element),n}createHandle(n){let e=document.createElement("div");return e.dataset.resizeHandle=n,e.style.position="absolute",this.classNames.handle&&(e.className=this.classNames.handle),e}positionHandle(n,e){let t=e.includes("top"),r=e.includes("bottom"),i=e.includes("left"),s=e.includes("right");t&&(n.style.top="0"),r&&(n.style.bottom="0"),i&&(n.style.left="0"),s&&(n.style.right="0"),(e==="top"||e==="bottom")&&(n.style.left="0",n.style.right="0"),(e==="left"||e==="right")&&(n.style.top="0",n.style.bottom="0")}attachHandles(){this.directions.forEach(n=>{let e;this.createCustomHandle?e=this.createCustomHandle(n):e=this.createHandle(n),e instanceof HTMLElement||(console.warn(`[ResizableNodeView] createCustomHandle("${n}") did not return an HTMLElement. Falling back to default handle.`),e=this.createHandle(n)),this.createCustomHandle||this.positionHandle(e,n),e.addEventListener("mousedown",t=>this.handleResizeStart(t,n)),e.addEventListener("touchstart",t=>this.handleResizeStart(t,n)),this.handleMap.set(n,e),this.wrapper.appendChild(e)})}removeHandles(){this.handleMap.forEach(n=>n.remove()),this.handleMap.clear()}applyInitialSize(){let n=this.node.attrs.width,e=this.node.attrs.height;n?(this.element.style.width=`${n}px`,this.initialWidth=n):this.initialWidth=this.element.offsetWidth,e?(this.element.style.height=`${e}px`,this.initialHeight=e):this.initialHeight=this.element.offsetHeight,this.initialWidth>0&&this.initialHeight>0&&(this.aspectRatio=this.initialWidth/this.initialHeight)}handleResizeStart(n,e){n.preventDefault(),n.stopPropagation(),this.isResizing=!0,this.activeHandle=e,tu(n)?(this.startX=n.touches[0].clientX,this.startY=n.touches[0].clientY):(this.startX=n.clientX,this.startY=n.clientY),this.startWidth=this.element.offsetWidth,this.startHeight=this.element.offsetHeight,this.startWidth>0&&this.startHeight>0&&(this.aspectRatio=this.startWidth/this.startHeight);let t=this.getPos();this.container.dataset.resizeState="true",this.classNames.resizing&&this.container.classList.add(this.classNames.resizing),document.addEventListener("mousemove",this.handleMouseMove),document.addEventListener("touchmove",this.handleTouchMove),document.addEventListener("mouseup",this.handleMouseUp),document.addEventListener("keydown",this.handleKeyDown),document.addEventListener("keyup",this.handleKeyUp)}handleResize(n,e){if(!this.activeHandle)return;let t=this.preserveAspectRatio||this.isShiftKeyPressed,{width:r,height:i}=this.calculateNewDimensions(this.activeHandle,n,e),s=this.applyConstraints(r,i,t);this.element.style.width=`${s.width}px`,this.element.style.height=`${s.height}px`,this.onResize&&this.onResize(s.width,s.height)}calculateNewDimensions(n,e,t){let r=this.startWidth,i=this.startHeight,s=n.includes("right"),o=n.includes("left"),l=n.includes("bottom"),a=n.includes("top");return s?r=this.startWidth+e:o&&(r=this.startWidth-e),l?i=this.startHeight+t:a&&(i=this.startHeight-t),(n==="right"||n==="left")&&(r=this.startWidth+(s?e:-e)),(n==="top"||n==="bottom")&&(i=this.startHeight+(l?t:-t)),this.preserveAspectRatio||this.isShiftKeyPressed?this.applyAspectRatio(r,i,n):{width:r,height:i}}applyConstraints(n,e,t){var r,i,s,o;if(!t){let c=Math.max(this.minSize.width,n),f=Math.max(this.minSize.height,e);return(r=this.maxSize)!=null&&r.width&&(c=Math.min(this.maxSize.width,c)),(i=this.maxSize)!=null&&i.height&&(f=Math.min(this.maxSize.height,f)),{width:c,height:f}}let l=n,a=e;return lthis.maxSize.width&&(l=this.maxSize.width,a=l/this.aspectRatio),(o=this.maxSize)!=null&&o.height&&a>this.maxSize.height&&(a=this.maxSize.height,l=a*this.aspectRatio),{width:l,height:a}}applyAspectRatio(n,e,t){let r=t==="left"||t==="right",i=t==="top"||t==="bottom";return r?{width:n,height:n/this.aspectRatio}:i?{width:e*this.aspectRatio,height:e}:{width:n,height:n/this.aspectRatio}}};var dp=class yl extends ri{constructor(){super(...arguments),this.type="node"}static create(e={}){let t=typeof e=="function"?e():e;return new yl(t)}configure(e){return super.configure(e)}extend(e){let t=typeof e=="function"?e():e;return super.extend(t)}};function hp(n){return new qd({find:n.find,handler:({state:e,range:t,match:r,pasteEvent:i})=>{let s=P(n.getAttributes,void 0,r,i);if(s===!1||s===null)return null;let{tr:o}=e,l=r[r.length-1],a=r[0],c=t.to;if(l){let f=a.search(/\S/),d=t.from+a.indexOf(l),u=d+l.length;if(Zo(t.from,t.to,e.doc).filter(m=>m.mark.type.excluded.find(y=>y===n.type&&y!==m.mark.type)).filter(m=>m.to>d).length)return null;ut.from&&o.delete(t.from+f,d),c=t.from+f+l.length,o.addMark(t.from+f,c,n.type.create(s||{})),r.index!==void 0&&r.input!==void 0&&r.index+r[0].length>=r.input.length||o.removeStoredMark(n.type)}}})}export{b as a,x as b,Ln as c,Wi as d,Ze as e,E as f,Gt as g,T as h,C as i,I as j,L as k,Ae as l,ae as m,Gc as n,V as o,Ft as p,_f as q,mh as r,Kf as s,M as t,P as u,Yf as v,Sn as w,td as x,id as y,Zo as z,kh as A,Mh as B,wh as C,ei as D,Th as E,Jh as F,jh as G,Wd as H,Hd as I,Tn as J,Kd as K,qd as L,ue as M,ip as N,sp as O,op as P,lp as Q,cp as R,fp as S,dp as T,hp as U};
diff --git a/packages/forms/dist/tiptap/chunk-CFHSZ3VY.js b/packages/forms/dist/tiptap/chunk-CFHSZ3VY.js
deleted file mode 100644
index 7e06d0ed..00000000
--- a/packages/forms/dist/tiptap/chunk-CFHSZ3VY.js
+++ /dev/null
@@ -1,89 +0,0 @@
-function J(n){this.content=n}J.prototype={constructor:J,find:function(n){for(var e=0;e>1}};J.from=function(n){if(n instanceof J)return n;var e=[];if(n)for(var t in n)e.push(t,n[t]);return new J(e)};var gn=J;function ti(n,e,t){for(let r=0;;r++){if(r==n.childCount||r==e.childCount)return n.childCount==e.childCount?null:t;let i=n.child(r),s=e.child(r);if(i==s){t+=i.nodeSize;continue}if(!i.sameMarkup(s))return t;if(i.isText&&i.text!=s.text){for(let o=0;i.text[o]==s.text[o];o++)t++;return t}if(i.content.size||s.content.size){let o=ti(i.content,s.content,t+1);if(o!=null)return o}t+=i.nodeSize}}function ni(n,e,t,r){for(let i=n.childCount,s=e.childCount;;){if(i==0||s==0)return i==s?null:{a:t,b:r};let o=n.child(--i),l=e.child(--s),a=o.nodeSize;if(o==l){t-=a,r-=a;continue}if(!o.sameMarkup(l))return{a:t,b:r};if(o.isText&&o.text!=l.text){let c=0,f=Math.min(o.text.length,l.text.length);for(;ce&&r(a,i+l,s||null,o)!==!1&&a.content.size){let f=l+1;a.nodesBetween(Math.max(0,e-f),Math.min(a.content.size,t-f),r,i+f)}l=c}}descendants(e){this.nodesBetween(0,this.size,e)}textBetween(e,t,r,i){let s="",o=!0;return this.nodesBetween(e,t,(l,a)=>{let c=l.isText?l.text.slice(Math.max(e,a)-a,t-a):l.isLeaf?i?typeof i=="function"?i(l):i:l.type.spec.leafText?l.type.spec.leafText(l):"":"";l.isBlock&&(l.isLeaf&&c||l.isTextblock)&&r&&(o?o=!1:s+=r),s+=c},0),s}append(e){if(!e.size)return this;if(!this.size)return e;let t=this.lastChild,r=e.firstChild,i=this.content.slice(),s=0;for(t.isText&&t.sameMarkup(r)&&(i[i.length-1]=t.withText(t.text+r.text),s=1);se)for(let s=0,o=0;oe&&((ot)&&(l.isText?l=l.cut(Math.max(0,e-o),Math.min(l.text.length,t-o)):l=l.cut(Math.max(0,e-o-1),Math.min(l.content.size,t-o-1))),r.push(l),i+=l.nodeSize),o=a}return new n(r,i)}cutByIndex(e,t){return e==t?n.empty:e==0&&t==this.content.length?this:new n(this.content.slice(e,t))}replaceChild(e,t){let r=this.content[e];if(r==t)return this;let i=this.content.slice(),s=this.size+t.nodeSize-r.nodeSize;return i[e]=t,new n(i,s)}addToStart(e){return new n([e].concat(this.content),this.size+e.nodeSize)}addToEnd(e){return new n(this.content.concat(e),this.size+e.nodeSize)}eq(e){if(this.content.length!=e.content.length)return!1;for(let t=0;tthis.size||e<0)throw new RangeError(`Position ${e} outside of fragment (${this})`);for(let t=0,r=0;;t++){let i=this.child(t),s=r+i.nodeSize;if(s>=e)return s==e?Et(t+1,s):Et(t,r);r=s}}toString(){return"<"+this.toStringInner()+">"}toStringInner(){return this.content.join(", ")}toJSON(){return this.content.length?this.content.map(e=>e.toJSON()):null}static fromJSON(e,t){if(!t)return n.empty;if(!Array.isArray(t))throw new RangeError("Invalid input for Fragment.fromJSON");return n.fromArray(t.map(e.nodeFromJSON))}static fromArray(e){if(!e.length)return n.empty;let t,r=0;for(let i=0;ithis.type.rank&&(t||(t=e.slice(0,i)),t.push(this),r=!0),t&&t.push(s)}}return t||(t=e.slice()),r||t.push(this),t}removeFromSet(e){for(let t=0;tr.type.rank-i.type.rank),t}};E.none=[];var Ee=class extends Error{},k=class n{constructor(e,t,r){this.content=e,this.openStart=t,this.openEnd=r}get size(){return this.content.size-this.openStart-this.openEnd}insertAt(e,t){let r=ii(this.content,e+this.openStart,t,this.openStart+1,this.openEnd+1);return r&&new n(r,this.openStart,this.openEnd)}removeBetween(e,t){return new n(ri(this.content,e+this.openStart,t+this.openStart),this.openStart,this.openEnd)}eq(e){return this.content.eq(e.content)&&this.openStart==e.openStart&&this.openEnd==e.openEnd}toString(){return this.content+"("+this.openStart+","+this.openEnd+")"}toJSON(){if(!this.content.size)return null;let e={content:this.content.toJSON()};return this.openStart>0&&(e.openStart=this.openStart),this.openEnd>0&&(e.openEnd=this.openEnd),e}static fromJSON(e,t){if(!t)return n.empty;let r=t.openStart||0,i=t.openEnd||0;if(typeof r!="number"||typeof i!="number")throw new RangeError("Invalid input for Slice.fromJSON");return new n(b.fromJSON(e,t.content),r,i)}static maxOpen(e,t=!0){let r=0,i=0;for(let s=e.firstChild;s&&!s.isLeaf&&(t||!s.type.spec.isolating);s=s.firstChild)r++;for(let s=e.lastChild;s&&!s.isLeaf&&(t||!s.type.spec.isolating);s=s.lastChild)i++;return new n(e,r,i)}};k.empty=new k(b.empty,0,0);function ri(n,e,t){let{index:r,offset:i}=n.findIndex(e),s=n.maybeChild(r),{index:o,offset:l}=n.findIndex(t);if(i==e||s.isText){if(l!=t&&!n.child(o).isText)throw new RangeError("Removing non-flat range");return n.cut(0,e).append(n.cut(t))}if(r!=o)throw new RangeError("Removing non-flat range");return n.replaceChild(r,s.copy(ri(s.content,e-i-1,t-i-1)))}function ii(n,e,t,r,i,s){let{index:o,offset:l}=n.findIndex(e),a=n.maybeChild(o);if(l==e||a.isText)return s&&r<=0&&i<=0&&!s.canReplace(o,o,t)?null:n.cut(0,e).append(t).append(n.cut(e));let c=ii(a.content,e-l-1,t,o==0?r-1:0,o==n.childCount-1?i-1:0,a);return c&&n.replaceChild(o,a.copy(c))}function $o(n,e,t){if(t.openStart>n.depth)throw new Ee("Inserted content deeper than insertion position");if(n.depth-t.openStart!=e.depth-t.openEnd)throw new Ee("Inconsistent open depths");return si(n,e,t,0)}function si(n,e,t,r){let i=n.index(r),s=n.node(r);if(i==e.index(r)&&r=0&&n.isText&&n.sameMarkup(e[t])?e[t]=n.withText(e[t].text+n.text):e.push(n)}function st(n,e,t,r){let i=(e||n).node(t),s=0,o=e?e.index(t):i.childCount;n&&(s=n.index(t),n.depth>t?s++:n.textOffset&&(Ne(n.nodeAfter,r),s++));for(let l=s;li&&bn(n,e,i+1),o=r.depth>i&&bn(t,r,i+1),l=[];return st(null,n,i,l),s&&o&&e.index(i)==t.index(i)?(oi(s,o),Ne(Te(s,li(n,e,t,r,i+1)),l)):(s&&Ne(Te(s,Rt(n,e,i+1)),l),st(e,t,i,l),o&&Ne(Te(o,Rt(t,r,i+1)),l)),st(r,null,i,l),new b(l)}function Rt(n,e,t){let r=[];if(st(null,n,t,r),n.depth>t){let i=bn(n,e,t+1);Ne(Te(i,Rt(n,e,t+1)),r)}return st(e,null,t,r),new b(r)}function Lo(n,e){let t=e.depth-n.openStart,i=e.node(t).copy(n.content);for(let s=t-1;s>=0;s--)i=e.node(s).copy(b.from(i));return{start:i.resolveNoCache(n.openStart+t),end:i.resolveNoCache(i.content.size-n.openEnd-t)}}var vt=class n{constructor(e,t,r){this.pos=e,this.path=t,this.parentOffset=r,this.depth=t.length/3-1}resolveDepth(e){return e==null?this.depth:e<0?this.depth+e:e}get parent(){return this.node(this.depth)}get doc(){return this.node(0)}node(e){return this.path[this.resolveDepth(e)*3]}index(e){return this.path[this.resolveDepth(e)*3+1]}indexAfter(e){return e=this.resolveDepth(e),this.index(e)+(e==this.depth&&!this.textOffset?0:1)}start(e){return e=this.resolveDepth(e),e==0?0:this.path[e*3-1]+1}end(e){return e=this.resolveDepth(e),this.start(e)+this.node(e).content.size}before(e){if(e=this.resolveDepth(e),!e)throw new RangeError("There is no position before the top-level node");return e==this.depth+1?this.pos:this.path[e*3-1]}after(e){if(e=this.resolveDepth(e),!e)throw new RangeError("There is no position after the top-level node");return e==this.depth+1?this.pos:this.path[e*3-1]+this.path[e*3].nodeSize}get textOffset(){return this.pos-this.path[this.path.length-1]}get nodeAfter(){let e=this.parent,t=this.index(this.depth);if(t==e.childCount)return null;let r=this.pos-this.path[this.path.length-1],i=e.child(t);return r?e.child(t).cut(r):i}get nodeBefore(){let e=this.index(this.depth),t=this.pos-this.path[this.path.length-1];return t?this.parent.child(e).cut(0,t):e==0?null:this.parent.child(e-1)}posAtIndex(e,t){t=this.resolveDepth(t);let r=this.path[t*3],i=t==0?0:this.path[t*3-1]+1;for(let s=0;s0;t--)if(this.start(t)<=e&&this.end(t)>=e)return t;return 0}blockRange(e=this,t){if(e.pos=0;r--)if(e.pos<=this.end(r)&&(!t||t(this.node(r))))return new De(this,e,r);return null}sameParent(e){return this.pos-this.parentOffset==e.pos-e.parentOffset}max(e){return e.pos>this.pos?e:this}min(e){return e.pos=0&&t<=e.content.size))throw new RangeError("Position "+t+" out of range");let r=[],i=0,s=t;for(let o=e;;){let{index:l,offset:a}=o.content.findIndex(s),c=s-a;if(r.push(o,l,i+a),!c||(o=o.child(l),o.isText))break;s=c-1,i+=a+1}return new n(t,r,s)}static resolveCached(e,t){let r=Hr.get(e);if(r)for(let s=0;se&&this.nodesBetween(e,t,s=>(r.isInSet(s.marks)&&(i=!0),!i)),i}get isBlock(){return this.type.isBlock}get isTextblock(){return this.type.isTextblock}get inlineContent(){return this.type.inlineContent}get isInline(){return this.type.isInline}get isText(){return this.type.isText}get isLeaf(){return this.type.isLeaf}get isAtom(){return this.type.isAtom}toString(){if(this.type.spec.toDebugString)return this.type.spec.toDebugString(this);let e=this.type.name;return this.content.size&&(e+="("+this.content.toStringInner()+")"),ai(this.marks,e)}contentMatchAt(e){let t=this.type.contentMatch.matchFragment(this.content,0,e);if(!t)throw new Error("Called contentMatchAt on a node with invalid content");return t}canReplace(e,t,r=b.empty,i=0,s=r.childCount){let o=this.contentMatchAt(e).matchFragment(r,i,s),l=o&&o.matchFragment(this.content,t);if(!l||!l.validEnd)return!1;for(let a=i;at.type.name)}`);this.content.forEach(t=>t.check())}toJSON(){let e={type:this.type.name};for(let t in this.attrs){e.attrs=this.attrs;break}return this.content.size&&(e.content=this.content.toJSON()),this.marks.length&&(e.marks=this.marks.map(t=>t.toJSON())),e}static fromJSON(e,t){if(!t)throw new RangeError("Invalid input for Node.fromJSON");let r;if(t.marks){if(!Array.isArray(t.marks))throw new RangeError("Invalid mark data for Node.fromJSON");r=t.marks.map(e.markFromJSON)}if(t.type=="text"){if(typeof t.text!="string")throw new RangeError("Invalid text node in JSON");return e.text(t.text,r)}let i=b.fromJSON(e,t.content),s=e.nodeType(t.type).create(t.attrs,i,r);return s.type.checkAttrs(s.attrs),s}};Y.prototype.text=void 0;var Sn=class n extends Y{constructor(e,t,r,i){if(super(e,t,null,i),!r)throw new RangeError("Empty text nodes are not allowed");this.text=r}toString(){return this.type.spec.toDebugString?this.type.spec.toDebugString(this):ai(this.marks,JSON.stringify(this.text))}get textContent(){return this.text}textBetween(e,t){return this.text.slice(e,t)}get nodeSize(){return this.text.length}mark(e){return e==this.marks?this:new n(this.type,this.attrs,this.text,e)}withText(e){return e==this.text?this:new n(this.type,this.attrs,e,this.marks)}cut(e=0,t=this.text.length){return e==0&&t==this.text.length?this:this.withText(this.text.slice(e,t))}eq(e){return this.sameMarkup(e)&&this.text==e.text}toJSON(){let e=super.toJSON();return e.text=this.text,e}};function ai(n,e){for(let t=n.length-1;t>=0;t--)e=n[t].type.name+"("+e+")";return e}var Ae=class n{constructor(e){this.validEnd=e,this.next=[],this.wrapCache=[]}static parse(e,t){let r=new xn(e,t);if(r.next==null)return n.empty;let i=ci(r);r.next&&r.err("Unexpected trailing text");let s=_o(Uo(i));return Go(s,r),s}matchType(e){for(let t=0;tc.createAndFill()));for(let c=0;c=this.next.length)throw new RangeError(`There's no ${e}th edge in this content match`);return this.next[e]}toString(){let e=[];function t(r){e.push(r);for(let i=0;i{let s=i+(r.validEnd?"*":" ")+" ";for(let o=0;o"+e.indexOf(r.next[o].next);return s}).join(`
-`)}};Ae.empty=new Ae(!0);var xn=class{constructor(e,t){this.string=e,this.nodeTypes=t,this.inline=null,this.pos=0,this.tokens=e.split(/\s*(?=\b|\W|$)/),this.tokens[this.tokens.length-1]==""&&this.tokens.pop(),this.tokens[0]==""&&this.tokens.shift()}get next(){return this.tokens[this.pos]}eat(e){return this.next==e&&(this.pos++||!0)}err(e){throw new SyntaxError(e+" (in content expression '"+this.string+"')")}};function ci(n){let e=[];do e.push(Jo(n));while(n.eat("|"));return e.length==1?e[0]:{type:"choice",exprs:e}}function Jo(n){let e=[];do e.push(jo(n));while(n.next&&n.next!=")"&&n.next!="|");return e.length==1?e[0]:{type:"seq",exprs:e}}function jo(n){let e=Ho(n);for(;;)if(n.eat("+"))e={type:"plus",expr:e};else if(n.eat("*"))e={type:"star",expr:e};else if(n.eat("?"))e={type:"opt",expr:e};else if(n.eat("{"))e=qo(n,e);else break;return e}function Ur(n){/\D/.test(n.next)&&n.err("Expected number, got '"+n.next+"'");let e=Number(n.next);return n.pos++,e}function qo(n,e){let t=Ur(n),r=t;return n.eat(",")&&(n.next!="}"?r=Ur(n):r=-1),n.eat("}")||n.err("Unclosed braced range"),{type:"range",min:t,max:r,expr:e}}function Ko(n,e){let t=n.nodeTypes,r=t[e];if(r)return[r];let i=[];for(let s in t){let o=t[s];o.isInGroup(e)&&i.push(o)}return i.length==0&&n.err("No node type or group '"+e+"' found"),i}function Ho(n){if(n.eat("(")){let e=ci(n);return n.eat(")")||n.err("Missing closing paren"),e}else if(/\W/.test(n.next))n.err("Unexpected token '"+n.next+"'");else{let e=Ko(n,n.next).map(t=>(n.inline==null?n.inline=t.isInline:n.inline!=t.isInline&&n.err("Mixing inline and block content"),{type:"name",value:t}));return n.pos++,e.length==1?e[0]:{type:"choice",exprs:e}}}function Uo(n){let e=[[]];return i(s(n,0),t()),e;function t(){return e.push([])-1}function r(o,l,a){let c={term:a,to:l};return e[o].push(c),c}function i(o,l){o.forEach(a=>a.to=l)}function s(o,l){if(o.type=="choice")return o.exprs.reduce((a,c)=>a.concat(s(c,l)),[]);if(o.type=="seq")for(let a=0;;a++){let c=s(o.exprs[a],l);if(a==o.exprs.length-1)return c;i(c,l=t())}else if(o.type=="star"){let a=t();return r(l,a),i(s(o.expr,a),a),[r(a)]}else if(o.type=="plus"){let a=t();return i(s(o.expr,l),a),i(s(o.expr,a),a),[r(a)]}else{if(o.type=="opt")return[r(l)].concat(s(o.expr,l));if(o.type=="range"){let a=l;for(let c=0;c{n[o].forEach(({term:l,to:a})=>{if(!l)return;let c;for(let f=0;f{c||i.push([l,c=[]]),c.indexOf(f)==-1&&c.push(f)})})});let s=e[r.join(",")]=new Ae(r.indexOf(n.length-1)>-1);for(let o=0;o