Skip to content

feat(feishu): support markdown tables via CardKit table element #27695

@sixerLiu

Description

@sixerLiu

Problem

Feishu post type messages do not render markdown tables — they display as raw text with | characters, making table-formatted data unreadable.

Solution

Detect markdown tables via regex and route them to the Feishu CardKit table element (JSON 2.0 schema) instead of post.

API Reference

CardKit Table JSON 2.0 structure

{
  "schema": "2.0",
  "header": { "template": "blue", "title": { "content": "Title", "tag": "plain_text" } },
  "body": {
    "elements": [{
      "tag": "table",
      "page_size": 5,
      "columns": [
        { "name": "col_0", "display_name": "Column A", "data_type": "text", "horizontal_align": "left" },
        { "name": "col_1", "display_name": "Column B", "data_type": "text", "horizontal_align": "left" }
      ],
      "rows": [
        { "col_0": "value 1", "col_1": "value 2" },
        { "col_0": "value 3", "col_1": "value 4" }
      ]
    }]
  }
}

Supported data_type values: text, lark_md, markdown, number, options, persons, date.

Implementation

  • parse_markdown_table() — parse markdown table string → (columns, rows) for CardKit
  • _build_ordered_blocks() — split message into text and table blocks, preserving order
  • send_card_table() — async method to send a CardKit card with a table element
  • send_text() override — detects tables in formatted content; builds a combined CardKit card (markdown sections + table element in order); falls back to plain text if parsing fails

Examples

1. Pure table (all markdown rows belong to the same table)

Input:

| Name | Score |
|------|-------|
| Alice | 98 |
| Bob   | 85 |

Output: CardKit card with blue header and a rendered table.

2. Text + table mixed content

Input:

## Performance Report

Summary: Strong quarter across all metrics.

| Region  | Revenue | Growth |
|---------|---------|--------|
| North   | 1200    | +15%   |
| South   | 890     | +8%    |

Please review and let me know if you have questions.

Output: A single CardKit card with:

  • A markdown element: ## Performance Report
  • A markdown element: Summary: Strong quarter across all metrics.
  • A table element with columns [Region, Revenue, Growth] and 2 data rows
  • A markdown element: Please review and let me know if you have questions.

3. Multiple tables in one message

Input:

## Q1 Metrics

### Sales
| Product | Units |
|---------|-------|
| A       | 100   |
| B       | 200   |

### Support
| Agent | Tickets |
|-------|---------|
| X     | 50      |
| Y     | 30      |

Output: One CardKit card with 4 elements: 2 markdown sections + 2 table elements.

Backwards Compatibility

  • Non-table messages pass through unchanged
  • If table regex matches but parsing fails (malformed table), falls back to plain text send
  • send_card_table() can be called directly for programmatic use

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3Low — cosmetic, nice to havecomp/gatewayGateway runner, session dispatch, deliveryduplicateThis issue or pull request already existsplatform/feishuFeishu / Lark adaptertype/featureNew feature or request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions