Skip to content

Conversation

@S-A-Adit
Copy link
Contributor

@S-A-Adit S-A-Adit commented Jan 6, 2026

Adds a DatabaseBlockMarkdownAdapter to serialize affine:database blocks into markdown tables. Previously, pages containing only table views were treated as empty by the AI. This adapter exposes table headers and rows, enabling AI summarization and project tracking.

### Notes:

  • Currently handles basic cell types; rich text, dates, and relations may need special handling in future PRs.

  • Kanban grouping, filters, and sorts are not yet supported.

Summary by CodeRabbit

  • New Features
    • Database blocks now support export to Markdown format. Data is converted to properly formatted tables with column headers and corresponding data rows.

✏️ Tip: You can customize this high-level summary in your review settings.

@S-A-Adit S-A-Adit requested a review from a team as a code owner January 6, 2026 01:08
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 6, 2026

📝 Walkthrough

Walkthrough

A new Markdown adapter for database blocks is introduced. The adapter converts DatabaseBlockModel instances into MarkdownTableNode by instantiating a datasource, constructing table headers from property names, iterating through rows to collect cell values, and returning a formatted markdown table.

Changes

Cohort / File(s) Summary
Database Block Markdown Adapter
blocksuite/affine/shared/src/adapters/markdown/database-block-adapter.ts
New file. Exports DatabaseBlockMarkdownAdapter constant created via BlockMarkdownAdapterExtension. Implements conversion logic: instantiates DatabaseBlockDataSource, maps property IDs to display names for table headers, iterates rows to populate cells with stringified values, returns MarkdownTableNode.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 A database blooms in markdown's embrace,
Tables take form with headers in place,
Row after row, each value takes flight,
From database model to markdown so bright!
BlockSuite's adapter hops left and right! 🌟

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: implementing a new DatabaseBlockMarkdownAdapter for converting database blocks to markdown format.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Fix all issues with AI Agents 🤖
In @blocksuite/affine/shared/src/adapters/markdown/database-block-adapter.ts:
- Around line 21-25: The code reads dataSource.properties$.value into
propertyIds without guarding against null/undefined, which can throw if the
observable isn't initialized; update the logic around propertyIds (from
dataSource.properties$.value) to first check for nullish (e.g., if
(!propertyIds) return or skip) before calling propertyIds.forEach, and ensure
the header population loop still uses dataSource.propertyNameGet(propId) to push
propertyName || propId into header only when propertyIds is present.
- Around line 28-29: The code reads dataSource.rows$.value without a
null/undefined guard which can throw; update the access to safely handle missing
rows by checking dataSource.rows$ and dataSource.rows$.value (similar to
properties$.value) before iterating—e.g., use a safe fallback like const rowIds
= dataSource.rows$?.value ?? [] or early-return if !dataSource.rows$ ||
!dataSource.rows$.value, then proceed with rowIds.forEach(...).
🧹 Nitpick comments (2)
blocksuite/affine/shared/src/adapters/markdown/database-block-adapter.ts (2)

30-38: Consider improving cell value conversion for complex types.

Using String(cellValue ?? '') will produce "[object Object]" for objects and may not format dates or arrays optimally. While the PR notes acknowledge that rich text, dates, and relations need future work, consider adding a helper function to handle common cases more gracefully.

💡 Example approach
function formatCellValue(value: unknown): string {
  if (value == null) return '';
  if (Array.isArray(value)) return value.join(', ');
  if (value instanceof Date) return value.toISOString();
  if (typeof value === 'object') return JSON.stringify(value);
  return String(value);
}

Then use:

-        rowContent.push(String(cellValue ?? ''));
+        rowContent.push(formatCellValue(cellValue));

40-46: Consider handling empty tables explicitly.

If a database block has no properties or rows, this will return a table with empty header/rows arrays. Verify whether the markdown renderer handles empty tables gracefully, or consider returning a different node type (e.g., a text node indicating "Empty table").

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between a11e9fe and 47cffdb.

📒 Files selected for processing (1)
  • blocksuite/affine/shared/src/adapters/markdown/database-block-adapter.ts
🔇 Additional comments (4)
blocksuite/affine/shared/src/adapters/markdown/database-block-adapter.ts (4)

6-9: Verify whether the database flavour constant is already exported from the model.

The comment suggests this flavour is "typically defined in a corresponding model file." Check if DatabaseBlockModel (from @blocksuite/affine-model) already exports a FLAVOUR or similar constant. If it does, import and reuse it instead of redefining it locally to avoid duplication and maintenance issues.


