Skip to content

Init#1

Closed
mdroidian wants to merge 21 commits intomainfrom
init
Closed

Init#1
mdroidian wants to merge 21 commits intomainfrom
init

Conversation

@mdroidian
Copy link
Copy Markdown
Contributor

@mdroidian mdroidian commented Feb 7, 2026

  • Added CSS rules to ensure tldraw cursors are not affected by external styles, specifically for elements with role="button".
  • Improved user experience by maintaining consistent cursor behavior within the tldraw canvas.

Open with Devin

Summary by CodeRabbit

  • New Features

    • TLDraw canvas editor integrated into Roam with inspector, drag‑and‑drop support, page/block search, and maximize/restore.
    • Canvas settings: configurable page patterns and maximize hotkey; persistent canvas state syncs with Roam.
  • Chores

    • Migrated package manager to pnpm and updated build/dev workflows.
    • Added developer tooling and configs: Prettier (Tailwind plugin), Tailwind CSS, TypeScript, and VSCode workspace settings.

…nagement, and improve Tldraw canvas interaction with keyboard shortcuts and maximization styles.
…efaultNodeUtil and Tldraw components to utilize TLShape and TLShapeId for better type safety. Update Roam store handling to ensure correct type parsing for tldraw state.
… and enhance inspector functionality. Add Tailwind CSS version update and adjust styles for node inspector. Implement snapshot sanitization for tldraw state to ensure consistent node properties.
… by adding edit time to search results and improving loading state management. Refactor result handling to limit visible results and ensure consistent user experience during searches.
…debounce search functionality. Introduce a constant for debounce time and enhance state handling to prevent unnecessary updates when the selected target remains unchanged.
…s to return promises for search results. Improve loading state management and prevent unnecessary updates during search requests by implementing request cancellation and debounce logic.
…ng user experience with dynamic panel resizing. Update styles for improved visual feedback on inspector interactions.
…pening blocks in the main window and sidebar. Improve user interaction with dynamic labels and refined styles for better visual feedback in the inspector panel.
- Introduced a Prettier configuration file to enforce consistent code styling.
- Added Prettier and Prettier Tailwind CSS plugin as development dependencies.
- Updated VSCode settings to integrate Prettier for automatic formatting on save.
…ling and introduce keyboard shortcuts dialog. Refactor TYPE_STYLES to include border properties for improved visual design, and implement a new MainMenu and KeyboardShortcutsDialog for better user interaction.
- Updated Prettier to version 3.8.1 and added prettier-plugin-tailwindcss version 0.6.14 as development dependencies.
- Enhanced lockfile with new resolutions and peer dependencies for improved code formatting and compatibility.
…ering of node titles

- Introduced RoamRenderedString to handle rendering of node titles with fallback for errors.
- Updated getNodeTypeFromRoamRefText to use BLOCK_REF_REGEX for block matching.
- Refactored title display in BaseRoamNodeShapeUtil to utilize the new component for better readability.
- Added methods to retrieve block UIDs from DOM elements and handle dropped text.
- Implemented drag event listeners to support transferring UIDs during drag-and-drop actions.
- Updated external content handling to process dropped text and UIDs effectively, improving user interaction with the canvas.
… styles

- Added CSS rules to ensure tldraw cursors are not affected by external styles, specifically for elements with role="button".
- Improved user experience by maintaining consistent cursor behavior within the tldraw canvas.
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Feb 7, 2026

Walkthrough

This PR adds a Tldraw-based canvas integration for Roam (components, styles, store hook, utilities, and entry wiring) and introduces a new esbuild-based build system with scripts (compile, build, dev). It migrates toolchain and CI from npm to pnpm (package.json, GitHub workflows, build.sh), adds editor/format tooling (Prettier, Tailwind, VSCode settings, tsconfig), and includes type/patch updates and pnpm overrides for dependency control.

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'Init' is vague and generic, using a non-descriptive term that does not convey meaningful information about the substantial changes in this changeset. Use a more descriptive title that captures the primary focus, such as 'Add tldraw canvas integration for Roam' or 'Migrate to pnpm and add canvas support', which would better reflect the major additions.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch init

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
Copy Markdown

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 2 potential issues.

View 8 additional findings in Devin Review.

Open in Devin Review

Comment on lines +361 to +391
useEffect(() => {
const container = containerRef.current;
if (!container) return;

const onDragOverNative = (e: DragEvent): void => {
const target = e.target as Node | null;
if (!target || !container.contains(target)) return;
e.preventDefault();
};

const onDropNative = (e: DragEvent): void => {
const target = e.target as Node | null;
if (!target || !container.contains(target)) return;
e.preventDefault();
e.stopPropagation();
(e as Event).stopImmediatePropagation?.();
if (!e.dataTransfer) return;
handleDropPayload({
dataTransfer: e.dataTransfer,
clientX: e.clientX,
clientY: e.clientY,
});
};

container.addEventListener("dragover", onDragOverNative, true);
container.addEventListener("drop", onDropNative, true);
return () => {
container.removeEventListener("dragover", onDragOverNative, true);
container.removeEventListener("drop", onDropNative, true);
};
}, []);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔴 useEffect called after conditional early return violates React rules of hooks

The useEffect at line 361 is placed after the conditional if (!pageUid) return null; at line 264. This violates React's rules of hooks, which require that hooks are always called in the same order on every render.

Root Cause and Impact

When pageUid is falsy, the component returns null at line 264, and the useEffect at line 361 is never reached. When pageUid is truthy, the useEffect IS reached. This means React sees a different number of hook calls between renders, which will cause a React runtime error:

"Rendered more hooks than during the previous render"

The hooks before line 264 (lines 165-262) total 8 hooks. The useEffect at line 361 would be the 9th hook, but it's only called when pageUid is truthy. If the component first renders with no pageUid (e.g., page not yet created) and then re-renders with a valid pageUid, React will crash.

Impact: The drag-and-drop event listeners for the canvas container will never be registered if the component initially renders without a pageUid, and React may crash if pageUid changes from falsy to truthy.

Prompt for agents
Move the useEffect at line 361-391 (the drag-and-drop event listener setup) to BEFORE the conditional return at line 264. It should be placed alongside the other useEffect hooks (e.g., after the useEffect at line 250-262). The handleDropPayload function it references (defined at line 318) should also be moved before the early return, or the useEffect should be refactored to not depend on it (e.g., by using a ref to hold the handler). The simplest fix is to move both handleDropPayload and this useEffect above line 264.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown

@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: 9

🤖 Fix all issues with AI agents
In `@build.sh`:
- Line 1: Add a POSIX-compatible shebang to the top of the script so the shell
is explicit (e.g., add "#!/usr/bin/env bash" as the first line) and ensure the
script is executable (chmod +x). Keep the existing command "pnpm run build:roam"
unchanged; the shebang will ensure it runs under the intended shell and satisfy
Shellcheck SC2148.

In `@package.json`:
- Line 22: The package.json currently pins the dependency "tldraw" to the
unpublished version "3.15.5"; update the dependency entry for "tldraw" to a
published version such as "3.15.1" (edit the "tldraw" value in package.json),
then reinstall/update lockfile (npm install or yarn install) so the lockfile
reflects the valid version.

In `@scripts/build.ts`:
- Around line 3-7: The build function reassigns process.env to a new object
which can break existing references; instead of replacing process.env in build,
set the specific variable directly (e.g., assign process.env.NODE_ENV =
process.env.NODE_ENV || "production") so existing references remain valid;
update the build function in scripts/build.ts (the build async function) to
mutate process.env rather than overwrite it, mirroring the direct-assignment
pattern used in scripts/dev.ts.

In `@scripts/dev.ts`:
- Around line 7-17: The dev function calls compile(...) but doesn't await or
handle its promise, so initial build errors become unhandled rejections; update
dev to propagate compile failures by either making dev async and awaiting
compile(...) or by adding a reject handler in the returned Promise (e.g., new
Promise<number>((resolve, reject) => { const p = compile(...);
p.then(...).catch(reject); process.on("exit", resolve); })). Ensure you
reference the existing compile(...) invocation and keep the existing builder
logic (esbuild.context(...).then(esb => esb.watch())) while wiring its returned
promise into the dev promise so main()'s try/catch can observe errors.

