From 99ec2cf3959434f74b4fa68aa7013fb781a40439 Mon Sep 17 00:00:00 2001 From: "Bryan A. Jones" Date: Tue, 19 May 2026 16:42:55 +0500 Subject: [PATCH 1/9] Fix: Revert max value for selection back to correct previous value. --- client/src/CodeChatEditor.mts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/client/src/CodeChatEditor.mts b/client/src/CodeChatEditor.mts index 672b8b6c..f9d2995c 100644 --- a/client/src/CodeChatEditor.mts +++ b/client/src/CodeChatEditor.mts @@ -526,10 +526,12 @@ export const restoreSelection = ({ } selection_node = new_selection_node; } - // In case of edits, avoid an offset past the end of the node. + // In case of edits, avoid an offset past the end of the node. Note that + // the maximum value is `length`, not `length - 1`, which represents a + // selection at the very end of the text node. const final_selection_offset = Math.min( selection_offset, - (selection_node.nodeValue?.length ?? 1) - 1, + selection_node.nodeValue?.length ?? 0, ); // Use that to set the selection. tinymce.activeEditor!.selection.setCursorLocation( From dd2f53ea747e2a088556d98114b8e781a3dcbdb2 Mon Sep 17 00:00:00 2001 From: "Bryan A. Jones" Date: Thu, 21 May 2026 14:25:52 +0500 Subject: [PATCH 2/9] Fix: run minify on each doc block, producing more accurate results than running it on all HTML including doc block separators. --- server/src/ide/vscode/tests.rs | 37 +++++++---------------- server/src/processing.rs | 3 +- server/src/processing/tests.rs | 55 ++++++++++++++++++++++++++-------- server/tests/overall_1.rs | 4 +-- 4 files changed, 55 insertions(+), 44 deletions(-) diff --git a/server/src/ide/vscode/tests.rs b/server/src/ide/vscode/tests.rs index 509cff9e..87287db8 100644 --- a/server/src/ide/vscode/tests.rs +++ b/server/src/ide/vscode/tests.rs @@ -47,12 +47,9 @@ use tokio_tungstenite::{ tungstenite::{http::StatusCode, protocol::Message}, }; -use crate::{ - processing::CodeMirrorDocBlockUpdate, - webserver::{ - EditorMessage, EditorMessageContents, INITIAL_CLIENT_MESSAGE_ID, INITIAL_IDE_MESSAGE_ID, - INITIAL_MESSAGE_ID, IdeType, MESSAGE_ID_INCREMENT, ResultErrTypes, - }, +use crate::webserver::{ + EditorMessage, EditorMessageContents, INITIAL_CLIENT_MESSAGE_ID, INITIAL_IDE_MESSAGE_ID, + INITIAL_MESSAGE_ID, IdeType, MESSAGE_ID_INCREMENT, ResultErrTypes, }; use crate::{ processing::{ @@ -767,27 +764,13 @@ async fn test_vscode_ide_websocket7() { to: None, insert: "code\n\n".to_string() }], - doc_blocks: vec![ - CodeMirrorDocBlockTransaction::Update(CodeMirrorDocBlockUpdate { - from: 0, - from_new: None, - to: None, - indent: None, - delimiter: None, - contents: vec![StringDiff { - from: 0, - to: Some(7,), - insert: "

more

".to_string(), - },], - }), - CodeMirrorDocBlockTransaction::Add(CodeMirrorDocBlock { - from: 6, - to: 7, - indent: "".to_string(), - delimiter: "#".to_string(), - contents: "

most".to_string(), - },), - ], + doc_blocks: vec![CodeMirrorDocBlockTransaction::Add(CodeMirrorDocBlock { + from: 6, + to: 7, + indent: "".to_string(), + delimiter: "#".to_string(), + contents: "

most".to_string(), + },),], version: 0.0, }), version: 1.0, diff --git a/server/src/processing.rs b/server/src/processing.rs index 858b3fe3..2103523e 100644 --- a/server/src/processing.rs +++ b/server/src/processing.rs @@ -934,7 +934,6 @@ pub fn source_to_codechat_for_web( // 3. Hydrate the cleaned HTML. let html = hydrate_html(&html) .map_err(|e| SourceToCodeChatForWebError::ParseFailed(e.to_string()))?; - let html = minify(&html)?; // 4. Split on the separator. let mut doc_block_contents_iter = html.split(DOC_BLOCK_SEPARATOR_SPLIT_STRING); // @@ -959,7 +958,7 @@ pub fn source_to_codechat_for_web( delimiter: doc_block.delimiter.to_string(), // Used the markdown-translated replacement for this // doc block, rather than the original string. - contents: doc_block_contents_iter.next().unwrap().to_string(), + contents: minify(doc_block_contents_iter.next().unwrap())?, }); // Append newlines to the document; the doc block will // replace these in the editor. This keeps the line diff --git a/server/src/processing/tests.rs b/server/src/processing/tests.rs index 70245d23..d59e5212 100644 --- a/server/src/processing/tests.rs +++ b/server/src/processing/tests.rs @@ -579,7 +579,7 @@ fn test_source_to_codechat_for_web_1() { "javascript", "\nlet a = 1;\n\n", vec![ - build_codemirror_doc_block(0, 1, "", "//", "