1-1: No action needed.

The import path ../../../blocks/database/src/data-source is correct. DatabaseBlockDataSource is properly exported from blocksuite/affine/blocks/database/src/data-source.ts and the relative path resolves correctly from the adapter file location.


14-15: No action required. DatabaseBlockDataSource does not implement any disposal methods and the usage pattern only performs synchronous reads (accessing .value on signals without creating subscriptions). The instance is a local variable that's used once and discarded naturally when the convert function returns.

Likely an incorrect or invalid review comment.


13-47: Error handling is not needed—the code is already defensive.

The adapter uses proper fallbacks (propertyName || propId and String(cellValue ?? '')) to handle missing or invalid data. The underlying DatabaseBlockDataSource methods (cellValueGet, propertyNameGet) are designed defensively, returning undefined or empty strings rather than throwing errors. This approach aligns with the codebase patterns shown in test usage, where DatabaseBlockDataSource is instantiated without try-catch blocks.

Likely an incorrect or invalid review comment.

Comment on lines +21 to +25
const propertyIds = dataSource.properties$.value;
propertyIds.forEach(propId => {
const propertyName = dataSource.propertyNameGet(propId);
header.push(propertyName || propId); // Use property name, fall back to ID if name is empty.
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add null/undefined check for properties$.value.

Accessing dataSource.properties$.value without checking for null/undefined can cause a runtime error if the observable hasn't been initialized or returns a nullish value.

🔎 Proposed fix
-    const propertyIds = dataSource.properties$.value;
+    const propertyIds = dataSource.properties$.value ?? [];
     propertyIds.forEach(propId => {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const propertyIds = dataSource.properties$.value;
propertyIds.forEach(propId => {
const propertyName = dataSource.propertyNameGet(propId);
header.push(propertyName || propId); // Use property name, fall back to ID if name is empty.
});
const propertyIds = dataSource.properties$.value ?? [];
propertyIds.forEach(propId => {
const propertyName = dataSource.propertyNameGet(propId);
header.push(propertyName || propId); // Use property name, fall back to ID if name is empty.
});
🤖 Prompt for AI Agents
In @blocksuite/affine/shared/src/adapters/markdown/database-block-adapter.ts
around lines 21-25, The code reads dataSource.properties$.value into propertyIds
without guarding against null/undefined, which can throw if the observable isn't
initialized; update the logic around propertyIds (from
dataSource.properties$.value) to first check for nullish (e.g., if
(!propertyIds) return or skip) before calling propertyIds.forEach, and ensure
the header population loop still uses dataSource.propertyNameGet(propId) to push
propertyName || propId into header only when propertyIds is present.

Comment on lines +28 to +29
const rowIds = dataSource.rows$.value;
rowIds.forEach(rowId => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add null/undefined check for rows$.value.

Similar to properties$.value, accessing dataSource.rows$.value without a null check can cause runtime errors.

🔎 Proposed fix
-    const rowIds = dataSource.rows$.value;
+    const rowIds = dataSource.rows$.value ?? [];
     rowIds.forEach(rowId => {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const rowIds = dataSource.rows$.value;
rowIds.forEach(rowId => {
const rowIds = dataSource.rows$.value ?? [];
rowIds.forEach(rowId => {
🤖 Prompt for AI Agents
In @blocksuite/affine/shared/src/adapters/markdown/database-block-adapter.ts
around lines 28-29, The code reads dataSource.rows$.value without a
null/undefined guard which can throw; update the access to safely handle missing
rows by checking dataSource.rows$ and dataSource.rows$.value (similar to
properties$.value) before iterating—e.g., use a safe fallback like const rowIds
= dataSource.rows$?.value ?? [] or early-return if !dataSource.rows$ ||
!dataSource.rows$.value, then proceed with rowIds.forEach(...).

@codecov
Copy link

codecov bot commented Jan 16, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 56.50%. Comparing base (cf98afb) to head (3223cc7).
⚠️ Report is 25 commits behind head on canary.

Additional details and impacted files
@@            Coverage Diff             @@
##           canary   #14218      +/-   ##
==========================================
- Coverage   56.51%   56.50%   -0.01%     
==========================================
  Files        2775     2775              
  Lines      142437   142437              
  Branches    21697    21695       -2     
==========================================
- Hits        80493    80479      -14     
- Misses      60160    60174      +14     
  Partials     1784     1784              
Flag Coverage Δ
server-test 76.13% <ø> (-0.03%) ⬇️
unittest 32.04% <ø> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

1 participant