In `@src/components/canvas/DefaultNodeUtil.tsx`:
- Around line 65-100: The Datalog query interpolation in searchPages (and
similarly in searchBlocks) is vulnerable because escapeRegex does not escape
double quotes, allowing injection when pattern is placed inside a double-quoted
re-pattern; modify the sanitization so double quotes are escaped before
interpolation (either add " to the escapeRegex character class or explicitly
call .replace(/"/g, '\\"') on pattern) and ensure the sanitized pattern is used
in the re-pattern interpolation; mirror the same change in searchBlocks and keep
getNodeTypeFromRoamRefText's quote-escaping approach as the reference.

In `@src/components/canvas/Tldraw.tsx`:
- Around line 494-519: The onMount block registers listeners without cleanup
causing leaks; move the registrations for editor.on(...) and
editor.registerExternalContentHandler("text", ...) into separate useEffect hooks
that run when the editor instance is ready, and in each effect return a cleanup
that calls editor.off(...) for the exact handler registered and
editor.registerExternalContentHandler("text", null) to unregister the external
handler; keep the handler logic (including calls to getNodeTypeFromRoamRefText,
defaultHandleExternalTextContent, createShapeId, and creating a RoamNodeShape)
identical but reference the same handler functions so they can be removed by
editor.off during cleanup.

In `@src/components/canvas/tldrawStyles.ts`:
- Around line 71-74: The global `.roam-main { position: relative; }` rule
affects all pages; narrow its scope so it only applies when the tldraw canvas is
active — update the selector used in tldrawStyles.ts to target `.roam-main` only
when it contains the canvas (e.g.
`.roam-main:has(.roamjs-tldraw-canvas-container) { position: relative; }`) or,
if :has isn’t acceptable, apply a contextual class when maximizing the canvas
(e.g. add/remove `.roamjs-tldraw-maximized` and use `.roamjs-tldraw-maximized
.roam-main { position: relative; }`) and remove the original broad `.roam-main`
rule.

In `@src/components/canvas/uiOverrides.tsx`:
- Around line 81-94: The map in Toolbar violates the Rules of Hooks by calling
useIsToolSelected inside a .map callback; fix by extracting a new child
component (e.g., ToolMenuItem) that accepts the tool descriptor and the tool
data from useTools(), call useIsToolSelected(toolData) inside that component,
and render TldrawUiMenuItem there; then change the map to return <ToolMenuItem
key={tool.id} tool={tool} toolData={tools[tool.id]} /> so hooks are only invoked
at the top level of a React component (referencing Toolbar, DEFAULT_NODE_TOOLS,
useTools, useIsToolSelected, and TldrawUiMenuItem).

In `@src/components/canvas/useRoamStore.ts`:
- Around line 65-82: The setPersistedSnapshot function currently discards errors
by using void on window.roamAlphaAPI.data.page.update; modify
setPersistedSnapshot (and its call sites if needed) to handle promise rejections
— either await the update inside an async function or append a .catch(...) to
the returned promise — and log/report failures (use existing logger or
console.error) including pageUid and the error so write failures to
ROAM_TLDRAW_KEY are visible; ensure you still merge props via
getPageProps(pageUid) and do not change the stored payload shape.
🧹 Nitpick comments (20)
.github/workflows/main.yaml (1)

23-23: Consider upgrading actions/checkout to v4.

Line 23 uses actions/checkout@v3, which is outdated. The rest of the workflow already uses v4 actions (setup-node@v4, action-setup@v4). While this line wasn't changed in this PR, it's worth aligning for consistency and to pick up Node 20 runtime support.

.vscode/settings.json (1)

4-4: prettier.prettierPath is unnecessary when the extension auto-resolves Prettier locally.

The Prettier VSCode extension automatically resolves Prettier from your project's node_modules without this setting. Removing it simplifies your configuration.

src/components/canvas/useRoamStore.ts (2)

95-111: customShapeUtils array in useMemo deps may cause unnecessary store re-creation.

If the caller passes a new array reference on every render (e.g., an inline [MyUtil]), the store will be re-created each time, losing all local state. Ensure the caller memoizes the customShapeUtils array, or consider removing it from the dependency array in favor of a ref-based approach.


13-38: sanitizeNodePropsInSnapshot hardcodes expected prop shapes — keep in sync with shape utils.

The sanitizer whitelists w, h, uid, title with hardcoded defaults. If the shape definition (e.g., DefaultNodeUtil) adds new props later, this sanitizer will silently strip them. Consider co-locating the default props with the shape util or importing them from a shared constant.

src/utils/isCanvasPage.ts (2)

15-18: Wildcard * requires at least one character — intentional?

\* is replaced with .+ (one-or-more), so Canvas/* won't match the bare title "Canvas/". If a zero-length suffix should also match, use .* instead. If the current behavior is intentional, this is fine as-is.

Regarding the static analysis ReDoS warning: the risk here is negligible since user-supplied * wildcards are converted to simple .+ with no nested quantifiers, and all other special characters are escaped beforehand.


33-42: Regex is recompiled on every call to isCanvasPage.

patternToRegex is called for each pattern on every isCanvasPage invocation (which fires on every H1 mutation). Since patterns rarely change, consider caching the compiled regexes (e.g., memoize on the raw settings string).

scripts/build.ts (1)

19-26: Redundant outer try/catchbuild() already calls process.exit(1) on failure.

Since process.exit(1) in build() terminates the process, the catch in main() is unreachable for compile errors. Consider collapsing into a single function or removing one layer.

src/index.ts (1)

67-71: TODO: Migration from roamjs-query-builder.tldraw to roamjs-tldraw.

There's an open TODO for migrating persisted state from the old key to the new one. Users upgrading from the query-builder extension may lose their existing canvas data without this migration.

Would you like me to draft a migration utility that reads from the old roamjs-query-builder.tldraw key and writes to roamjs-tldraw, or open an issue to track this?

src/components/canvas/tldrawStyles.ts (1)

77-90: Hardcoded color values for inspector hover/active states.

#dadddf is used in multiple places. Consider extracting it to a CSS variable for easier theming and consistency, especially if Roam supports dark mode.

src/components/canvas/Tldraw.tsx (6)

157-162: toggleMaximized is omitted from useMemo deps — safe here but fragile.

toggleMaximized (line 138) is recreated each render, yet it's excluded from the useMemo deps at line 161. This works today because toggleMaximized only accesses refs (containerRef, appRef), which are stable across renders. However, if it ever closes over reactive state, this will silently become a stale closure bug.

Consider wrapping toggleMaximized in useCallback (with an empty dep array since it only uses refs) and including it in the useMemo deps for correctness-by-construction.


458-462: Every editor event is cast to TLPointerEventInfo and triggers refreshInspectorTarget.

editor.on("event", ...) fires for all event types (keyboard, wheel, pointer, etc.). Casting every event to TLPointerEventInfo and calling refreshInspectorTarget() unconditionally on each event is wasteful. The modifier-key check at line 461 is safe (properties are just undefined for non-pointer events) but the refreshInspectorTarget() call queries the editor's selection state on every single event.

Consider filtering to pointer events before calling refreshInspectorTarget:

💡 Suggested optimization
           editor.on("event", (event) => {
-            const e = event as TLPointerEventInfo;
-            refreshInspectorTarget();
-            const validModifier = e.shiftKey || e.ctrlKey;
-            if (!(e.name === "pointer_up" && validModifier)) return;
+            if (event.name === "pointer_up") {
+              refreshInspectorTarget();
+              const e = event as TLPointerEventInfo;
+              const validModifier = e.shiftKey || e.ctrlKey;
+              if (!validModifier) return;

Wait — refreshInspectorTarget likely needs to run on selection changes too (e.g., keyboard-driven selection). If so, consider listening to editor.on("change") or the store's selection events instead, which would be more targeted than reacting to every raw input event.


361-391: Drop event listener registered with empty deps — relies on stable refs internally.

handleDropPayload (defined at line 318) is captured once by this effect because of the [] dependency array. This is safe only because handleDropPayload accesses the editor through appRef.current (a ref). If handleDropPayload ever starts using reactive state directly (not via refs), this will silently become stale. A brief comment noting this invariant would help future maintainers.


250-262: Global dragstart listener intercepts all drag events in the document.

This registers a document-level dragstart listener that calls getBlockUidFromBullet on any drag target anywhere in the page. If other parts of the Roam UI or extensions also rely on dataTransfer data, this could interfere. The current guard (if (uid)) mitigates this, but it's worth noting the broad scope.


623-624: MenuItem key may not be unique across search results.

The key {`${result.uid}-${result.title}`} assumes uid is unique. If it is (which it should be in Roam), then result.uid alone suffices. Including title is redundant noise in the key — and in theory, if two different UIDs share the same title, this still works, but if a UID somehow appeared twice in results, the composite key wouldn't help since the title would also match.

Minor — no functional issue, just a simplification opportunity.


59-66: getPageUidByPageTitle(title) is called on every render.

Line 66 calls getPageUidByPageTitle(title) directly in the component body, meaning it runs on every render. If this is a synchronous Roam API query, it could be a performance concern. Consider memoizing it:

💡 Suggested fix
-  const pageUid = getPageUidByPageTitle(title);
+  const pageUid = useMemo(() => getPageUidByPageTitle(title), [title]);
src/components/canvas/uiOverrides.tsx (1)

109-124: ViewMenu component defined inside MainMenu is recreated every render.

Defining ViewMenu as an inline component inside MainMenu means React creates a new component type on each render, causing full unmount/remount of the subtree. For a menu this is likely harmless in practice, but it's better practice to hoist it outside:

♻️ Suggested refactor
+const ViewMenu = () => {
+  const actions = useActions();
+  return (
+    <TldrawUiMenuSubmenu id="view" label="menu.view">
+      <TldrawUiMenuGroup id="view-actions">
+        <TldrawUiMenuItem {...actions["zoom-in"]} />
+        <TldrawUiMenuItem {...actions["zoom-out"]} />
+        <ZoomTo100MenuItem />
+        <ZoomToFitMenuItem />
+        <ZoomToSelectionMenuItem />
+        <TldrawUiMenuItem {...actions["toggle-full-screen"]} />
+      </TldrawUiMenuGroup>
+    </TldrawUiMenuSubmenu>
+  );
+};
+
 export const createUiComponents = (): TLUiComponents => ({
   // ...
   MainMenu: () => {
-    const ViewMenu = () => {
-      const actions = useActions();
-      return (
-        <TldrawUiMenuSubmenu id="view" label="menu.view">
-          ...
-        </TldrawUiMenuSubmenu>
-      );
-    };
     return (
       <DefaultMainMenu>
         <EditSubmenu />
         <ViewMenu />
scripts/compile.ts (3)

21-21: Unused variable envContents.

let envContents = null; is declared but never read or written anywhere in the file.

🧹 Remove it
-let envContents = null;

85-90: external field in the Zod schema requires a value but has no default.

z.array(z.string()) without .default([]) means cliArgs.parse(opts) will throw if a caller provides a custom opts without external. Since compile has a default for opts (line 124), this only manifests when opts is partially overridden. Adding .default([]) makes the API more resilient.

💡 Suggested fix
 const cliArgs = z.object({
   out: z.string().optional(),
   root: z.string().optional(),
   format: z.enum(["esm"]).optional(),
-  external: z.array(z.string()),
+  external: z.array(z.string()).default([]),
 });

24-72: importAsGlobals plugin: the file-existence check doubles as a path resolver.

Line 56 checks fs.existsSync(global) where global is the mapping value (e.g., ./node_modules/react/jsx-runtime.js). This is clever but means the mapping value serves dual purpose — it's either a global variable expression or a file path. This could be confusing for future maintainers. A brief inline comment clarifying this behavior would help.

src/components/canvas/DefaultNodeUtil.tsx (1)

68-101: searchPages and searchBlocks fetch all matching results and sort client-side.

When the query is empty (lines 85–91, 120–126), these functions fetch all pages/blocks from Roam and sort them by edit time. For large graphs, this could be very expensive. Consider adding a limit clause in the Datalog query or documenting the expected dataset size.

Also applies to: 103-136

@@ -1 +1 @@
npm run build:roam
pnpm run build:roam
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add a shebang line.

The script lacks a shebang, so the shell used to execute it is undefined and platform-dependent. This was also flagged by Shellcheck (SC2148).

Proposed fix
+#!/usr/bin/env bash
 pnpm run build:roam
📝 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
pnpm run build:roam
#!/usr/bin/env bash
pnpm run build:roam
🧰 Tools
🪛 Shellcheck (0.11.0)

[error] 1-1: Tips depend on target shell and yours is unknown. Add a shebang or a 'shell' directive.

(SC2148)

🤖 Prompt for AI Agents
In `@build.sh` at line 1, Add a POSIX-compatible shebang to the top of the script
so the shell is explicit (e.g., add "#!/usr/bin/env bash" as the first line) and
ensure the script is executable (chmod +x). Keep the existing command "pnpm run
build:roam" unchanged; the shebang will ensure it runs under the intended shell
and satisfy Shellcheck SC2148.

Comment on lines +3 to +7
const build = async () => {
process.env = {
...process.env,
NODE_ENV: process.env.NODE_ENV || "production",
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Replacing process.env object can break references held elsewhere.

Assigning a new object to process.env (via spread) means any module that captured a reference to the original process.env will no longer see updates. The dev script at scripts/dev.ts:8 uses the safer direct-assignment pattern.

Proposed fix
 const build = async () => {
-  process.env = {
-    ...process.env,
-    NODE_ENV: process.env.NODE_ENV || "production",
-  };
+  process.env.NODE_ENV = process.env.NODE_ENV || "production";
📝 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 build = async () => {
process.env = {
...process.env,
NODE_ENV: process.env.NODE_ENV || "production",
};
const build = async () => {
process.env.NODE_ENV = process.env.NODE_ENV || "production";
🤖 Prompt for AI Agents
In `@scripts/build.ts` around lines 3 - 7, The build function reassigns
process.env to a new object which can break existing references; instead of
replacing process.env in build, set the specific variable directly (e.g., assign
process.env.NODE_ENV = process.env.NODE_ENV || "production") so existing
references remain valid; update the build function in scripts/build.ts (the
build async function) to mutate process.env rather than overwrite it, mirroring
the direct-assignment pattern used in scripts/dev.ts.

Comment on lines +7 to +17
const dev = () => {
process.env.NODE_ENV = process.env.NODE_ENV || "development";
return new Promise<number>((resolve) => {
compile({
opts: args,
builder: (opts: esbuild.BuildOptions) =>
esbuild.context(opts).then((esb) => esb.watch()),
});
process.on("exit", resolve);
});
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

compile() return value is not awaited — initial build errors will be unhandled rejections.

The promise returned by compile(...) is neither awaited nor .catch()-ed, so if the initial build fails, the error surfaces as an unhandled promise rejection instead of being caught by main()'s try/catch.

Proposed fix
 const dev = () => {
   process.env.NODE_ENV = process.env.NODE_ENV || "development";
-  return new Promise<number>((resolve) => {
-    compile({
+  return new Promise<number>((resolve, reject) => {
+    compile({
       opts: args,
       builder: (opts: esbuild.BuildOptions) =>
         esbuild.context(opts).then((esb) => esb.watch()),
-    });
+    }).catch(reject);
     process.on("exit", resolve);
   });
 };
📝 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 dev = () => {
process.env.NODE_ENV = process.env.NODE_ENV || "development";
return new Promise<number>((resolve) => {
compile({
opts: args,
builder: (opts: esbuild.BuildOptions) =>
esbuild.context(opts).then((esb) => esb.watch()),
});
process.on("exit", resolve);
});
};
const dev = () => {
process.env.NODE_ENV = process.env.NODE_ENV || "development";
return new Promise<number>((resolve, reject) => {
compile({
opts: args,
builder: (opts: esbuild.BuildOptions) =>
esbuild.context(opts).then((esb) => esb.watch()),
}).catch(reject);
process.on("exit", resolve);
});
};
🤖 Prompt for AI Agents
In `@scripts/dev.ts` around lines 7 - 17, The dev function calls compile(...) but
doesn't await or handle its promise, so initial build errors become unhandled
rejections; update dev to propagate compile failures by either making dev async
and awaiting compile(...) or by adding a reject handler in the returned Promise
(e.g., new Promise<number>((resolve, reject) => { const p = compile(...);
p.then(...).catch(reject); process.on("exit", resolve); })). Ensure you
reference the existing compile(...) invocation and keep the existing builder
logic (esbuild.context(...).then(esb => esb.watch())) while wiring its returned
promise into the dev promise so main()'s try/catch can observe errors.

Comment on lines +494 to +519
editor.registerExternalContentHandler("text", async (content) => {
if (content.type !== "text") return;
const trimmedText = content.text.trim();
const match = getNodeTypeFromRoamRefText(trimmedText);
if (!match) {
await defaultHandleExternalTextContent(editor, {
point: content.point,
text: content.text,
});
return;
}
const point =
content.point ?? editor.getViewportPageBounds().center;
editor.createShape<RoamNodeShape>({
id: createShapeId(),
type: match.type,
x: point.x,
y: point.y,
props: {
uid: match.uid,
title: match.title,
w: 220,
h: 92,
},
});
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

tldraw registerExternalContentHandler documentation API

💡 Result:

registerExternalContentHandler is an Editor method in tldraw for customizing how the editor handles external content (paste/drag-drop) for a given content type (e.g. text, image, url, embed). You register (or clear) a handler per type. [1]

API (from docs)

editor.registerExternalContentHandler<T extends TLExternalContent<E>['type'], E>(
  type: T,
  handler: ((info: /* the matching TLExternalContent payload */) => void) | null
): this
  • Pass a function to register a handler; pass null to remove it. [1]