Link

"), + build_codemirror_doc_block(0, 1, "", "//", "

Link"), build_codemirror_doc_block(12, 13, "", "/*", "") ] ))) @@ -587,9 +587,9 @@ fn test_source_to_codechat_for_web_1() { // Trigger special cases: // - // * An empty doc block at the beginning of the file. - // * A doc block in the middle of the file - // * A doc block with no trailing newline at the end of the file. + // * An empty doc block at the beginning of the file. + // * A doc block in the middle of the file + // * A doc block with no trailing newline at the end of the file. assert_eq!( source_to_codechat_for_web("//\n\n//\n\n//", &"cpp".to_string(), 0.0, false, false), Ok(TranslationResults::CodeChat(build_codechat_for_web( @@ -623,8 +623,8 @@ fn test_source_to_codechat_for_web_1() { // char: --Οƒ-- ---πŸ˜„--- ----------πŸ‘‰πŸΏ--------- --------------πŸ‘¨β€πŸ‘¦-------------- -----------πŸ‡ΊπŸ‡³---------- // ``` // - // These are taken from the [MDN UTF-16 - // docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String#utf-16_characters_unicode_code_points_and_grapheme_clusters). + // These are taken from the + // [MDN UTF-16 docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String#utf-16_characters_unicode_code_points_and_grapheme_clusters). assert_eq!( source_to_codechat_for_web("; // ΟƒπŸ˜„πŸ‘‰πŸΏπŸ‘¨β€πŸ‘¦πŸ‡ΊπŸ‡³\n//", &"cpp".to_string(), 0.0, false, false), Ok(TranslationResults::CodeChat(build_codechat_for_web( @@ -654,8 +654,8 @@ fn test_source_to_codechat_for_web_1() { ))) ); - // Test a fenced code block that's unterminated. See [fence - // mending](#fence-mending). + // Test a fenced code block that's unterminated. See + // [fence mending](#fence-mending). assert_eq!( source_to_codechat_for_web( "/* ``` foo\n*/\n// Test", @@ -724,7 +724,7 @@ fn test_source_to_codechat_for_web_1() { "\n\n", vec![ build_codemirror_doc_block(0, 1, "", "//", " "), - build_codemirror_doc_block(1, 2, " ", "//", "

Test

"), + build_codemirror_doc_block(1, 2, " ", "//", "

Test"), ] ))) ); @@ -744,7 +744,35 @@ fn test_source_to_codechat_for_web_1() { "\n\n", vec![ build_codemirror_doc_block(0, 1, "", "//", "

