npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@markwhen/codemirror-tables

v0.1.1

Published

Rich WYSIWYG table editing extension for CodeMirror 6

Readme

@markwhen/codemirror-tables

A standalone CodeMirror 6 extension for rendering and editing GitHub Flavored Markdown (GFM) tables with an interactive widget interface.

Originally extracted from Joplin's rich markdown editor.

Features

  • Visual Table Rendering: Markdown tables are rendered as interactive HTML tables
  • Inline Cell Editing: Click any cell to edit its contents with a nested CodeMirror editor
  • Floating Toolbar: Context-aware toolbar with table manipulation commands
  • Keyboard Navigation: Tab, Shift+Tab, Enter, and arrow keys for cell navigation
  • Table Operations: Insert/delete rows and columns, change alignment, apply formatting
  • Markdown Support: Full markdown rendering in cells (bold, italic, links, code, etc.)

Installation

npm install @markwhen/codemirror-tables

Basic Usage

import { EditorState } from "@codemirror/state";
import { EditorView } from "@codemirror/view";
import { markdown } from "@codemirror/lang-markdown";
import { tableExtension } from "@markwhen/codemirror-tables";

const state = EditorState.create({
  doc: `# My Document

| Name  | Age | City |
|-------|-----|------|
| Alice | 30  | NYC  |
| Bob   | 25  | LA   |

Some text after the table.`,
  extensions: [
    markdown(),
    tableExtension(),
  ],
});

const view = new EditorView({
  state,
  parent: document.getElementById("editor"),
});

Configuration

The createTableExtension function accepts an optional configuration object:

import { createTableExtension } from "@markwhen/codemirror-tables";
import { keymap } from "@codemirror/view";
import { myCustomTheme } from "./myTheme";

const extensions = [
  markdown(),
  createTableExtension({
    // Custom link click handler (default opens in new tab)
    onLinkClick: (href: string) => {
      window.open(href, "_blank");
    },
    
    // Additional extensions for nested cell editors
    cellEditorExtensions: [
      myCustomTheme,
      keymap.of([/* custom keybindings */]),
    ],
    
    // Enable debug logging
    debug: false,
  }),
];

Configuration Options

| Option | Type | Description | |--------|------|-------------| | onLinkClick | (href: string) => void | Custom handler for link clicks within table cells | | cellEditorExtensions | Extension \| Extension[] | Additional CodeMirror extensions for nested cell editors (themes, keymaps, etc.) | | debug | boolean | Enable debug logging (default: false) |

Keyboard Shortcuts

When editing a cell:

| Key | Action | |-----|--------| | Tab | Move to next cell (creates new row at end of table) | | Shift+Tab | Move to previous cell | | Enter | Move to cell below (creates new row at end of table) | | Shift+Enter | Insert line break in cell | | Escape | Close cell editor | | Arrow Keys | Navigate between cells (when at cell boundary) |

Toolbar Actions

The floating toolbar appears above a table when a cell is being edited:

  • Row Operations: Insert row above/below, delete row
  • Column Operations: Insert column left/right, delete column
  • Alignment: Align column left, center, or right
  • Formatting: Bold, italic, code, strikethrough, link (applied to selected text in cell)

API Reference

Main Extension

import { tableExtension } from "@markwhen/codemirror-tables";

// Returns an Extension array to add to your EditorState
const extensions = tableExtension(config?);

Commands

Execute table operations programmatically:

import {
  execInsertRowAbove,
  execInsertRowBelow,
  execDeleteRow,
  execInsertColumnLeft,
  execInsertColumnRight,
  execDeleteColumn,
  execSetColumnAlignment,
  execToggleBoldAtCursor,
  execToggleItalicAtCursor,
  execToggleCodeAtCursor,
  execToggleStrikethroughAtCursor,
  execToggleLinkAtCursor,
} from "@markwhen/codemirror-tables";

// Commands take the parent EditorView
execInsertRowBelow(view);
execSetColumnAlignment(view, "center"); // "left" | "center" | "right"

State Management

import {
  activeCellState,
  setActiveCell,
  clearActiveCell,
  getActiveCell,
} from "@markwhen/codemirror-tables";

// Get current active cell
const activeCell = getActiveCell(view.state);
if (activeCell) {
  console.log(`Editing row ${activeCell.row}, col ${activeCell.col}`);
}

// Dispatch to set/clear active cell
view.dispatch({ effects: setActiveCell.of({ tableId, row, col }) });
view.dispatch({ effects: clearActiveCell.of(null) });

Table Parsing & Manipulation

import {
  parseMarkdownTable,
  serializeTable,
  computeMarkdownTableCellRanges,
  insertRow,
  deleteRow,
  insertColumn,
  deleteColumn,
  updateColumnAlignment,
} from "@markwhen/codemirror-tables";

// Parse markdown table text
const tableText = "| A | B |\n|---|---|\n| 1 | 2 |";
const tableData = parseMarkdownTable(tableText);
// { headers: ['A', 'B'], alignments: [null, null], rows: [['1', '2']] }

// Manipulate the table
let table = tableData;
table = insertRow(table, 0, "after");
table = insertColumn(table, 1, "before");
table = updateColumnAlignment(table, 0, "center");

// Serialize back to markdown
const newMarkdown = serializeTable(table);

Types

import type {
  TableConfig,
  ActiveCell,
  TableCellRanges,
  CellRange,
  ColumnAlignment,
  ParsedTable,
} from "@markwhen/codemirror-tables";

interface TableConfig {
  onLinkClick?: (href: string) => void;
}

interface ActiveCell {
  tableId: string;
  row: number;  // -1 for header row
  col: number;
}

type ColumnAlignment = "left" | "center" | "right" | null;

Styling

The package includes default styles. You can customize the appearance by overriding these CSS classes:

/* Table widget container */
.cm-table-widget { }

/* The table element */
.cm-table-widget table { }

/* Header cells */
.cm-table-widget th { }

/* Data cells */
.cm-table-widget td { }

/* Cell being edited */
.cm-table-widget .cell-editing { }

/* Floating toolbar */
.cm-table-toolbar { }

/* Toolbar buttons */
.cm-table-toolbar button { }
.cm-table-toolbar button:hover { }

/* Toolbar dividers */
.cm-table-toolbar .toolbar-divider { }

Integration with Collaboration

The extension uses syncAnnotation to mark internal transactions that synchronize nested editor state with the parent document. When integrating with collaborative editing (e.g., @codemirror/collab), you may want to filter these:

import { syncAnnotation } from "@markwhen/codemirror-tables";

// In your collab integration
for (const tr of update.transactions) {
  if (tr.annotation(syncAnnotation)) {
    // Skip internal sync transactions
    continue;
  }
  // Process for collaboration...
}

Peer Dependencies

This package requires the following CodeMirror packages:

  • @codemirror/state ^6.0.0
  • @codemirror/view ^6.0.0
  • @codemirror/language ^6.0.0
  • @codemirror/commands ^6.0.0
  • @codemirror/lang-markdown ^6.0.0
  • @lezer/markdown ^1.0.0

And uses:

  • @floating-ui/dom - For toolbar positioning
  • style-mod - For CSS-in-JS styling

License

MIT