Docs & examples

  • Editor API reference (includes the signature + examples like editor.registerExternalContentHandler('text', myHandler) and typed 'embed' usage). [1]
  • Example: External content sources shows handling pasted 'text/html' by inspecting sources and then creating a custom shape (otherwise falling back to the default handler). [2]
  • If you want the built-in behavior as a starting point, see registerDefaultExternalContentHandlers(editor, options) and the defaultHandleExternal* helpers. [3] [4]

Sources: [1] tldraw Editor reference • registerExternalContentHandler section, [2] “External content sources” example, [3] registerDefaultExternalContentHandlers reference, [4] defaultHandleExternalTextContent reference.


🏁 Script executed:

# First, let's examine the full Tldraw.tsx component to understand the mount/unmount lifecycle
fd "Tldraw.tsx" --type f

Repository: RoamJS/tldraw

Length of output: 89


🏁 Script executed:

# Get the full context of the Tldraw.tsx component focusing on hooks and lifecycle
cat -n src/components/canvas/Tldraw.tsx | head -550 | tail -150

Repository: RoamJS/tldraw

Length of output: 6368


🏁 Script executed:

# Also check the beginning of the component to find useEffect hooks
cat -n src/components/canvas/Tldraw.tsx | head -100

Repository: RoamJS/tldraw

Length of output: 3908


🏁 Script executed:

# Search for useEffect, cleanup, and unmount patterns in Tldraw.tsx
rg "useEffect|cleanup|onUnmount|unmount" src/components/canvas/Tldraw.tsx -n

Repository: RoamJS/tldraw

Length of output: 355


🏁 Script executed:

# Check the end of the component file to see if there's any cleanup
tail -100 src/components/canvas/Tldraw.tsx

Repository: RoamJS/tldraw

Length of output: 2386


🏁 Script executed:

# Search for any code that might be unregistering the handler or handling Tldraw unmount
rg "registerExternalContentHandler|unregister|null" src/components/canvas/Tldraw.tsx -n -B2 -A2

Repository: RoamJS/tldraw