\n"),
-                build_codemirror_doc_block(1, 2, " ", "//", "\n

Test

\n
"), + build_codemirror_doc_block(1, 2, " ", "//", "

Test"), + ] + ))) + ); + + // Test that minify functions correctly across multiple paragraphs separated + // by a code block. + assert_eq!( + source_to_codechat_for_web( + indoc!( + " + // One + // + // Two + three(); + // Four + " + ), + &"cpp".to_string(), + 0.0, + false, + false + ), + Ok(TranslationResults::CodeChat(build_codechat_for_web( + "cpp", + "\n\n\nthree();\n\n", + vec![ + build_codemirror_doc_block(0, 3, "", "//", "

One

Two"), + build_codemirror_doc_block(12, 13, "", "//", "

Four"), ] ))) ); @@ -912,8 +940,8 @@ fn test_diff_1() { // char: ---πŸ˜„--- \n ----------πŸ‘‰πŸΏ--------- --------------πŸ‘¨β€πŸ‘¦-------------- -----------πŸ‡ΊπŸ‡³---------- \n β‘€ β‘₯ // ``` // - // These are taken from the [MDN UTF-16 - // docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String#utf-16_characters_unicode_code_points_and_grapheme_clusters). + // These are taken from the + // [MDN UTF-16 docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String#utf-16_characters_unicode_code_points_and_grapheme_clusters). "πŸ˜„\nπŸ‘‰πŸΏπŸ‘¨β€πŸ‘¦πŸ‡ΊπŸ‡³\nβ‘€β‘₯", "πŸ˜„\n❷❸\nβ‘€β‘₯", &[StringDiff { @@ -1254,7 +1282,8 @@ fn test_doc_block_html_to_markdown_1() { #[test] fn test_hydrate_html_1() { - // These tests check the translation from Markdown to "wet" HTML (what the user provides) instead of dry -> wet HTML. + // These tests check the translation from Markdown to "wet" HTML (what the + // user provides) instead of dry -> wet HTML. assert_eq!( hydrate_html(&markdown_to_html(indoc!( "```mermaid diff --git a/server/tests/overall_1.rs b/server/tests/overall_1.rs index 61490f7c..23e64d10 100644 --- a/server/tests/overall_1.rs +++ b/server/tests/overall_1.rs @@ -161,7 +161,7 @@ async fn test_server_core( doc: vec![StringDiff { from: 0, to: Some(7), - insert: "# Tesfoot\n".to_string() + insert: "# Testfoo\n".to_string() }], doc_blocks: vec![], version, @@ -197,7 +197,7 @@ async fn test_server_core( doc: vec![StringDiff { from: 0, to: Some(10), - insert: " # Tesfoot\n".to_string(), + insert: " # Testfoo\n".to_string(), }], doc_blocks: vec![], version, From 8a873c6b7ea951d2e79167f9d5637779ae96145c Mon Sep 17 00:00:00 2001 From: "Bryan A. Jones" Date: Thu, 21 May 2026 21:55:57 +0500 Subject: [PATCH 3/9] Fix: correct scrolling, especially horizaontal scrollbars. --- client/src/css/CodeChatEditor.css | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/client/src/css/CodeChatEditor.css b/client/src/css/CodeChatEditor.css index 10e6b7b2..08ae2b12 100644 --- a/client/src/css/CodeChatEditor.css +++ b/client/src/css/CodeChatEditor.css @@ -39,6 +39,7 @@ :root { --top-height: 3.7rem; --body-padding: 0.2rem; + --body-height: calc(100vh - var(--top-height) - 2 * var(--body-padding)); } /* See [box sizing](https://css-tricks.com/box-sizing/) for the following @@ -67,11 +68,24 @@ body { /* The rest of the screen is the editor area. Omit this for printing, so the text flows across multiple pages. */ @media not print { - #CodeChat-body { - height: calc(100vh - var(--top-height) - 2 * var(--body-padding)); - overflow: auto; + /* ### Height and scrollbars + + When in document-only mode, the editor is a child of the body. */ + #CodeChat-body > .CodeChat-doc-contents { + height: var(--body-height); + overflow: auto + } + + /* Otherwise, set this per the + [CodeMirror docs](https://codemirror.net/examples/styling/). */ + .cm-editor { + height: var(--body-height); + } + .cm-scroller { + overflow: auto } } + /* Misc styling ------------ @@ -131,7 +145,7 @@ body { /* Used to hide the TinyMCE editor instance when it's not in use. */ .CodeChat-doc-hidden { - visibility: hidden; + display: none; } /* Combined code/doc block styling From ce4d093df81cfe49e0d38d1f48b1b1df1b534ceb Mon Sep 17 00:00:00 2001 From: "Bryan A. Jones" Date: Thu, 21 May 2026 21:57:50 +0500 Subject: [PATCH 4/9] Docs: update/wrap. --- CHANGELOG.md | 5 ++++- server/src/translation.rs | 11 ++++++++--- server/tests/overall_common/mod.rs | 1 + 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d463f551..a6c484fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,7 +22,10 @@ Changelog [Github master](https://github.com/bjones1/CodeChat_Editor) ----------------------------------------------------------- -* No changes yet. +* Fix regression that moves the cursor back one character when at the end of a + line in a doc block in the Client. +* Correct minifier use to reduce unnecessary re-translations. +* Enable horizontal scrolling in Client, fix approach to scrollbars. Version 0.1.55-beta1 -- 2026-May-12 ----------------------------------- diff --git a/server/src/translation.rs b/server/src/translation.rs index e87cf978..892d2a4a 100644 --- a/server/src/translation.rs +++ b/server/src/translation.rs @@ -1345,10 +1345,15 @@ fn compare_html( if let Ok(raw_html) = transform_html(raw_html, compare_html_walker) && let Ok(raw_html) = minify(&raw_html) { - // pulldown-cmark puts a newline after a `
`, which `minify` - // doesn't remove but TinyMCE does. + // pulldown-cmark puts a newline after a `
`, which `minify` doesn't + // remove but TinyMCE does. let normalized_html = normalized_html.replace("
", "
"); - // TinyMCE wraps an `

`, which minifies to `

Previous paragraph

`. The IDE doesn't wrap the ``, which minifies to `

Previous paragraph

`. Fix up this difference. + // TinyMCE wraps an `

`, which + // minifies to `

Previous paragraph

`. The IDE + // doesn't wrap the ``, which minifies to `

Previous + // paragraph

`. Fix up this difference. let raw_html = raw_html.replace("