Restructure the Automerge editor's single livePreview.ts into per-node
CodeMirror extensions composed by livePreview(), mirroring the reams
reference. Extends coverage from 4 node types to the core CommonMark +
GFM set (no widgets):
- headings: ported as-is
- bold / italic / strikethrough: content classes + proximity-revealed
markers, registered atomic so cursor motion skips hidden markers
- inline code + fenced code blocks: content/line styling + fence hiding
- links: fix the broken handler (hide all brackets/paren/URL, style the
label) + cmd/ctrl-click to open externally via a new shell.openExternal
IPC bridge
- blockquotes, horizontal rules, lists: line decorations + prefix hiding
Splits the single cm-formatting-hidden class into idiom-correct hide
classes (per the CM6 cursor-skip contract) and activates the previously
dead CSS. Adds headless builder tests (pure over EditorState) covering
each node type.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Drag a file in the sidebar onto a directory (or the vault root) to move it.
- FileSystemProvider gains moveFile(entryId, targetDirId) and a rootId.
Local implementation renames across directories (guards same-location
and name conflicts); the synced provider syncs afterward like rename.
- VaultTree: files are draggable (independent off-screen drag ghost);
every row carries data-drop-dir for its enclosing folder, and the tree
container resolves the drop target via closest() — so it picks the
deepest folder under the cursor, falls through to the root over empty
space / root-level files, and clears reliably. The target directory's
whole subtree (or the whole tree, for the root) is highlighted.
- AppSidebar: handleMove calls provider.moveFile then refreshes the tree.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Split Right / Split Down move from the bottom bar to the top bar as
icon buttons (SplitRightIcon / SplitDownIcon) with hover tooltips, left
of the workspace selector, shown only in tiling mode. Wired through the
tiling handle (App.handleSplitRight/Down).
- Title bar: compact fixed-height buttons (size-5) with header padding so
hover highlights no longer reach the screen edge; tooltip + pointer
cursor on the sidebar toggle, split buttons, and workspace menu.
- Sidebar: fix asymmetric collapse animation — the open state now also
transitions width, so re-opening animates instead of snapping.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Drag tab chips to reorder, move between tiles, or split.
- tabsModel.moveTab: reorder within / move across tiles (pure + tested);
creates the destination tile if absent and reports sourceEmptied.
- tabDnd: shared drag payload + computeDropZone (5 zones; pure + tested).
- model.splitLeaf gains `before` so a drop on a left/top edge puts the
new tile first.
- tiling.tsx: TilingTile shows a translucent zone overlay (raised via a
capture overlay so the editor can't swallow drag events); dropping on
an edge splits the tile and moves the tab; center moves it in.
- TileTabStrip: draggable chips with an insertion marker, cleared on
dragleave/dragend; an independent off-screen clone is used as the drag
image so the clipped live chip can't corrupt it. Thin, non-overlay tab
scrollbar (TileTabStrip.css).
Fixes found while testing:
- White-screen crash: read currentTarget synchronously, not inside a
setState updater (React nulls it after the handler).
- Tiles overlapped: split panes now flex-grow to share the space the
fixed splitter leaves, so panes + divider sum to 100% with no
hardcoded splitter width.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Each vault's tile layout and tabs now survive an app restart, along with
the selected vault and (pre-existing) workspace kind.
- tilingPersistence: serializeLayout / buildInitialTileTabs / maxIdSuffix
(pure + tested) and load/save to localStorage under
textile.tiling.<vaultId>. Persists the split tree, active tile, and
each tile's documents + active index; not handles or load state.
- useTilingDocumentSlots: accept initialTiles, seed the tab map with
loading tabs, and re-open the documents in a mount-only effect
(cancelled on unmount). Exposes tileTabs for serialization.
- TilingWorkspace: lazy-init tree/activeTile/id-counter from the
persisted layout (keyed by instanceKey = vault provider id) and write
on change (skipping no-op rewrites).
- App: persist + restore selectedProviderId (textile.selectedVault); the
registry loads vaults synchronously so the restored vault survives the
auto-select effects.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Tab management via right-click, a shared context-menu provider, and a
sidebar action to open a file in a new tile.
- tabsModel: closeOthers / closeToRight / closeToLeft (anchor survives)
and closeAllOfFile (across tiles, reports emptiedTileIds), all pure +
tested. Shared slotShowsRequest helper.
- TileTabStrip: right-click -> native context menu of close actions
(unified onTabAction dispatcher). useTilingDocumentSlots gains the
matching close methods; tiling.tsx collapses tiles emptied by
close-all-of-file.
- platform/contextMenu: ContextMenuProvider + useContextMenu inject the
context-menu capability (electron impl is the one place touching
window.textile). Provider mounted at the App composition root; TileTabStrip
and the sidebar consume via the hook instead of window.
- Sidebar file menu: "Open in new tile to the right/below" splits the
active tile then opens the file (App.handleOpenInSplit).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Wire tab/tile actions into the command palette and hotkey system.
- TilingHandle + TilingActionSet gain new-tab (Mod+T), close-tab,
next-tab (Mod+Alt+Right), prev-tab (Mod+Alt+Left). Close-tab has no
default binding since Mod+W is owned by the native window menu.
- tabsModel.cycleActiveTab: pure wrap-around tab cycling (+ tests).
useTilingDocumentSlots exposes cycleTab.
- tiling.tsx: the imperative handle now delegates to a per-render
handleImplRef, so every tiling/tab command runs against the latest
tree / activeTileId / tab state (previously only splits did).
- formatHotkeyForDisplay: render special keys as glyphs (arrows, Enter,
Tab, etc.) so the palette shows arrow icons, not "ARROWLEFT".
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>