Length of output: 2514


🏁 Script executed:

# Let's examine the useEffect hooks to see if any handle cleanup of the handler
sed -n '165,180p' src/components/canvas/Tldraw.tsx

Repository: RoamJS/tldraw

Length of output: 459


🏁 Script executed:

# Also check what happens on component unmount - look for useEffect dependencies
sed -n '361,380p' src/components/canvas/Tldraw.tsx

Repository: RoamJS/tldraw

Length of output: 726


🏁 Script executed:

# Let's check how Tldraw component is used - specifically the onUnmount or lifecycle
rg "Tldraw" src/components/canvas/Tldraw.tsx -n -B2 -A10 | grep -A15 "onMount"

Repository: RoamJS/tldraw

Length of output: 396


🌐 Web query:

tldraw editor cleanup handlers unmount component lifecycle

💡 Result:

In tldraw, anything you “subscribe” to (events, store listeners, side-effect handlers) should be explicitly unsubscribed/removed in your component’s unmount cleanup.

1) Editor events: editor.on(...)editor.off(...)

tldraw’s Editor is an EventEmitter; subscribe with on and always unsubscribe with off using the same handler function. (tldraw.dev)

useEffect(() => {
  if (!editor) return

  const handleChange = (entry: any) => {
    // ...
  }

  editor.on('change', handleChange)
  return () => editor.off('change', handleChange)
}, [editor])

2) Store subscriptions: editor.store.listen(...) returns a cleanup function

editor.store.listen(...) returns an “unsubscribe” function—call it on unmount. (tldraw.dev)

useEffect(() => {
  if (!editor) return
  const stop = editor.store.listen((entry) => {
    // ...
  })
  return () => stop()
}, [editor])

3) Side-effects (record lifecycle handlers): registration returns a removal function

editor.sideEffects.register*Handler(...) returns () => void that removes the handler—call that on unmount too. (tldraw.dev)

useEffect(() => {
  if (!editor) return
  const unregister = editor.sideEffects.registerAfterCreateHandler('shape', (shape) => {
    // ...
  })
  return () => unregister()
}, [editor])

If you set these up inside Tldraw’s onMount, store the returned cleanup/unregister functions somewhere your React component’s useEffect cleanup can reach (or prefer wiring them in useEffect once you have the editor instance). (tldraw.dev)

Citations:


🏁 Script executed:

# Check if there are any other examples in the codebase of handler cleanup
rg "registerExternalContentHandler" -A5 -B5

Repository: RoamJS/tldraw

Length of output: 819


🏁 Script executed:

# Check if registerExternalContentHandler returns a cleanup function or if null is used for removal
# Let's look for any documentation or examples in node_modules or comments
rg "registerExternalContentHandler" -A3 | head -30

Repository: RoamJS/tldraw

Length of output: 434


🏁 Script executed:

# Also check if the editor.on("event") handler has cleanup - that's at line 458
sed -n '458,492p' src/components/canvas/Tldraw.tsx

Repository: RoamJS/tldraw

Length of output: 1321


Both event listeners and external content handlers registered in onMount require cleanup on unmount.

Line 458: editor.on("event", ...) and line 494: editor.registerExternalContentHandler("text", ...) are registered inside onMount without cleanup. Per tldraw best practices, event listeners must be unsubscribed with editor.off() in a cleanup function, and external content handlers should be unregistered (by passing null) on unmount. Since the component can unmount (via renderWithUnmount), these subscriptions will leak. Move these registrations into useEffect hooks with proper cleanup:

useEffect(() => {
  const editor = appRef.current;
  if (!editor) return;
  
  const handleEvent = (event) => { /* ... */ };
  editor.on("event", handleEvent);
  return () => editor.off("event", handleEvent);
}, []);

useEffect(() => {
  const editor = appRef.current;
  if (!editor) return;
  
  editor.registerExternalContentHandler("text", async (content) => { /* ... */ });
  return () => editor.registerExternalContentHandler("text", null);
}, []);
🤖 Prompt for AI Agents
In `@src/components/canvas/Tldraw.tsx` around lines 494 - 519, The onMount block
registers listeners without cleanup causing leaks; move the registrations for
editor.on(...) and editor.registerExternalContentHandler("text", ...) into
separate useEffect hooks that run when the editor instance is ready, and in each
effect return a cleanup that calls editor.off(...) for the exact handler
registered and editor.registerExternalContentHandler("text", null) to unregister
the external handler; keep the handler logic (including calls to
getNodeTypeFromRoamRefText, defaultHandleExternalTextContent, createShapeId, and
creating a RoamNodeShape) identical but reference the same handler functions so
they can be removed by editor.off during cleanup.

