ENG-889: Render SmartBlocks as interactive buttons in left sidebar#940
Conversation
Block references containing SmartBlock syntax (`:SmartBlock:` buttons or `<%` workflows) now render via Roam's renderBlock API instead of plain text. Clicking a SmartBlock button navigates to the block. - Add RoamRenderedBlock component using renderBlock with open?:false - Extract hasSmartBlockSyntax to module level in createDiscourseNode, add isSmartBlockUid export for sidebar/settings detection - Hide alias settings gear for SmartBlock children - Inject CSS once in mountLeftSidebar to hide Roam chrome - Add "DG: Favorites - Add to Global section" context menu with dual-write (restores global support removed in PR #902 self-review)
|
This pull request has been ignored for the connected project Preview Branches by Supabase. |
|
@coderabbitai full review |
✅ Actions performedFull review triggered. |
📝 WalkthroughWalkthroughRenders smart-block references in the left sidebar by embedding a Roam-rendered block component, adds smart-block detection helpers, adjusts personal settings rendering for smart-block children, injects sidebar-specific CSS, and adds a command to insert blocks into the global sidebar section. ChangesSmart-block rendering & detection
Global favorites command
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Tip 💬 Introducing Slack Agent: The best way for teams to turn conversations into code.Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.
Built for teams:
One agent for your entire SDLC. Right inside Slack. 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. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
apps/roam/src/utils/registerCommandPaletteCommands.ts (1)
430-463: Consider constructing updated children in-memory instead of re-reading from config.The
addBlockToGlobalSectionfunction re-reads the config tree after creating the block, which differs fromaddBlockToPersonalSection(lines 397-412) that manually constructs the updated state. This inconsistency could lead to subtle timing issues ifrefreshConfigTreereads stale data.♻️ Suggested refactor to match personal section pattern
const addBlockToGlobalSection = async ({ blockUid, globalChildrenUid, }: { blockUid: string; globalChildrenUid: string; }) => { const blockRef = `((${blockUid}))`; try { await createBlock({ parentUid: globalChildrenUid, order: "last", node: { text: blockRef }, }); refreshConfigTree(); - const updatedChildren = getLeftSidebarGlobalSectionConfig( - discourseConfigRef.tree.find((n) => n.text === "Left Sidebar") - ?.children || [], - ).children; + const leftSidebarNode = discourseConfigRef.tree.find( + (n) => n.text === "Left Sidebar", + ); + const currentChildren = getLeftSidebarGlobalSectionConfig( + leftSidebarNode?.children || [], + ).children; + const updatedChildrenTexts = [ + ...currentChildren.map((c) => c.text), + blockRef, + ]; setGlobalSetting( ["Left sidebar", "Children"], - updatedChildren.map((c) => c.text), + updatedChildrenTexts, ); refreshAndNotify(); } catch {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/roam/src/utils/registerCommandPaletteCommands.ts` around lines 430 - 463, addBlockToGlobalSection currently calls refreshConfigTree and then re-reads discourseConfigRef to build updatedChildren; instead, mirror addBlockToPersonalSection by building the updated children in-memory immediately after createBlock to avoid timing/stale-read issues. After createBlock succeeds, locate the "Left Sidebar" node via discourseConfigRef.tree.find(...) (same selector used now), get its global section children via getLeftSidebarGlobalSectionConfig(...).children, append a new child node whose text is the blockRef string, then call setGlobalSetting(["Left sidebar", "Children"], updatedChildren.map(c => c.text)) and refreshAndNotify(); remove the refreshConfigTree() + re-read sequence so the update is based on the in-memory constructed updatedChildren rather than a subsequent config fetch.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@apps/roam/src/utils/registerCommandPaletteCommands.ts`:
- Around line 430-463: addBlockToGlobalSection currently calls refreshConfigTree
and then re-reads discourseConfigRef to build updatedChildren; instead, mirror
addBlockToPersonalSection by building the updated children in-memory immediately
after createBlock to avoid timing/stale-read issues. After createBlock succeeds,
locate the "Left Sidebar" node via discourseConfigRef.tree.find(...) (same
selector used now), get its global section children via
getLeftSidebarGlobalSectionConfig(...).children, append a new child node whose
text is the blockRef string, then call setGlobalSetting(["Left sidebar",
"Children"], updatedChildren.map(c => c.text)) and refreshAndNotify(); remove
the refreshConfigTree() + re-read sequence so the update is based on the
in-memory constructed updatedChildren rather than a subsequent config fetch.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 18d00343-4dcd-4af8-bd3f-9dcac2e69e8b
📒 Files selected for processing (4)
apps/roam/src/components/LeftSidebarView.tsxapps/roam/src/components/settings/LeftSidebarPersonalSettings.tsxapps/roam/src/utils/createDiscourseNode.tsapps/roam/src/utils/registerCommandPaletteCommands.ts
mdroidian
left a comment
There was a problem hiding this comment.
Could you please include a loom video?
- Replace useEffect+renderBlock with roamAlphaAPI.ui.components.react.block - Simplify isSmartBlockUid to only check :SmartBlock: text - Move isSmartBlockUid to getLeftSidebarSettings (shared by both consumers) - Restore hasSmartBlockSyntax as local fn in createDiscourseNode
mdroidian
left a comment
There was a problem hiding this comment.
It's a good idea to run through some quick tests when you make changes.
- Use RenderRoamBlock from ~/utils/roamReactComponents (correct API path: ui.react.Block, not ui.components.react.block). Fixes runtime break and removes the @ts-expect-error + eslint-disables. - Move isSmartBlockUid into its own file at ~/utils/isSmartBlockUid.ts.
mdroidian
left a comment
There was a problem hiding this comment.
Couple of issues
A caching issue:
https://www.loom.com/share/1617eacbe53c433793afdd417b99345d
And the openBlock issue noted below
| (e: React.MouseEvent) => { | ||
| const target = e.target as HTMLElement; | ||
| if (target.closest("[data-roamjs-smartblock-button]")) { | ||
| void window.roamAlphaAPI.ui.mainWindow.openBlock({ |
There was a problem hiding this comment.
- Remove openBlock click handler. SmartBlock buttons handle their own clicks; the wrapper was running the SmartBlock AND navigating to its source block. - Pull-watch :block/string and force RenderRoamBlock remount via key. Roam's react.Block doesn't re-render on text changes when used outside the main render tree, so editing a SmartBlock action left the sidebar showing the previous action.
…d-in-left-sidebar
|
@coderabbitai full review |
✅ Actions performedFull review triggered. |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/roam/src/utils/isSmartBlockUid.ts`:
- Around line 3-7: The isSmartBlockUid function only checks for the literal
substring ":SmartBlock:", which misses SmartBlock workflow syntax like "<%";
update isSmartBlockUid (which calls getTextByBlockUid) to treat a block as a
SmartBlock if its text includes either ":SmartBlock:" OR the workflow token
"<%"; keep the existing guard for missing text (if !text return false) and then
return text.includes(":SmartBlock:") || text.includes("<%") so both forms are
detected.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: cf1bbe11-524e-415a-b1f9-2fa04142027e
📒 Files selected for processing (5)
apps/roam/src/components/LeftSidebarView.tsxapps/roam/src/components/settings/LeftSidebarPersonalSettings.tsxapps/roam/src/utils/createDiscourseNode.tsapps/roam/src/utils/isSmartBlockUid.tsapps/roam/src/utils/registerCommandPaletteCommands.ts
https://www.loom.com/share/81ab669efc904bd3b45706020daa5b07
Block references containing SmartBlock syntax (
:SmartBlock:buttons or<%workflows) now render via Roam's renderBlock API instead of plain text. Clicking a SmartBlock button navigates to the block.Summary by CodeRabbit
New Features
Improvements