From cffdb6d6fcc853f298cc1fe488a1244197ceef52 Mon Sep 17 00:00:00 2001 From: murashit Date: Thu, 28 May 2026 14:14:28 +0900 Subject: [PATCH] Downgrade React to 18 for Obsidian review --- package-lock.json | 63 +++++++++++-------- package.json | 8 +-- src/features/chat/ui/message-stream.tsx | 19 +++--- src/shared/ui/react-components.tsx | 4 +- .../features/chat/ui/message-stream.test.tsx | 6 +- 5 files changed, 58 insertions(+), 42 deletions(-) diff --git a/package-lock.json b/package-lock.json index 23b4d717..5b8b971d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,14 +9,14 @@ "version": "2.3.0", "license": "Apache-2.0", "dependencies": { - "react": "^19.2.6", - "react-dom": "^19.2.6" + "react": "^18.3.1", + "react-dom": "^18.3.1" }, "devDependencies": { "@eslint/js": "^9.39.4", "@types/node": "^25.6.0", - "@types/react": "^19.2.15", - "@types/react-dom": "^19.2.3", + "@types/react": "^18.3.29", + "@types/react-dom": "^18.3.7", "esbuild": "^0.28.0", "eslint": "^10.3.0", "eslint-config-prettier": "^10.1.8", @@ -2015,24 +2015,32 @@ "undici-types": ">=7.24.0 <7.24.7" } }, + "node_modules/@types/prop-types": { + "version": "15.7.15", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", + "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/react": { - "version": "19.2.15", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.15.tgz", - "integrity": "sha512-eRwcGNHve+E8qtEQSSRl6urh+rFop4v8gm6O8rGv25CodbvFdLjA1vVQ1KkiFE0w0UPOnb8tDiFKL5lp0rtY5Q==", + "version": "18.3.29", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.29.tgz", + "integrity": "sha512-ch0qJdr2JY0r04NXSprbK6TXOgnaJ1Tz23fm5W+z0/CBah6BSBc3n96h7K9GOtwh0HrilNWHIBzE1Ko4Dcw/Wg==", "dev": true, "license": "MIT", "dependencies": { + "@types/prop-types": "*", "csstype": "^3.2.2" } }, "node_modules/@types/react-dom": { - "version": "19.2.3", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", - "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "version": "18.3.7", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz", + "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", "dev": true, "license": "MIT", "peerDependencies": { - "@types/react": "^19.2.0" + "@types/react": "^18.0.0" } }, "node_modules/@types/tern": { @@ -5649,7 +5657,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true, "license": "MIT" }, "node_modules/js-yaml": { @@ -6183,7 +6190,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dev": true, "license": "MIT", "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" @@ -6908,24 +6914,28 @@ "license": "MIT" }, "node_modules/react": { - "version": "19.2.6", - "resolved": "https://registry.npmjs.org/react/-/react-19.2.6.tgz", - "integrity": "sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + }, "engines": { "node": ">=0.10.0" } }, "node_modules/react-dom": { - "version": "19.2.6", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.6.tgz", - "integrity": "sha512-0prMI+hvBbPjsWnxDLxlCGyM8PN6UuWjEUCYmZhO67xIV9Xasa/r/vDnq+Xyq4Lo27g8QSbO5YzARu0D1Sps3g==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "license": "MIT", "dependencies": { - "scheduler": "^0.27.0" + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" }, "peerDependencies": { - "react": "^19.2.6" + "react": "^18.3.1" } }, "node_modules/react-is": { @@ -7222,10 +7232,13 @@ } }, "node_modules/scheduler": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", - "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", - "license": "MIT" + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + } }, "node_modules/semver": { "version": "7.8.0", diff --git a/package.json b/package.json index 254e03cc..032a499c 100644 --- a/package.json +++ b/package.json @@ -35,8 +35,8 @@ "devDependencies": { "@eslint/js": "^9.39.4", "@types/node": "^25.6.0", - "@types/react": "^19.2.15", - "@types/react-dom": "^19.2.3", + "@types/react": "^18.3.29", + "@types/react-dom": "^18.3.7", "esbuild": "^0.28.0", "eslint": "^10.3.0", "eslint-config-prettier": "^10.1.8", @@ -52,7 +52,7 @@ "vitest": "^4.1.7" }, "dependencies": { - "react": "^19.2.6", - "react-dom": "^19.2.6" + "react": "^18.3.1", + "react-dom": "^18.3.1" } } diff --git a/src/features/chat/ui/message-stream.tsx b/src/features/chat/ui/message-stream.tsx index 24eba8f3..3bce78ea 100644 --- a/src/features/chat/ui/message-stream.tsx +++ b/src/features/chat/ui/message-stream.tsx @@ -1,4 +1,4 @@ -import { Fragment, useLayoutEffect, useRef, useState, type Ref, type ReactNode } from "react"; +import { Fragment, forwardRef, useLayoutEffect, useRef, useState, type ReactNode } from "react"; import { displayBlocksForItems } from "../display/blocks"; import { executionState } from "../display/state"; @@ -323,17 +323,16 @@ function CollapsibleMessageContent({ item, context }: { item: RenderableMessageI ); } -function MarkdownContent({ - item, - context, - collapsed = false, - ref, -}: { +interface MarkdownContentProps { item: RenderableMessageItem; context: MessageStreamContext; collapsed?: boolean; - ref?: Ref; -}): ReactNode { +} + +const MarkdownContent = forwardRef(function MarkdownContent( + { item, context, collapsed = false }, + ref, +): ReactNode { const localRef = useRef(null); const contextRef = useRef(context); useLayoutEffect(() => { @@ -369,7 +368,7 @@ function MarkdownContent({ .join(" ")} /> ); -} +}); function ReferencedThread({ item }: { item: Extract }): ReactNode { const reference = item.referencedThread; diff --git a/src/shared/ui/react-components.tsx b/src/shared/ui/react-components.tsx index f97f452e..d4489179 100644 --- a/src/shared/ui/react-components.tsx +++ b/src/shared/ui/react-components.tsx @@ -1,5 +1,5 @@ import { setIcon } from "obsidian"; -import { useLayoutEffect, useRef, type ButtonHTMLAttributes, type ReactNode, type Ref } from "react"; +import { useLayoutEffect, useRef, type ButtonHTMLAttributes, type MutableRefObject, type ReactNode, type Ref } from "react"; export interface ObsidianIconProps { icon: string; @@ -39,7 +39,7 @@ export function IconButton({ icon, label, buttonRef, className, children, ...pro if (typeof buttonRef === "function") { buttonRef(element); } else if (buttonRef) { - buttonRef.current = element; + (buttonRef as MutableRefObject).current = element; } }} className={className} diff --git a/tests/features/chat/ui/message-stream.test.tsx b/tests/features/chat/ui/message-stream.test.tsx index 3e9ac405..9b8afbc5 100644 --- a/tests/features/chat/ui/message-stream.test.tsx +++ b/tests/features/chat/ui/message-stream.test.tsx @@ -12,6 +12,8 @@ import { messageStreamBlocks as rawMessageStreamBlocks, renderMessageStreamBlock import { changeInputValue, installObsidianDomShims, topLevelDetailsSummaries } from "./dom-test-helpers"; import { renderReactRoot, unmountReactRoot } from "../../../../src/shared/ui/react-root"; +(globalThis as typeof globalThis & { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true; + installObsidianDomShims(); function messageStreamBlocks( @@ -46,7 +48,9 @@ function testMessageStreamBlock(key: string, node: ReactNode): ReturnType[number]): HTMLElement { const parent = document.createElement("div"); - renderMessageStreamBlocks(parent, [block]); + act(() => { + renderMessageStreamBlocks(parent, [block]); + }); const host = expectPresent(parent.querySelector(`[data-codex-panel-block-key="${block.key}"]`)); return expectPresent(host.firstElementChild as HTMLElement | null); }