Comment on lines +71 to +74
/* Roam's main container should be static when tldraw is maximized */
.roam-main {
position: relative;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Broad .roam-main rule may cause unintended side-effects.

This selector applies position: relative to all .roam-main elements globally, not only when the tldraw canvas is maximized. If Roam's default positioning for this element is something other than relative, this could break layouts on non-canvas pages. Consider scoping it, e.g., .roam-main:has(.roamjs-tldraw-canvas-container).

💡 Proposed scoping fix
-/* Roam's main container should be static when tldraw is maximized */
-.roam-main {
-  position: relative;
-}
+/* Roam's main container should be relative when a tldraw canvas is present */
+.roam-main:has(.roamjs-tldraw-canvas-container) {
+  position: relative;
+}
📝 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
/* Roam's main container should be static when tldraw is maximized */
.roam-main {
position: relative;
}
/* Roam's main container should be relative when a tldraw canvas is present */
.roam-main:has(.roamjs-tldraw-canvas-container) {
position: relative;
}
🤖 Prompt for AI Agents
In `@src/components/canvas/tldrawStyles.ts` around lines 71 - 74, The global
`.roam-main { position: relative; }` rule affects all pages; narrow its scope so
it only applies when the tldraw canvas is active — update the selector used in
tldrawStyles.ts to target `.roam-main` only when it contains the canvas (e.g.
`.roam-main:has(.roamjs-tldraw-canvas-container) { position: relative; }`) or,
if :has isn’t acceptable, apply a contextual class when maximizing the canvas
(e.g. add/remove `.roamjs-tldraw-maximized` and use `.roamjs-tldraw-maximized
.roam-main { position: relative; }`) and remove the original broad `.roam-main`
rule.

Comment on lines +81 to +94
Toolbar: () => {
const tools = useTools();
return (
<DefaultToolbar>
{DEFAULT_NODE_TOOLS.map((tool) => (
<TldrawUiMenuItem
key={tool.id}
{...tools[tool.id]}
isSelected={useIsToolSelected(tools[tool.id])}
/>
))}
<DefaultToolbarContent />
</DefaultToolbar>
);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

useIsToolSelected called inside .map() violates the Rules of Hooks.

Line 89 calls useIsToolSelected(tools[tool.id]) inside the .map() callback. React hooks must not be called inside loops, conditions, or nested functions. Although DEFAULT_NODE_TOOLS is a fixed-length array (2 items), React's hook ordering contract is still violated, and linters will flag this.

Extract a wrapper component for each tool item:

🔧 Proposed fix
+const ToolbarNodeItem = ({ toolId }: { toolId: string }) => {
+  const tools = useTools();
+  const tool = tools[toolId];
+  const isSelected = useIsToolSelected(tool);
+  return <TldrawUiMenuItem {...tool} isSelected={isSelected} />;
+};
+
 export const createUiComponents = (): TLUiComponents => ({
   Toolbar: () => {
-    const tools = useTools();
     return (
       <DefaultToolbar>
-        {DEFAULT_NODE_TOOLS.map((tool) => (
-          <TldrawUiMenuItem
-            key={tool.id}
-            {...tools[tool.id]}
-            isSelected={useIsToolSelected(tools[tool.id])}
-          />
+        {DEFAULT_NODE_TOOLS.map((tool) => (
+          <ToolbarNodeItem key={tool.id} toolId={tool.id} />
         ))}
         <DefaultToolbarContent />
       </DefaultToolbar>
     );
   },
🤖 Prompt for AI Agents
In `@src/components/canvas/uiOverrides.tsx` around lines 81 - 94, The map in
Toolbar violates the Rules of Hooks by calling useIsToolSelected inside a .map
callback; fix by extracting a new child component (e.g., ToolMenuItem) that
accepts the tool descriptor and the tool data from useTools(), call
useIsToolSelected(toolData) inside that component, and render TldrawUiMenuItem
there; then change the map to return <ToolMenuItem key={tool.id} tool={tool}
toolData={tools[tool.id]} /> so hooks are only invoked at the top level of a
React component (referencing Toolbar, DEFAULT_NODE_TOOLS, useTools,
useIsToolSelected, and TldrawUiMenuItem).

Comment on lines +65 to +82
const setPersistedSnapshot = ({
pageUid,
state,
}: {
pageUid: string;
state: RoamTldrawState;
}): void => {
const props = getPageProps(pageUid);
void window.roamAlphaAPI.data.page.update({
page: {
uid: pageUid,
props: {
...props,
[ROAM_TLDRAW_KEY]: state,
},
},
});
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Unhandled write errors in setPersistedSnapshot.

void discards the promise from window.roamAlphaAPI.data.page.update(...), so any write failure (network error, API error) is silently swallowed. Since this is the persistence path for canvas state, a failed write could cause data loss without any user feedback.

Consider adding a .catch() to at least log failures:

Proposed fix
-  void window.roamAlphaAPI.data.page.update({
+  window.roamAlphaAPI.data.page.update({
     page: {
       uid: pageUid,
       props: {
         ...props,
         [ROAM_TLDRAW_KEY]: state,
       },
     },
-  });
+  }).catch((err: unknown) => {
+    console.error("Failed to persist tldraw snapshot:", err);
+  });
📝 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 setPersistedSnapshot = ({
pageUid,
state,
}: {
pageUid: string;
state: RoamTldrawState;
}): void => {
const props = getPageProps(pageUid);
void window.roamAlphaAPI.data.page.update({
page: {
uid: pageUid,
props: {
...props,
[ROAM_TLDRAW_KEY]: state,
},
},
});
};
const setPersistedSnapshot = ({
pageUid,
state,
}: {
pageUid: string;
state: RoamTldrawState;
}): void => {
const props = getPageProps(pageUid);
window.roamAlphaAPI.data.page.update({
page: {
uid: pageUid,
props: {
...props,
[ROAM_TLDRAW_KEY]: state,
},
},
}).catch((err: unknown) => {
console.error("Failed to persist tldraw snapshot:", err);
});
};
🤖 Prompt for AI Agents
In `@src/components/canvas/useRoamStore.ts` around lines 65 - 82, The
setPersistedSnapshot function currently discards errors by using void on
window.roamAlphaAPI.data.page.update; modify setPersistedSnapshot (and its call
sites if needed) to handle promise rejections — either await the update inside
an async function or append a .catch(...) to the returned promise — and
log/report failures (use existing logger or console.error) including pageUid and
the error so write failures to ROAM_TLDRAW_KEY are visible; ensure you still
merge props via getPageProps(pageUid) and do not change the stored payload
shape.

…onfiguration

- Downgraded tldraw from version 3.15.5 to 3.15.1 to address compatibility issues.
- Updated package.json to include an external dependency for react-dom/client, improving integration with the global ReactDOM object.
… rendering

- Simplified the rendering logic by utilizing the BlockString component from roamAlphaAPI.
- Replaced the contentRef and innerHTML manipulation with a more straightforward React.createElement approach.
- Updated the return structure to use a span for better styling and text handling.
- Removed unnecessary external dependencies from the samepage configuration in package.json.
- Added new dependencies for @blueprintjs/core and @juggle/resize-observer in pnpm-lock.yaml to ensure compatibility with React 18.
- Updated existing @blueprintjs/core references to the latest version for improved functionality.
Copy link
Copy Markdown

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 1 new potential issue.

View 12 additional findings in Devin Review.

Open in Devin Review

Comment on lines +85 to +91
{DEFAULT_NODE_TOOLS.map((tool) => (
<TldrawUiMenuItem
key={tool.id}
{...tools[tool.id]}
isSelected={useIsToolSelected(tools[tool.id])}
/>
))}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 useIsToolSelected hook called inside .map() loop violates Rules of Hooks

In the Toolbar component returned by createUiComponents, the useIsToolSelected React hook is called inside a .map() callback at line 89, which violates React's Rules of Hooks.

Detailed Explanation

At src/components/canvas/uiOverrides.tsx:85-91, the code does:

{DEFAULT_NODE_TOOLS.map((tool) => (
  <TldrawUiMenuItem
    key={tool.id}
    {...tools[tool.id]}
    isSelected={useIsToolSelected(tools[tool.id])}
  />
))}

useIsToolSelected is a React hook (it starts with use and internally calls other hooks). Calling it inside .map() violates the Rules of Hooks, which state hooks must not be called inside loops, conditions, or nested functions.

In this specific case, DEFAULT_NODE_TOOLS is a constant array with exactly 2 elements that never changes, so the number of hook calls is always the same and this won't cause a runtime crash. However, it will trigger ESLint's react-hooks/rules-of-hooks rule and is fragile — if the array ever becomes dynamic, it would break. The proper fix is to extract each tool item into its own component that calls useIsToolSelected at the top level.

Impact: Currently works due to constant array size, but violates React's contract and will break if the array becomes dynamic.

Prompt for agents
In src/components/canvas/uiOverrides.tsx, extract the tool menu items in the Toolbar component (lines 85-91) into a separate component. Create a new component like:

const ToolbarItem = ({ toolId }: { toolId: string }) => {
  const tools = useTools();
  const tool = tools[toolId];
  const isSelected = useIsToolSelected(tool);
  return <TldrawUiMenuItem {...tool} isSelected={isSelected} />;
};

Then in the Toolbar component, replace the .map() with:
{DEFAULT_NODE_TOOLS.map((tool) => (
  <ToolbarItem key={tool.id} toolId={tool.id} />
))}

This ensures useIsToolSelected is called at the top level of a component, not inside a loop.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown

@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: 1

🤖 Fix all issues with AI agents
In `@patches/`@blueprintjs__core@3.50.4.patch:
- Around line 1-384: Summary: The esnext build is missing the tooltip type
patch; the ITooltipProps interface in the esnext tooltip declaration lacks
children?: React.ReactNode. Fix: update the esnext tooltip declaration (the
ITooltipProps interface for TooltipProps / ITooltipProps) to include children?:
React.ReactNode exactly like the cjs/esm variants, ensuring the ITooltipProps
declaration for the tooltip component mirrors the other build targets.
🧹 Nitpick comments (5)
tsconfig.json (1)

3-9: "src/types.d.ts" is redundant in include.

"src" already covers all files under src/, including src/types.d.ts.

Proposed fix
   "include": [
     "src",
-    "src/types.d.ts",
     "tailwind.config.ts",
     "tests",
     "scripts"
   ],
src/components/canvas/DefaultNodeUtil.tsx (2)

58-126: searchPages and searchBlocks are near-duplicates — consider extracting a shared helper.

The two functions differ only in the Datalog attribute (:node/title vs :block/string) and the binding variable name. A single parameterized helper would eliminate ~30 lines of duplication and ensure future fixes (e.g., pagination, error handling) apply uniformly.

Also, in searchBlocks at line 119, the destructured variable is named title but it actually represents block string content (?text). This is misleading and could confuse future maintainers.

Sketch of a shared helper
+const searchRoamEntities = (
+  query: string,
+  attr: ":node/title" | ":block/string",
+): Promise<SearchResult[]> => {
+  const pattern = escapeRegex(query.trim());
+  const binding = attr === ":node/title" ? "?title" : "?text";
+  return (
+    (pattern
+      ? window.roamAlphaAPI.data.async.q(
+          `[:find ?uid ${binding} ?time
+            :where
+              [?e ${attr} ${binding}]
+              [?e :block/uid ?uid]
+              [(get-else $ ?e :edit/time 0) ?time]
+              [(re-pattern "(?i)${pattern}") ?re]
+              [(re-find ?re ${binding})]]`,
+        )
+      : window.roamAlphaAPI.data.async.q(
+          `[:find ?uid ${binding} ?time
+            :where
+              [?e ${attr} ${binding}]
+              [?e :block/uid ?uid]
+              [(get-else $ ?e :edit/time 0) ?time]]`,
+        )) as Promise<[string, string, number][]>
+  ).then((rows) =>
+    rows
+      .map(([uid, text, editTime]) => ({
+        uid,
+        title: text,
+        editTime: editTime || 0,
+      }))
+      .sort((a, b) => b.editTime - a.editTime),
+  );
+};
+
+export const searchPages = ({ query }: { query: string }) =>
+  searchRoamEntities(query, ":node/title");
+
+export const searchBlocks = ({ query }: { query: string }) =>
+  searchRoamEntities(query, ":block/string");

267-298: Anonymous tool classes created in .map() all share the same displayName.

Each generated class is named DefaultNodeTool in the source, but the static id is correctly set per tool. This means debugging/stack traces will show the same class name for both tools. Not a functional issue, but worth noting.

package.json (2)

39-43: Exact-pinned peer dependencies are restrictive.

Using exact versions (18.2.0) for react/react-dom peers means any host using 18.3.x will get unmet-peer warnings (or errors in strict mode). If this is intentional for Roam Research compatibility, consider adding a comment. Otherwise, use a range like "^18.2.0".


35-35: esbuild@0.17.14 is nearly 3 years old — consider upgrading to the latest version.

This exact pin dates to March 26, 2023. Current esbuild (v0.27.3 as of Feb 2026) includes significant performance improvements, bug fixes, security patches, and CSS features. The gap between 0.17.14 and 0.27.3 represents substantial development. If this pin is intentional for compatibility reasons, it's worth documenting.

Comment on lines +1 to +384
diff --git a/lib/cjs/components/alert/alert.d.ts b/lib/cjs/components/alert/alert.d.ts
index 09b06be01bbe6d22a7dda4aa1652a19b50ff77ec..f74f6a34012e1fa97761bfec2f78fa6ba6dfb0d1 100644
--- a/lib/cjs/components/alert/alert.d.ts
+++ b/lib/cjs/components/alert/alert.d.ts
@@ -5,6 +5,7 @@ import { IOverlayLifecycleProps } from "../overlay/overlay";
export declare type AlertProps = IAlertProps;
/** @deprecated use AlertProps */
export interface IAlertProps extends IOverlayLifecycleProps, Props {
+ children?: React.ReactNode;
/**
* Whether pressing <kbd>escape</kbd> when focused on the Alert should cancel the alert.
* If this prop is enabled, then either `onCancel` or `onClose` must also be defined.
diff --git a/lib/cjs/components/collapse/collapse.d.ts b/lib/cjs/components/collapse/collapse.d.ts
index 7f4ef301528cfa44000c9f330dc0e9bd4d26aa63..a93bb0951783f56799a16431110ac280a38ffba9 100644
--- a/lib/cjs/components/collapse/collapse.d.ts
+++ b/lib/cjs/components/collapse/collapse.d.ts
@@ -4,6 +4,7 @@ import { Props } from "../../common/props";
export declare type CollapseProps = ICollapseProps;
/** @deprecated use CollapseProps */
export interface ICollapseProps extends Props {
+ children?: React.ReactNode;
/**
* Component to render as the root element.
* Useful when rendering a `Collapse` inside a `<table>`, for instance.
diff --git a/lib/cjs/components/dialog/dialog.d.ts b/lib/cjs/components/dialog/dialog.d.ts
index e90ee312da59d1e7a404dd029a79af640a9c3ea4..3e4f4ef68fd429fb9e272df3c730e556c163dca4 100644
--- a/lib/cjs/components/dialog/dialog.d.ts
+++ b/lib/cjs/components/dialog/dialog.d.ts
@@ -6,6 +6,7 @@ import { IBackdropProps, OverlayableProps } from "../overlay/overlay";
export declare type DialogProps = IDialogProps;
/** @deprecated use DialogProps */
export interface IDialogProps extends OverlayableProps, IBackdropProps, Props {
+ children?: React.ReactNode;
/**
* Toggles the visibility of the overlay and its children.
* This prop is required because the component is controlled.
diff --git a/lib/cjs/components/drawer/drawer.d.ts b/lib/cjs/components/drawer/drawer.d.ts
index 301ed2e66dfb42850c5671220b8c5241df560e9a..24d4b161d42d201f9f45f9ab8f1f874746db4430 100644
--- a/lib/cjs/components/drawer/drawer.d.ts
+++ b/lib/cjs/components/drawer/drawer.d.ts
@@ -12,6 +12,7 @@ export declare enum DrawerSize {
export declare type DrawerProps = IDrawerProps;
/** @deprecated use DrawerProps */
export interface IDrawerProps extends OverlayableProps, IBackdropProps, Props {
+ children?: React.ReactNode;
/**
* Name of a Blueprint UI icon (or an icon element) to render in the
* drawer's header. Note that the header will only be rendered if `title` is
diff --git a/lib/cjs/components/forms/formGroup.d.ts b/lib/cjs/components/forms/formGroup.d.ts
index 1b69eeb6f353e44c1400a223c129cba88738e655..961ba5ef85105aed8db86c7d64254d468baa3b7f 100644
--- a/lib/cjs/components/forms/formGroup.d.ts
+++ b/lib/cjs/components/forms/formGroup.d.ts
@@ -4,6 +4,7 @@ import { IntentProps, Props } from "../../common/props";
export declare type FormGroupProps = IFormGroupProps;
/** @deprecated use FormGroupProps */
export interface IFormGroupProps extends IntentProps, Props {
+ children?: React.ReactNode;
/**
* A space-delimited list of class names to pass along to the
* `Classes.FORM_CONTENT` element that contains `children`.
diff --git a/lib/cjs/components/forms/radioGroup.d.ts b/lib/cjs/components/forms/radioGroup.d.ts
index 2f21804fca8599d6d98a6be80473d03a52419196..e96697514e15a44aa614e4086503848614956210 100644
--- a/lib/cjs/components/forms/radioGroup.d.ts
+++ b/lib/cjs/components/forms/radioGroup.d.ts
@@ -4,6 +4,7 @@ import { OptionProps, Props } from "../../common/props";
export declare type RadioGroupProps = IRadioGroupProps;
/** @deprecated use RadioGroupProps */
export interface IRadioGroupProps extends Props {
+ children?: React.ReactNode;
/**
* Whether the group and _all_ its radios are disabled.
* Individual radios can be disabled using their `disabled` prop.
diff --git a/lib/cjs/components/popover/popover.d.ts b/lib/cjs/components/popover/popover.d.ts
index e1490579f16bbc57b9bd37e0d782a4c7c55196fa..f3cdf4a9fe3bd32e19bd9220662115930a94e03f 100644
--- a/lib/cjs/components/popover/popover.d.ts
+++ b/lib/cjs/components/popover/popover.d.ts
@@ -9,6 +9,7 @@ export declare const PopoverInteractionKind: {
};
export declare type PopoverInteractionKind = typeof PopoverInteractionKind[keyof typeof PopoverInteractionKind];
export interface IPopoverProps extends IPopoverSharedProps {
+ children?: React.ReactNode;
/** HTML props for the backdrop element. Can be combined with `backdropClassName`. */
backdropProps?: React.HTMLProps<HTMLDivElement>;
/**
diff --git a/lib/cjs/components/portal/portal.d.ts b/lib/cjs/components/portal/portal.d.ts
index 9e3460b460c48dad99ef04c26261094bb5c4a5d3..5ffb40eeb79c6fc838d63b1261eafc964bd26b14 100644
--- a/lib/cjs/components/portal/portal.d.ts
+++ b/lib/cjs/components/portal/portal.d.ts
@@ -3,6 +3,7 @@ import { ValidationMap } from "../../common/context";
import { Props } from "../../common/props";
export declare type PortalProps = IPortalProps;
export interface IPortalProps extends Props {
+ children?: React.ReactNode;
/**
* Callback invoked when the children of this `Portal` have been added to the DOM.
*/
diff --git a/lib/cjs/components/tabs/tabs.d.ts b/lib/cjs/components/tabs/tabs.d.ts
index 2ca5fe5b4bed0871154292aa3248af1f2ea8331d..870a32ee7d2bd6c934216e268016b4b2d2e2b514 100644
--- a/lib/cjs/components/tabs/tabs.d.ts
+++ b/lib/cjs/components/tabs/tabs.d.ts
@@ -6,6 +6,7 @@ export declare const Expander: React.FunctionComponent;
export declare type TabsProps = ITabsProps;
/** @deprecated use TabsProps */
export interface ITabsProps extends Props {
+ children?: React.ReactNode;
/**
* Whether the selected tab indicator should animate its movement.
*
diff --git a/lib/cjs/components/toast/toaster.d.ts b/lib/cjs/components/toast/toaster.d.ts
index a6c712467c7ac81aed9ea00207aeaaeb14bd9337..353621182cb1c4d8c5ea6430504ab0ed38fec195 100644
--- a/lib/cjs/components/toast/toaster.d.ts
+++ b/lib/cjs/components/toast/toaster.d.ts
@@ -26,6 +26,7 @@ export interface IToaster {
* These props can be passed as an argument to the static `Toaster.create(props?, container?)` method.
*/
export interface IToasterProps extends Props {
+ children?: React.ReactNode;
/**
* Whether a toast should acquire application focus when it first opens.
* This is disabled by default so that toasts do not interrupt the user's flow.
diff --git a/lib/cjs/components/tooltip/tooltip.d.ts b/lib/cjs/components/tooltip/tooltip.d.ts
index 94f4af9427a35ad9333f2bfd30ab712499efcd57..4db48a9bc4cac14aacd90535f403f87b76445815 100644
--- a/lib/cjs/components/tooltip/tooltip.d.ts
+++ b/lib/cjs/components/tooltip/tooltip.d.ts
@@ -6,6 +6,7 @@ import { IPopoverSharedProps } from "../popover/popoverSharedProps";
export declare type TooltipProps = ITooltipProps;
/** @deprecated use TooltipProps */
export interface ITooltipProps extends IPopoverSharedProps, IntentProps {
+ children?: React.ReactNode;
/**
* The content that will be displayed inside of the tooltip.
*/
diff --git a/lib/esm/components/alert/alert.d.ts b/lib/esm/components/alert/alert.d.ts
index 09b06be01bbe6d22a7dda4aa1652a19b50ff77ec..f74f6a34012e1fa97761bfec2f78fa6ba6dfb0d1 100644
--- a/lib/esm/components/alert/alert.d.ts
+++ b/lib/esm/components/alert/alert.d.ts
@@ -5,6 +5,7 @@ import { IOverlayLifecycleProps } from "../overlay/overlay";
export declare type AlertProps = IAlertProps;
/** @deprecated use AlertProps */
export interface IAlertProps extends IOverlayLifecycleProps, Props {
+ children?: React.ReactNode;
/**
* Whether pressing <kbd>escape</kbd> when focused on the Alert should cancel the alert.
* If this prop is enabled, then either `onCancel` or `onClose` must also be defined.
diff --git a/lib/esm/components/collapse/collapse.d.ts b/lib/esm/components/collapse/collapse.d.ts
index 7f4ef301528cfa44000c9f330dc0e9bd4d26aa63..a93bb0951783f56799a16431110ac280a38ffba9 100644
--- a/lib/esm/components/collapse/collapse.d.ts
+++ b/lib/esm/components/collapse/collapse.d.ts
@@ -4,6 +4,7 @@ import { Props } from "../../common/props";
export declare type CollapseProps = ICollapseProps;
/** @deprecated use CollapseProps */
export interface ICollapseProps extends Props {
+ children?: React.ReactNode;
/**
* Component to render as the root element.
* Useful when rendering a `Collapse` inside a `<table>`, for instance.
diff --git a/lib/esm/components/dialog/dialog.d.ts b/lib/esm/components/dialog/dialog.d.ts
index e90ee312da59d1e7a404dd029a79af640a9c3ea4..3e4f4ef68fd429fb9e272df3c730e556c163dca4 100644
--- a/lib/esm/components/dialog/dialog.d.ts
+++ b/lib/esm/components/dialog/dialog.d.ts
@@ -6,6 +6,7 @@ import { IBackdropProps, OverlayableProps } from "../overlay/overlay";
export declare type DialogProps = IDialogProps;
/** @deprecated use DialogProps */
export interface IDialogProps extends OverlayableProps, IBackdropProps, Props {
+ children?: React.ReactNode;
/**
* Toggles the visibility of the overlay and its children.
* This prop is required because the component is controlled.
diff --git a/lib/esm/components/drawer/drawer.d.ts b/lib/esm/components/drawer/drawer.d.ts
index 301ed2e66dfb42850c5671220b8c5241df560e9a..24d4b161d42d201f9f45f9ab8f1f874746db4430 100644
--- a/lib/esm/components/drawer/drawer.d.ts
+++ b/lib/esm/components/drawer/drawer.d.ts
@@ -12,6 +12,7 @@ export declare enum DrawerSize {
export declare type DrawerProps = IDrawerProps;
/** @deprecated use DrawerProps */
export interface IDrawerProps extends OverlayableProps, IBackdropProps, Props {
+ children?: React.ReactNode;
/**
* Name of a Blueprint UI icon (or an icon element) to render in the
* drawer's header. Note that the header will only be rendered if `title` is
diff --git a/lib/esm/components/forms/formGroup.d.ts b/lib/esm/components/forms/formGroup.d.ts
index 1b69eeb6f353e44c1400a223c129cba88738e655..961ba5ef85105aed8db86c7d64254d468baa3b7f 100644
--- a/lib/esm/components/forms/formGroup.d.ts
+++ b/lib/esm/components/forms/formGroup.d.ts
@@ -4,6 +4,7 @@ import { IntentProps, Props } from "../../common/props";
export declare type FormGroupProps = IFormGroupProps;
/** @deprecated use FormGroupProps */
export interface IFormGroupProps extends IntentProps, Props {
+ children?: React.ReactNode;
/**
* A space-delimited list of class names to pass along to the
* `Classes.FORM_CONTENT` element that contains `children`.
diff --git a/lib/esm/components/forms/radioGroup.d.ts b/lib/esm/components/forms/radioGroup.d.ts
index 2f21804fca8599d6d98a6be80473d03a52419196..e96697514e15a44aa614e4086503848614956210 100644
--- a/lib/esm/components/forms/radioGroup.d.ts
+++ b/lib/esm/components/forms/radioGroup.d.ts
@@ -4,6 +4,7 @@ import { OptionProps, Props } from "../../common/props";
export declare type RadioGroupProps = IRadioGroupProps;
/** @deprecated use RadioGroupProps */
export interface IRadioGroupProps extends Props {
+ children?: React.ReactNode;
/**
* Whether the group and _all_ its radios are disabled.
* Individual radios can be disabled using their `disabled` prop.
diff --git a/lib/esm/components/popover/popover.d.ts b/lib/esm/components/popover/popover.d.ts
index e1490579f16bbc57b9bd37e0d782a4c7c55196fa..f3cdf4a9fe3bd32e19bd9220662115930a94e03f 100644
--- a/lib/esm/components/popover/popover.d.ts
+++ b/lib/esm/components/popover/popover.d.ts
@@ -9,6 +9,7 @@ export declare const PopoverInteractionKind: {
};
export declare type PopoverInteractionKind = typeof PopoverInteractionKind[keyof typeof PopoverInteractionKind];
export interface IPopoverProps extends IPopoverSharedProps {
+ children?: React.ReactNode;
/** HTML props for the backdrop element. Can be combined with `backdropClassName`. */
backdropProps?: React.HTMLProps<HTMLDivElement>;
/**
diff --git a/lib/esm/components/portal/portal.d.ts b/lib/esm/components/portal/portal.d.ts
index 9e3460b460c48dad99ef04c26261094bb5c4a5d3..5ffb40eeb79c6fc838d63b1261eafc964bd26b14 100644
--- a/lib/esm/components/portal/portal.d.ts
+++ b/lib/esm/components/portal/portal.d.ts
@@ -3,6 +3,7 @@ import { ValidationMap } from "../../common/context";
import { Props } from "../../common/props";
export declare type PortalProps = IPortalProps;
export interface IPortalProps extends Props {
+ children?: React.ReactNode;
/**
* Callback invoked when the children of this `Portal` have been added to the DOM.
*/
diff --git a/lib/esm/components/tabs/tabs.d.ts b/lib/esm/components/tabs/tabs.d.ts
index 2ca5fe5b4bed0871154292aa3248af1f2ea8331d..870a32ee7d2bd6c934216e268016b4b2d2e2b514 100644
--- a/lib/esm/components/tabs/tabs.d.ts
+++ b/lib/esm/components/tabs/tabs.d.ts
@@ -6,6 +6,7 @@ export declare const Expander: React.FunctionComponent;
export declare type TabsProps = ITabsProps;
/** @deprecated use TabsProps */
export interface ITabsProps extends Props {
+ children?: React.ReactNode;
/**
* Whether the selected tab indicator should animate its movement.
*
diff --git a/lib/esm/components/toast/toaster.d.ts b/lib/esm/components/toast/toaster.d.ts
index a6c712467c7ac81aed9ea00207aeaaeb14bd9337..353621182cb1c4d8c5ea6430504ab0ed38fec195 100644
--- a/lib/esm/components/toast/toaster.d.ts
+++ b/lib/esm/components/toast/toaster.d.ts
@@ -26,6 +26,7 @@ export interface IToaster {
* These props can be passed as an argument to the static `Toaster.create(props?, container?)` method.
*/
export interface IToasterProps extends Props {
+ children?: React.ReactNode;
/**
* Whether a toast should acquire application focus when it first opens.
* This is disabled by default so that toasts do not interrupt the user's flow.
diff --git a/lib/esm/components/tooltip/tooltip.d.ts b/lib/esm/components/tooltip/tooltip.d.ts
index 94f4af9427a35ad9333f2bfd30ab712499efcd57..4db48a9bc4cac14aacd90535f403f87b76445815 100644
--- a/lib/esm/components/tooltip/tooltip.d.ts
+++ b/lib/esm/components/tooltip/tooltip.d.ts
@@ -6,6 +6,7 @@ import { IPopoverSharedProps } from "../popover/popoverSharedProps";
export declare type TooltipProps = ITooltipProps;
/** @deprecated use TooltipProps */
export interface ITooltipProps extends IPopoverSharedProps, IntentProps {
+ children?: React.ReactNode;
/**
* The content that will be displayed inside of the tooltip.
*/
diff --git a/lib/esnext/components/alert/alert.d.ts b/lib/esnext/components/alert/alert.d.ts
index 09b06be01bbe6d22a7dda4aa1652a19b50ff77ec..f74f6a34012e1fa97761bfec2f78fa6ba6dfb0d1 100644
--- a/lib/esnext/components/alert/alert.d.ts
+++ b/lib/esnext/components/alert/alert.d.ts
@@ -5,6 +5,7 @@ import { IOverlayLifecycleProps } from "../overlay/overlay";
export declare type AlertProps = IAlertProps;
/** @deprecated use AlertProps */
export interface IAlertProps extends IOverlayLifecycleProps, Props {
+ children?: React.ReactNode;
/**
* Whether pressing <kbd>escape</kbd> when focused on the Alert should cancel the alert.
* If this prop is enabled, then either `onCancel` or `onClose` must also be defined.
diff --git a/lib/esnext/components/collapse/collapse.d.ts b/lib/esnext/components/collapse/collapse.d.ts
index 7f4ef301528cfa44000c9f330dc0e9bd4d26aa63..a93bb0951783f56799a16431110ac280a38ffba9 100644
--- a/lib/esnext/components/collapse/collapse.d.ts
+++ b/lib/esnext/components/collapse/collapse.d.ts
@@ -4,6 +4,7 @@ import { Props } from "../../common/props";
export declare type CollapseProps = ICollapseProps;
/** @deprecated use CollapseProps */
export interface ICollapseProps extends Props {
+ children?: React.ReactNode;
/**
* Component to render as the root element.
* Useful when rendering a `Collapse` inside a `<table>`, for instance.
diff --git a/lib/esnext/components/dialog/dialog.d.ts b/lib/esnext/components/dialog/dialog.d.ts
index e90ee312da59d1e7a404dd029a79af640a9c3ea4..3e4f4ef68fd429fb9e272df3c730e556c163dca4 100644
--- a/lib/esnext/components/dialog/dialog.d.ts
+++ b/lib/esnext/components/dialog/dialog.d.ts
@@ -6,6 +6,7 @@ import { IBackdropProps, OverlayableProps } from "../overlay/overlay";
export declare type DialogProps = IDialogProps;
/** @deprecated use DialogProps */
export interface IDialogProps extends OverlayableProps, IBackdropProps, Props {
+ children?: React.ReactNode;
/**
* Toggles the visibility of the overlay and its children.
* This prop is required because the component is controlled.
diff --git a/lib/esnext/components/drawer/drawer.d.ts b/lib/esnext/components/drawer/drawer.d.ts
index 301ed2e66dfb42850c5671220b8c5241df560e9a..24d4b161d42d201f9f45f9ab8f1f874746db4430 100644
--- a/lib/esnext/components/drawer/drawer.d.ts
+++ b/lib/esnext/components/drawer/drawer.d.ts
@@ -12,6 +12,7 @@ export declare enum DrawerSize {
export declare type DrawerProps = IDrawerProps;
/** @deprecated use DrawerProps */
export interface IDrawerProps extends OverlayableProps, IBackdropProps, Props {
+ children?: React.ReactNode;
/**
* Name of a Blueprint UI icon (or an icon element) to render in the
* drawer's header. Note that the header will only be rendered if `title` is
diff --git a/lib/esnext/components/forms/formGroup.d.ts b/lib/esnext/components/forms/formGroup.d.ts
index 1b69eeb6f353e44c1400a223c129cba88738e655..961ba5ef85105aed8db86c7d64254d468baa3b7f 100644
--- a/lib/esnext/components/forms/formGroup.d.ts
+++ b/lib/esnext/components/forms/formGroup.d.ts
@@ -4,6 +4,7 @@ import { IntentProps, Props } from "../../common/props";
export declare type FormGroupProps = IFormGroupProps;
/** @deprecated use FormGroupProps */
export interface IFormGroupProps extends IntentProps, Props {
+ children?: React.ReactNode;
/**
* A space-delimited list of class names to pass along to the
* `Classes.FORM_CONTENT` element that contains `children`.
diff --git a/lib/esnext/components/forms/radioGroup.d.ts b/lib/esnext/components/forms/radioGroup.d.ts
index 2f21804fca8599d6d98a6be80473d03a52419196..e96697514e15a44aa614e4086503848614956210 100644
--- a/lib/esnext/components/forms/radioGroup.d.ts
+++ b/lib/esnext/components/forms/radioGroup.d.ts
@@ -4,6 +4,7 @@ import { OptionProps, Props } from "../../common/props";
export declare type RadioGroupProps = IRadioGroupProps;
/** @deprecated use RadioGroupProps */
export interface IRadioGroupProps extends Props {
+ children?: React.ReactNode;
/**
* Whether the group and _all_ its radios are disabled.
* Individual radios can be disabled using their `disabled` prop.
diff --git a/lib/esnext/components/popover/popover.d.ts b/lib/esnext/components/popover/popover.d.ts
index e1490579f16bbc57b9bd37e0d782a4c7c55196fa..f3cdf4a9fe3bd32e19bd9220662115930a94e03f 100644
--- a/lib/esnext/components/popover/popover.d.ts
+++ b/lib/esnext/components/popover/popover.d.ts
@@ -9,6 +9,7 @@ export declare const PopoverInteractionKind: {
};
export declare type PopoverInteractionKind = typeof PopoverInteractionKind[keyof typeof PopoverInteractionKind];
export interface IPopoverProps extends IPopoverSharedProps {
+ children?: React.ReactNode;
/** HTML props for the backdrop element. Can be combined with `backdropClassName`. */
backdropProps?: React.HTMLProps<HTMLDivElement>;
/**
diff --git a/lib/esnext/components/portal/portal.d.ts b/lib/esnext/components/portal/portal.d.ts
index 9e3460b460c48dad99ef04c26261094bb5c4a5d3..5ffb40eeb79c6fc838d63b1261eafc964bd26b14 100644
--- a/lib/esnext/components/portal/portal.d.ts
+++ b/lib/esnext/components/portal/portal.d.ts
@@ -3,6 +3,7 @@ import { ValidationMap } from "../../common/context";
import { Props } from "../../common/props";
export declare type PortalProps = IPortalProps;
export interface IPortalProps extends Props {
+ children?: React.ReactNode;
/**
* Callback invoked when the children of this `Portal` have been added to the DOM.
*/
diff --git a/lib/esnext/components/tabs/tabs.d.ts b/lib/esnext/components/tabs/tabs.d.ts
index 2ca5fe5b4bed0871154292aa3248af1f2ea8331d..870a32ee7d2bd6c934216e268016b4b2d2e2b514 100644
--- a/lib/esnext/components/tabs/tabs.d.ts
+++ b/lib/esnext/components/tabs/tabs.d.ts
@@ -6,6 +6,7 @@ export declare const Expander: React.FunctionComponent;
export declare type TabsProps = ITabsProps;
/** @deprecated use TabsProps */
export interface ITabsProps extends Props {
+ children?: React.ReactNode;
/**
* Whether the selected tab indicator should animate its movement.
*
diff --git a/lib/esnext/components/toast/toaster.d.ts b/lib/esnext/components/toast/toaster.d.ts
index a6c712467c7ac81aed9ea00207aeaaeb14bd9337..353621182cb1c4d8c5ea6430504ab0ed38fec195 100644
--- a/lib/esnext/components/toast/toaster.d.ts
+++ b/lib/esnext/components/toast/toaster.d.ts
@@ -26,6 +26,7 @@ export interface IToaster {
* These props can be passed as an argument to the static `Toaster.create(props?, container?)` method.
*/
export interface IToasterProps extends Props {
+ children?: React.ReactNode;
/**
* Whether a toast should acquire application focus when it first opens.
* This is disabled by default so that toasts do not interrupt the user's flow.
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find all tooltip.d.ts files and check for ITooltipProps
echo "=== Finding tooltip.d.ts files ==="
find . -name "tooltip.d.ts" -type f 2>/dev/null | sort

echo -e "\n=== Checking ITooltipProps in each tooltip.d.ts ==="
find . -name "tooltip.d.ts" -type f 2>/dev/null | sort | while read file; do
  echo -e "\n$file:"
  grep -n "ITooltipProps" "$file" | head -5
done

Repository: RoamJS/tldraw

Length of output: 144


🏁 Script executed:

#!/bin/bash
# Check the patch file for tooltip entries
echo "=== Counting tooltip patches in the patch file ==="
grep -c "lib/.*/components/tooltip/tooltip.d.ts" patches/@blueprintjs__core@3.50.4.patch

echo -e "\n=== All tooltip patches found ==="
grep "lib/.*/components/tooltip/tooltip.d.ts" patches/@blueprintjs__core@3.50.4.patch

echo -e "\n=== Line count of patch file ==="
wc -l patches/@blueprintjs__core@3.50.4.patch

echo -e "\n=== Last 50 lines of patch file to check if it's truncated ==="
tail -50 patches/@blueprintjs__core@3.50.4.patch

Repository: RoamJS/tldraw

Length of output: 3438


🏁 Script executed:

#!/bin/bash
# Verify the specific claim about esnext tooltip being missing
echo "=== Searching for esnext tooltip in patch file ==="
grep "esnext.*tooltip" patches/@blueprintjs__core@3.50.4.patch

echo -e "\n=== Listing all esnext files patched ==="
grep "diff --git a/lib/esnext" patches/@blueprintjs__core@3.50.4.patch | sed 's/diff --git a\///' | sed 's/ .*//'

echo -e "\n=== Comparing component counts across build targets ==="
echo "cjs components:"
grep "diff --git a/lib/cjs/components" patches/@blueprintjs__core@3.50.4.patch | wc -l

echo "esm components:"
grep "diff --git a/lib/esm/components" patches/@blueprintjs__core@3.50.4.patch | wc -l

echo "esnext components:"
grep "diff --git a/lib/esnext/components" patches/@blueprintjs__core@3.50.4.patch | wc -l

Repository: RoamJS/tldraw

Length of output: 681


Missing esnext variant for tooltip component patch.

This patch correctly adds children?: React.ReactNode to multiple component interfaces for React 18 compatibility. However, lib/esnext/components/tooltip/tooltip.d.ts is missing from this patch.

The cjs and esm build targets include tooltip patches (11 components each), but the esnext variant only patches 10 components, excluding tooltip. Add the esnext tooltip patch to maintain consistency across all build targets:

diff --git a/lib/esnext/components/tooltip/tooltip.d.ts b/lib/esnext/components/tooltip/tooltip.d.ts
index 94f4af9427a35ad9333f2bfd30ab712499efcd57..4db48a9bc4cac14aacd90535f403f87b76445815 100644
--- a/lib/esnext/components/tooltip/tooltip.d.ts
+++ b/lib/esnext/components/tooltip/tooltip.d.ts
@@ -6,6 +6,7 @@ import { IPopoverSharedProps } from "../popover/popoverSharedProps";
 export declare type TooltipProps = ITooltipProps;
 /** `@deprecated` use TooltipProps */
 export interface ITooltipProps extends IPopoverSharedProps, IntentProps {
+    children?: React.ReactNode;
     /**
      * The content that will be displayed inside of the tooltip.
      */
🤖 Prompt for AI Agents
In `@patches/`@blueprintjs__core@3.50.4.patch around lines 1 - 384, Summary: The
esnext build is missing the tooltip type patch; the ITooltipProps interface in
the esnext tooltip declaration lacks children?: React.ReactNode. Fix: update the
esnext tooltip declaration (the ITooltipProps interface for TooltipProps /
ITooltipProps) to include children?: React.ReactNode exactly like the cjs/esm
variants, ensuring the ITooltipProps declaration for the tooltip component
mirrors the other build targets.

@mdroidian mdroidian closed this Feb 10, 2026
@mdroidian mdroidian deleted the init branch February 10, 2026 06:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant