feat: Add table support to Markdown-ProseMirror converter
- Tables now render properly in Outline Wiki - Parses Markdown table syntax (| Col1 | Col2 |) - Converts to ProseMirror table structure with tr, th, td nodes - First row becomes header cells (th) - Bidirectional: tables also convert back to Markdown - v1.3.16 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
15
CHANGELOG.md
15
CHANGELOG.md
@@ -2,6 +2,21 @@
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## [1.3.16] - 2026-02-01
|
||||
|
||||
### Added
|
||||
|
||||
- **Table Support in Markdown Converter:** Tables now render properly in Outline
|
||||
- Parses Markdown table syntax (`| Col1 | Col2 |`)
|
||||
- Converts to ProseMirror table structure with `table`, `tr`, `th`, `td` nodes
|
||||
- Supports header rows (first row becomes `th` elements)
|
||||
- Handles variable column counts with proper padding
|
||||
- Bidirectional: ProseMirror tables also convert back to Markdown
|
||||
|
||||
### Fixed
|
||||
|
||||
- **Checkbox List:** Already supported but confirmed working with `checkbox_list` and `checkbox_item` node types
|
||||
|
||||
## [1.3.15] - 2026-01-31
|
||||
|
||||
### Fixed
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# MCP Outline PostgreSQL - Continuacao de Testes
|
||||
|
||||
**Ultima Sessao:** 2026-01-31 (actualizado)
|
||||
**Versao Actual:** 1.3.15
|
||||
**Ultima Sessao:** 2026-02-01 (actualizado)
|
||||
**Versao Actual:** 1.3.16
|
||||
**Progresso:** ~95/164 tools testadas (58%) - **CÓDIGO VALIDADO**
|
||||
|
||||
---
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "mcp-outline-postgresql",
|
||||
"version": "1.3.15",
|
||||
"version": "1.3.16",
|
||||
"description": "MCP Server for Outline Wiki via PostgreSQL direct access",
|
||||
"main": "dist/index.js",
|
||||
"scripts": {
|
||||
|
||||
@@ -68,7 +68,7 @@ async function main() {
|
||||
JSON.stringify({
|
||||
status: 'ok',
|
||||
transport: 'streamable-http',
|
||||
version: '1.3.15',
|
||||
version: '1.3.16',
|
||||
sessions: sessions.size,
|
||||
stateful: STATEFUL,
|
||||
tools: allTools.length
|
||||
@@ -101,7 +101,7 @@ async function main() {
|
||||
// Create MCP server
|
||||
const server = createMcpServer(pgClient.getPool(), {
|
||||
name: 'mcp-outline-http',
|
||||
version: '1.3.15'
|
||||
version: '1.3.16'
|
||||
});
|
||||
|
||||
// Track session if stateful
|
||||
|
||||
@@ -39,7 +39,7 @@ async function main() {
|
||||
// Create MCP server with shared configuration
|
||||
const server = createMcpServer(pgClient.getPool(), {
|
||||
name: 'mcp-outline-postgresql',
|
||||
version: '1.3.15'
|
||||
version: '1.3.16'
|
||||
});
|
||||
|
||||
// Connect stdio transport
|
||||
|
||||
@@ -122,7 +122,7 @@ export function createMcpServer(
|
||||
): Server {
|
||||
const server = new Server({
|
||||
name: config.name || 'mcp-outline-postgresql',
|
||||
version: config.version || '1.3.15'
|
||||
version: config.version || '1.3.16'
|
||||
});
|
||||
|
||||
// Set capabilities (required for MCP v2.2+)
|
||||
|
||||
@@ -137,6 +137,62 @@ export function markdownToProseMirror(markdown: string): ProseMirrorDoc {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Table
|
||||
if (line.includes('|') && line.trim().startsWith('|')) {
|
||||
const tableRows: string[][] = [];
|
||||
|
||||
// Collect all table rows
|
||||
while (i < lines.length && lines[i].includes('|')) {
|
||||
const row = lines[i].trim();
|
||||
// Skip separator row (|---|---|)
|
||||
if (/^\|[\s\-:]+\|/.test(row) && row.includes('-')) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
// Parse cells
|
||||
const cells = row
|
||||
.split('|')
|
||||
.slice(1, -1) // Remove empty first and last from split
|
||||
.map(cell => cell.trim());
|
||||
if (cells.length > 0) {
|
||||
tableRows.push(cells);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
if (tableRows.length > 0) {
|
||||
const numCols = Math.max(...tableRows.map(r => r.length));
|
||||
const tableContent: ProseMirrorNode[] = tableRows.map((row, rowIndex) => {
|
||||
// Process cells with content
|
||||
const cells: ProseMirrorNode[] = row.map(cell => ({
|
||||
type: rowIndex === 0 ? 'th' : 'td',
|
||||
attrs: {
|
||||
colspan: 1,
|
||||
rowspan: 1,
|
||||
alignment: null
|
||||
},
|
||||
content: [{ type: 'paragraph', content: parseInlineContent(cell) }]
|
||||
}));
|
||||
// Pad with empty cells if row is shorter
|
||||
const padding = numCols - row.length;
|
||||
for (let p = 0; p < padding; p++) {
|
||||
cells.push({
|
||||
type: rowIndex === 0 ? 'th' : 'td',
|
||||
attrs: { colspan: 1, rowspan: 1, alignment: null },
|
||||
content: [{ type: 'paragraph', content: [] }]
|
||||
});
|
||||
}
|
||||
return { type: 'tr', content: cells };
|
||||
});
|
||||
|
||||
content.push({
|
||||
type: 'table',
|
||||
content: tableContent
|
||||
});
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Default: paragraph
|
||||
content.push({
|
||||
type: 'paragraph',
|
||||
@@ -275,6 +331,21 @@ function nodeToMarkdown(node: ProseMirrorNode, indent = ''): string {
|
||||
case 'hr':
|
||||
return '---';
|
||||
|
||||
case 'table':
|
||||
const rows = (node.content || []).map((tr, rowIndex) => {
|
||||
const cells = (tr.content || []).map(cell =>
|
||||
contentToMarkdown(cell.content?.[0]?.content)
|
||||
);
|
||||
return '| ' + cells.join(' | ') + ' |';
|
||||
});
|
||||
// Add separator after header
|
||||
if (rows.length > 0) {
|
||||
const headerCells = (node.content?.[0]?.content || []).length;
|
||||
const separator = '| ' + Array(headerCells).fill('---').join(' | ') + ' |';
|
||||
rows.splice(1, 0, separator);
|
||||
}
|
||||
return rows.join('\n');
|
||||
|
||||
default:
|
||||
return contentToMarkdown(node.content);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user