{ "issue": { "url": "https://api.github.com/repos/laurent22/joplin/issues/14669", "repository_url": "https://api.github.com/repos/laurent22/joplin", "labels_url": "https://api.github.com/repos/laurent22/joplin/issues/14669/labels{/name}", "comments_url": "https://api.github.com/repos/laurent22/joplin/issues/14669/comments", "events_url": "https://api.github.com/repos/laurent22/joplin/issues/14669/events", "html_url": "https://github.com/laurent22/joplin/pull/14669", "id": 4049372770, "node_id": "PR_kwDOBLftOs7JQ-qD", "number": 14669, "title": "Desktop: Resolves #14143: Fixed scrolling behaviour in long lines for TinyMCE and CodeMirror", "user": { "login": "Veivel", "id": 25278964, "node_id": "MDQ6VXNlcjI1Mjc4OTY0", "avatar_url": "https://avatars.githubusercontent.com/u/25278964?v=4", "gravatar_id": "", "url": "https://api.github.com/users/Veivel", "html_url": "https://github.com/Veivel", "followers_url": "https://api.github.com/users/Veivel/followers", "following_url": "https://api.github.com/users/Veivel/following{/other_user}", "gists_url": "https://api.github.com/users/Veivel/gists{/gist_id}", "starred_url": "https://api.github.com/users/Veivel/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/Veivel/subscriptions", "organizations_url": "https://api.github.com/users/Veivel/orgs", "repos_url": "https://api.github.com/users/Veivel/repos", "events_url": "https://api.github.com/users/Veivel/events{/privacy}", "received_events_url": "https://api.github.com/users/Veivel/received_events", "type": "User", "user_view_type": "public", "site_admin": false }, "labels": [ { "id": 519700741, "node_id": "MDU6TGFiZWw1MTk3MDA3NDE=", "url": "https://api.github.com/repos/laurent22/joplin/labels/bug", "name": "bug", "color": "cf353b", "default": true, "description": "It's a bug" }, { "id": 1080341216, "node_id": "MDU6TGFiZWwxMDgwMzQxMjE2", "url": "https://api.github.com/repos/laurent22/joplin/labels/desktop", "name": "desktop", "color": "ffc274", "default": false, "description": "All desktop platforms" }, { "id": 1366672371, "node_id": "MDU6TGFiZWwxMzY2NjcyMzcx", "url": "https://api.github.com/repos/laurent22/joplin/labels/editor", "name": "editor", "color": "91ca55", "default": false, "description": "" }, { "id": 5991245949, "node_id": "LA_kwDOBLftOs8AAAABZRsofQ", "url": "https://api.github.com/repos/laurent22/joplin/labels/markdown-editor", "name": "markdown-editor", "color": "91ca55", "default": false, "description": "About the Markdown editor" } ], "state": "closed", "locked": false, "assignees": [], "milestone": null, "comments": 3, "created_at": "2026-03-10T04:47:37Z", "updated_at": "2026-03-10T15:12:25Z", "closed_at": "2026-03-10T12:26:04Z", "assignee": null, "author_association": "CONTRIBUTOR", "active_lock_reason": null, "draft": false, "pull_request": { "url": "https://api.github.com/repos/laurent22/joplin/pulls/14669", "html_url": "https://github.com/laurent22/joplin/pull/14669", "diff_url": "https://github.com/laurent22/joplin/pull/14669.diff", "patch_url": "https://github.com/laurent22/joplin/pull/14669.patch", "merged_at": "2026-03-10T12:26:04Z" }, "body": "## Problem\r\nIn #14143, there are two different scenarios where making changes inside very long lines will cause the scroll position to jump or move incorrectly:\r\n1. Problem 1 (additional bug, reported in a [comment](https://github.com/laurent22/joplin/issues/14143#issuecomment-3767793559)): In the Markdown Editor, making any changes to the very long line causes the scroll position in both the Editor and the Viewer to jump to the bottom of the document\r\n2. Problem 2 (main bug, reported in original Issue post): In the Rich Text Editor, undoing/redoing any change to the very long line causes the scroll position to jump to the start of the line.\r\n\r\nThe expected behaviour on both Problem 1 & 2 is that the scroll position jumps to where the change occurred, so that it is visible inside the window to the user.\r\n\r\n## Solution\r\n1. Problem 1: Implement an `editor.on()` handler that overrides TinyMCE's handling of the `ScrollIntoView` event\r\n2. Problem 2: Modify the `translateLE_()` calculation to prevent cases where `ePercentL === ePercentU`.\r\n\r\n## Details\r\n1. Problem 1 occurs because TinyMCE handles `ScrollIntoView` by creating a temporary \"marker\" element at the start of the line that it uses as a destination to scroll to. Unfortunately this marker is always created at the start of the line, even if the `ScrollIntoView` event was fired because of a change at the bottom of the line. Hence, the solution simply overrides this behaviour.\r\n2. Problem 2 occurs because of an edge case inside `translateLE_()` where `ePercentL === ePercentU`.\r\n\t- In `l2e` translation, the edge case above causes the following value `result = ePercentU + (ePercentL - ePercentU) * linInterp` to return a value equivalent to `ePercentU` (which is the start of the line). This is the case documented for Problem 2 in the Issue.\r\n\t- In `e2l` translatiion, the following value (which I'll call raw linInterp) `(percent - ePercentU) / (ePercentL - ePercentU)` resolves to Infinity because it is divided by 0. However it is bounded by `Math.min(1,…)` which resolves to 1. As a result, it can also scroll to the bottom of the document.\r\n\t- Hence, the solution does two things:\r\n\t\t1. 2A: Checks if rawLinInterp is Infinity. If true, use `percent`.\r\n\t\t2. 2B: fixes `cm.heightAtLine()` to return \"extra\" height for lines beyond the document:\r\n\t\t```js\r\n\t\tconst ePercentU = codeMirror.heightAtLine(lineU, 'local') / height;\r\n\t\tconst ePercentL = codeMirror.heightAtLine(lineU + 1, 'local') / height;\r\n\t\t// existing behaviour: when lineU is the last line, height for lineU + 1 returned the same value as height for lineU.\r\n\t\t// with the fix: it should now return a larger value\r\n## Manual Tests\r\nI repeat these tests with two files: The initial [very long line text sample](https://github.com/laurent22/joplin/issues/14143#issuecomment-3766215253) from the Issue, and this [markdown sample file](https://gist.github.com/rt2zz/e0a1d6ab2682d2c47746950b84c0b6ee).\r\n1. Erase word then Undo, with word inside the window\r\n 1. Select a word\r\n 2. Press backspace\r\n 3. Ensure scroll position does not move\r\n 4. Undo\r\n 5. Ensure scroll position does not move\r\n2. Erase word then Undo, with word outside window\r\n 1. Select a word\r\n 2. Scroll until the word is not visible & well outside the window\r\n 3. Press backspace\r\n 4. Ensure scroll position jumps where the erased word was\r\n 5. Scroll again until word is well outside the window\r\n 6. Undo\r\n 7. Ensure scroll position jumps where the erased word is\r\n3. Enter text then Undo , with word inside the window\r\n 1. Type in text\r\n 2. Ensure scroll position does not move\r\n 3. Undo\r\n 4. Ensure scroll position does not move\r\n4. Enter text then Undo, with word outside the window\r\n 1. Scroll until cursor is not visible & well outside the window\r\n 2. Type in text\r\n 3. Ensure scroll position jumps where the new text is\r\n 4. Undo\r\n 5. Ensure scroll position jumps to where the new text was\r\n \r\n## Demo\r\n\r\nhttps://github.com/user-attachments/assets/a14a1b3d-e243-4687-b909-c913b9830f9d\r\n", "closed_by": { "login": "laurent22", "id": 1285584, "node_id": "MDQ6VXNlcjEyODU1ODQ=", "avatar_url": "https://avatars.githubusercontent.com/u/1285584?v=4", "gravatar_id": "", "url": "https://api.github.com/users/laurent22", "html_url": "https://github.com/laurent22", "followers_url": "https://api.github.com/users/laurent22/followers", "following_url": "https://api.github.com/users/laurent22/following{/other_user}", "gists_url": "https://api.github.com/users/laurent22/gists{/gist_id}", "starred_url": "https://api.github.com/users/laurent22/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/laurent22/subscriptions", "organizations_url": "https://api.github.com/users/laurent22/orgs", "repos_url": "https://api.github.com/users/laurent22/repos", "events_url": "https://api.github.com/users/laurent22/events{/privacy}", "received_events_url": "https://api.github.com/users/laurent22/received_events", "type": "User", "user_view_type": "public", "site_admin": false }, "reactions": { "url": "https://api.github.com/repos/laurent22/joplin/issues/14669/reactions", "total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0 }, "timeline_url": "https://api.github.com/repos/laurent22/joplin/issues/14669/timeline", "performed_via_github_app": null, "state_reason": null }, "comments": [ { "url": "https://api.github.com/repos/laurent22/joplin/issues/comments/4028645878", "html_url": "https://github.com/laurent22/joplin/pull/14669#issuecomment-4028645878", "issue_url": "https://api.github.com/repos/laurent22/joplin/issues/14669", "id": 4028645878, "node_id": "IC_kwDOBLftOs7wIEH2", "user": { "login": "github-actions[bot]", "id": 41898282, "node_id": "MDM6Qm90NDE4OTgyODI=", "avatar_url": "https://avatars.githubusercontent.com/in/15368?v=4", "gravatar_id": "", "url": "https://api.github.com/users/github-actions%5Bbot%5D", "html_url": "https://github.com/apps/github-actions", "followers_url": "https://api.github.com/users/github-actions%5Bbot%5D/followers", "following_url": "https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}", "gists_url": "https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}", "starred_url": "https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/github-actions%5Bbot%5D/subscriptions", "organizations_url": "https://api.github.com/users/github-actions%5Bbot%5D/orgs", "repos_url": "https://api.github.com/users/github-actions%5Bbot%5D/repos", "events_url": "https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}", "received_events_url": "https://api.github.com/users/github-actions%5Bbot%5D/received_events", "type": "Bot", "user_view_type": "public", "site_admin": false }, "created_at": "2026-03-10T04:47:49Z", "updated_at": "2026-03-10T15:12:25Z", "body": "****CLA Assistant Lite bot**** All contributors have signed the CLA ✍️ ✅", "author_association": "CONTRIBUTOR", "reactions": { "url": "https://api.github.com/repos/laurent22/joplin/issues/comments/4028645878/reactions", "total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0 }, "performed_via_github_app": { "id": 15368, "client_id": "Iv1.05c79e9ad1f6bdfa", "slug": "github-actions", "node_id": "MDM6QXBwMTUzNjg=", "owner": { "login": "github", "id": 9919, "node_id": "MDEyOk9yZ2FuaXphdGlvbjk5MTk=", "avatar_url": "https://avatars.githubusercontent.com/u/9919?v=4", "gravatar_id": "", "url": "https://api.github.com/users/github", "html_url": "https://github.com/github", "followers_url": "https://api.github.com/users/github/followers", "following_url": "https://api.github.com/users/github/following{/other_user}", "gists_url": "https://api.github.com/users/github/gists{/gist_id}", "starred_url": "https://api.github.com/users/github/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/github/subscriptions", "organizations_url": "https://api.github.com/users/github/orgs", "repos_url": "https://api.github.com/users/github/repos", "events_url": "https://api.github.com/users/github/events{/privacy}", "received_events_url": "https://api.github.com/users/github/received_events", "type": "Organization", "user_view_type": "public", "site_admin": false }, "name": "GitHub Actions", "description": "Automate your workflow from idea to production", "external_url": "https://help.github.com/en/actions", "html_url": "https://github.com/apps/github-actions", "created_at": "2018-07-30T09:30:17Z", "updated_at": "2026-05-05T14:51:38Z", "permissions": { "actions": "write", "administration": "read", "artifact_metadata": "write", "attestations": "write", "checks": "write", "code_quality": "write", "contents": "write", "copilot_requests": "write", "deployments": "write", "discussions": "write", "issues": "write", "merge_queues": "write", "metadata": "read", "models": "read", "packages": "write", "pages": "write", "pull_requests": "write", "repository_hooks": "write", "repository_projects": "write", "security_events": "write", "statuses": "write", "vulnerability_alerts": "read" }, "events": [ "branch_protection_rule", "check_run", "check_suite", "create", "delete", "deployment", "deployment_status", "discussion", "discussion_comment", "fork", "gollum", "issues", "issue_comment", "label", "merge_group", "milestone", "page_build", "public", "pull_request", "pull_request_review", "pull_request_review_comment", "push", "registry_package", "release", "repository", "repository_dispatch", "status", "watch", "workflow_dispatch", "workflow_run" ] } }, { "url": "https://api.github.com/repos/laurent22/joplin/issues/comments/4028647019", "html_url": "https://github.com/laurent22/joplin/pull/14669#issuecomment-4028647019", "issue_url": "https://api.github.com/repos/laurent22/joplin/issues/14669", "id": 4028647019, "node_id": "IC_kwDOBLftOs7wIEZr", "user": { "login": "coderabbitai[bot]", "id": 136622811, "node_id": "BOT_kgDOCCSy2w", "avatar_url": "https://avatars.githubusercontent.com/in/347564?v=4", "gravatar_id": "", "url": "https://api.github.com/users/coderabbitai%5Bbot%5D", "html_url": "https://github.com/apps/coderabbitai", "followers_url": "https://api.github.com/users/coderabbitai%5Bbot%5D/followers", "following_url": "https://api.github.com/users/coderabbitai%5Bbot%5D/following{/other_user}", "gists_url": "https://api.github.com/users/coderabbitai%5Bbot%5D/gists{/gist_id}", "starred_url": "https://api.github.com/users/coderabbitai%5Bbot%5D/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/coderabbitai%5Bbot%5D/subscriptions", "organizations_url": "https://api.github.com/users/coderabbitai%5Bbot%5D/orgs", "repos_url": "https://api.github.com/users/coderabbitai%5Bbot%5D/repos", "events_url": "https://api.github.com/users/coderabbitai%5Bbot%5D/events{/privacy}", "received_events_url": "https://api.github.com/users/coderabbitai%5Bbot%5D/received_events", "type": "Bot", "user_view_type": "public", "site_admin": false }, "created_at": "2026-03-10T04:48:09Z", "updated_at": "2026-03-10T05:04:03Z", "body": "\n\n\n
\n📝 Walkthrough\n\n## Walkthrough\n\nThese changes fix edge cases in scroll and height calculation logic across the CodeMirror and TinyMCE editors. The updates prevent division-by-zero errors in interpolation calculations and refine how cursor position is scrolled into view when text selection changes.\n\n## Changes\n\n|Cohort / File(s)|Summary|\n|---|---|\n|**Scroll Handler Interpolation**
`packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useScrollHandler.ts`|Guards against division by zero in linear interpolation by computing raw value first and conditionally clamping, avoiding jumps when denominator equals zero.|\n|**Height Calculation for Out-of-Range Lines**
`packages/editor/CodeMirror/CodeMirror5Emulation/CodeMirror5Emulation.ts`, `packages/editor/CodeMirror/CodeMirror5Emulation/CodeMirror5Emulation.test.ts`|Modified heightAtLine to include lineBlock.height for lines beyond document bounds, preventing infinity in interpolation; adds comprehensive test coverage for height computation edge cases.|\n|**ScrollIntoView Event Override**
`packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/TinyMCE.tsx`|Introduces custom ScrollIntoView handler that scrolls to cursor's character position using selection bounding rect instead of paragraph start, with viewport comparison and edge case handling.|\n\n## Suggested labels\n\n`bug`, `desktop`, `editor`, `markdown-editor`\n\n
\n\n\n\n\n\n\n
\n🚥 Pre-merge checks | ✅ 4\n\n
\n✅ Passed checks (4 passed)\n\n| Check name | Status | Explanation |\n| :-----------------------------------: | :------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| Title check | ✅ Passed | The PR title clearly and specifically identifies the main changes: fixing scrolling behaviour in long lines for both TinyMCE and CodeMirror, and references the resolved issue `#14143`. |\n| Description check | ✅ Passed | The PR description comprehensively explains the two problems addressed, their root causes, the implemented solutions, technical details, manual test cases, and includes a demo. It is thoroughly related to all changeset modifications. |\n| Docstring Coverage | ✅ Passed | No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. |\n| Pr Description Must Follow Guidelines | ✅ Passed | The PR description includes clear problem/user-impact description, high-level solution explanation, and test plan with manual tests and demo assets. |\n\n
\n\n✏️ Tip: You can configure your own custom pre-merge checks in the settings.\n\n
\n\n\n\n\n\n
\n✨ Finishing Touches\n\n
\n🧪 Generate unit tests (beta)\n\n- [ ] Create PR with unit tests\n- [ ] Post copyable unit tests in a comment\n\n
\n\n
\n\n\n\n\n\n---\n\nThanks for using [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=laurent22/joplin&utm_content=14669)! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.\n\n
\n❤️ Share\n\n- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)\n- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)\n- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)\n- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)\n\n
\n\nComment `@coderabbitai help` to get the list of available commands and usage tips.\n\n\n\n\n\n\n\n\n", "author_association": "CONTRIBUTOR", "reactions": { "url": "https://api.github.com/repos/laurent22/joplin/issues/comments/4028647019/reactions", "total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0 }, "performed_via_github_app": { "id": 347564, "client_id": "Iv1.6aaafe4fe882736b", "slug": "coderabbitai", "node_id": "A_kwHOB96YWc4ABU2s", "owner": { "login": "coderabbitai", "id": 132028505, "node_id": "O_kgDOB96YWQ", "avatar_url": "https://avatars.githubusercontent.com/u/132028505?v=4", "gravatar_id": "", "url": "https://api.github.com/users/coderabbitai", "html_url": "https://github.com/coderabbitai", "followers_url": "https://api.github.com/users/coderabbitai/followers", "following_url": "https://api.github.com/users/coderabbitai/following{/other_user}", "gists_url": "https://api.github.com/users/coderabbitai/gists{/gist_id}", "starred_url": "https://api.github.com/users/coderabbitai/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/coderabbitai/subscriptions", "organizations_url": "https://api.github.com/users/coderabbitai/orgs", "repos_url": "https://api.github.com/users/coderabbitai/repos", "events_url": "https://api.github.com/users/coderabbitai/events{/privacy}", "received_events_url": "https://api.github.com/users/coderabbitai/received_events", "type": "Organization", "user_view_type": "public", "site_admin": false }, "name": "coderabbitai", "description": "# Transforming Code Reviews with AI\r\n\r\n## Features\r\n\r\n**Automated Reviews**: Continuous reviews of the pull requests including incremental commits. \r\n\r\n**Summarization**: Generates high-level summary and a technical walkthrough of the PR changes. \r\n\r\n**Line-by-line review**: Provides line-by-line suggestions committable with one click.\r\n\r\n**Codebase verification**: Verifies the impact on the overall codebase and identifies missing changes.\r\n\r\n**Insights into your code**: Ask any questions on your codebase within the pull request \r\n\r\n**Chat about your code** : Chat with the bot around your code. The more you chat, the smarter it gets.\r\n\r\n**Issue Validation**: Validates the PR against the linked issues and identifies other related issues \r\n\r\n\r\n\r\n", "external_url": "https://coderabbit.ai?utm_source=cr_app&utm_medium=github", "html_url": "https://github.com/apps/coderabbitai", "created_at": "2023-06-14T15:47:27Z", "updated_at": "2026-03-14T07:58:44Z", "permissions": { "actions": "read", "checks": "write", "contents": "write", "discussions": "read", "issues": "write", "members": "read", "metadata": "read", "pull_requests": "write", "statuses": "write" }, "events": [ "issues", "issue_comment", "label", "organization", "pull_request", "pull_request_review", "pull_request_review_comment", "pull_request_review_thread", "release", "repository" ] } }, { "url": "https://api.github.com/repos/laurent22/joplin/issues/comments/4028649497", "html_url": "https://github.com/laurent22/joplin/pull/14669#issuecomment-4028649497", "issue_url": "https://api.github.com/repos/laurent22/joplin/issues/14669", "id": 4028649497, "node_id": "IC_kwDOBLftOs7wIFAZ", "user": { "login": "Veivel", "id": 25278964, "node_id": "MDQ6VXNlcjI1Mjc4OTY0", "avatar_url": "https://avatars.githubusercontent.com/u/25278964?v=4", "gravatar_id": "", "url": "https://api.github.com/users/Veivel", "html_url": "https://github.com/Veivel", "followers_url": "https://api.github.com/users/Veivel/followers", "following_url": "https://api.github.com/users/Veivel/following{/other_user}", "gists_url": "https://api.github.com/users/Veivel/gists{/gist_id}", "starred_url": "https://api.github.com/users/Veivel/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/Veivel/subscriptions", "organizations_url": "https://api.github.com/users/Veivel/orgs", "repos_url": "https://api.github.com/users/Veivel/repos", "events_url": "https://api.github.com/users/Veivel/events{/privacy}", "received_events_url": "https://api.github.com/users/Veivel/received_events", "type": "User", "user_view_type": "public", "site_admin": false }, "created_at": "2026-03-10T04:48:55Z", "updated_at": "2026-03-10T04:48:55Z", "body": "I have read the CLA Document and I hereby sign the CLA", "author_association": "CONTRIBUTOR", "reactions": { "url": "https://api.github.com/repos/laurent22/joplin/issues/comments/4028649497/reactions", "total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0 }, "performed_via_github_app": null } ] }