${renderTabButtons()}
${
diff --git a/webui/src/main/resources/application/favicon.svg b/webui/src/main/resources/application/favicon.svg
index 6a09be9f..32b29bf6 100644
--- a/webui/src/main/resources/application/favicon.svg
+++ b/webui/src/main/resources/application/favicon.svg
@@ -194,7 +194,8 @@
C187.4,151.4,187.3,151.4,187.2,151.4z"/>
-
+
@@ -436,7 +437,8 @@
c-4.1-2.3-1-4.4,0.7-6.2c12.5-12.6,25.1-25.2,37.6-37.8C181.5,156.1,182.7,154.5,184.3,152.8z"/>
-
+
-
+
-
+
@@ -528,7 +532,8 @@
-
+
@@ -572,7 +577,8 @@
-
+
-
+
-
+
-
-
+
-
+
-
-
-
+
+
+
-
+
-
-
+
+
-
+
-
+
@@ -689,8 +708,10 @@
c0.7-0.2,1.4-0.3,2-0.5c-0.6-4.1-1.1-8.2-1.7-12.3c-0.3,0.1-0.5,0.1-0.8,0.2C345.3,172.4,345.1,174.7,344.5,177.7z"/>
-
-
+
+
{
diff --git a/webui/src/main/resources/application/index.html b/webui/src/main/resources/application/index.html
index 088e3fb3..11c466cf 100644
--- a/webui/src/main/resources/application/index.html
+++ b/webui/src/main/resources/application/index.html
@@ -16,10 +16,10 @@
rel="stylesheet"/>
-
-
-
-
+
+
+
+
diff --git a/webui/src/main/resources/application/main.js b/webui/src/main/resources/application/main.js
index 5fd70ad4..90ea7099 100644
--- a/webui/src/main/resources/application/main.js
+++ b/webui/src/main/resources/application/main.js
@@ -1,4 +1,16 @@
import {connect, queueMessage} from './chat.js';
+import {
+ applyToAllSvg,
+ closeModal,
+ findAncestor,
+ getSessionId,
+ refreshReplyForms,
+ refreshVerbose,
+ showModal,
+ substituteMessages,
+ toggleVerbose
+} from './functions.js';
+import {restoreTabs, updateTabs} from './tabs.js';
let messageVersions = {};
window.messageMap = {}; // Make messageMap global
@@ -8,6 +20,55 @@ let loadImages = "true";
let showMenubar = true;
let messageDiv;
+// Add debounce function
+function debounce(func, wait) {
+ let timeout;
+ return function executedFunction(...args) {
+ const later = () => {
+ clearTimeout(timeout);
+ func(...args);
+ };
+ clearTimeout(timeout);
+ timeout = setTimeout(later, wait);
+ };
+}
+
+// Create a debounced version of updateDocumentComponents
+const debouncedUpdateDocumentComponents = debounce(updateDocumentComponents, 250);
+
+function updateDocumentComponents() {
+ try {
+ if (typeof Prism !== 'undefined') Prism.highlightAll();
+ } catch (e) {
+ console.log("Error highlighting code: " + e);
+ }
+ try {
+ refreshVerbose();
+ } catch (e) {
+ console.log("Error refreshing verbose: " + e);
+ }
+ try {
+ refreshReplyForms()
+ } catch (e) {
+ console.log("Error refreshing reply forms: " + e);
+ }
+ try {
+ if (typeof mermaid !== 'undefined') mermaid.run();
+ } catch (e) {
+ console.log("Error running mermaid: " + e);
+ }
+ try {
+ applyToAllSvg();
+ } catch (e) {
+ console.log("Error applying SVG pan zoom: " + e);
+ }
+ try {
+ updateTabs();
+ } catch (e) {
+ console.log("Error updating tabs: " + e);
+ }
+}
+
function onWebSocketText(event) {
console.debug('WebSocket message:', event);
const messagesDiv = document.getElementById('messages');
@@ -25,19 +86,19 @@ function onWebSocketText(event) {
if (messageDiv) {
messageDiv.innerHTML = messageContent;
substituteMessages(messageId, messageDiv);
+ //requestAnimationFrame(() => updateNestedTabs(messageDiv));
}
});
- if (messageDivs.length == 0 && !messageId.startsWith("z")) {
+ if (messageDivs.length === 0 && !messageId.startsWith("z")) {
messageDiv = document.createElement('div');
messageDiv.className = 'message message-container ' + (messageId.startsWith('u') ? 'user-message' : 'response-message');
messageDiv.id = messageId;
messageDiv.innerHTML = messageContent;
if (messagesDiv) messagesDiv.appendChild(messageDiv);
substituteMessages(messageId, messageDiv);
-
- // Scroll to the new message with smooth behavior
- messageDiv.scrollIntoView({behavior: 'smooth', block: 'end'});
+ //requestAnimationFrame(() => updateNestedTabs(messageDiv));
}
+ if (messagesDiv) messagesDiv.scrollTop = messagesDiv.scrollHeight;
if (singleInput) {
const mainInput = document.getElementById('main-input');
if (mainInput) {
@@ -57,37 +118,7 @@ function onWebSocketText(event) {
console.log("Error: Could not find .main-input");
}
}
- if (messagesDiv) messagesDiv.scrollTop = messagesDiv.scrollHeight;
- try {
- if (typeof Prism !== 'undefined') Prism.highlightAll();
- } catch (e) {
- console.log("Error highlighting code: " + e);
- }
- try {
- refreshVerbose();
- } catch (e) {
- console.log("Error refreshing verbose: " + e);
- }
- try {
- refreshReplyForms()
- } catch (e) {
- console.log("Error refreshing reply forms: " + e);
- }
- try {
- if (typeof mermaid !== 'undefined') mermaid.run();
- } catch (e) {
- console.log("Error running mermaid: " + e);
- }
- try {
- applyToAllSvg();
- } catch (e) {
- console.log("Error applying SVG pan zoom: " + e);
- }
- try {
- updateTabs();
- } catch (e) {
- console.log("Error updating tabs: " + e);
- }
+ debouncedUpdateDocumentComponents();
}
document.addEventListener('DOMContentLoaded', () => {
@@ -99,33 +130,7 @@ document.addEventListener('DOMContentLoaded', () => {
applyToAllSvg();
}, 5000); // Adjust the interval as needed
- // Restore the selected tabs from localStorage before adding event listeners
- document.querySelectorAll('.tabs-container').forEach(tabsContainer => {
- const savedTab = localStorage.getItem(`selectedTab_${tabsContainer.id}`);
- if (savedTab) {
- const savedButton = tabsContainer.querySelector(`.tab-button[data-for-tab="${savedTab}"]`);
- if (savedButton) {
- savedButton.classList.add('active');
- const forTab = savedButton.getAttribute('data-for-tab');
- const selectedContent = tabsContainer.querySelector(`.tab-content[data-tab="${forTab}"]`);
- if (selectedContent) {
- selectedContent.classList.add('active');
- selectedContent.style.display = 'block';
- }
- console.log(`Restored saved tab: ${savedTab}`);
- }
- }
- });
- document.querySelectorAll('.tabs-container').forEach(tabsContainer => {
- const savedTab = localStorage.getItem(`selectedTab_${tabsContainer.id}`);
- if (savedTab) {
- const savedButton = tabsContainer.querySelector(`.tab-button[data-for-tab="${savedTab}"]`);
- if (savedButton) {
- savedButton.click();
- console.log(`Restored saved tab: ${savedTab}`);
- }
- }
- });
+ restoreTabs();
const historyElement = document.getElementById('history');
if (historyElement) historyElement.addEventListener('click', () => showModal('sessions'));
diff --git a/webui/src/main/resources/application/main.scss b/webui/src/main/resources/application/main.scss
index 34cd9293..0d476ca5 100644
--- a/webui/src/main/resources/application/main.scss
+++ b/webui/src/main/resources/application/main.scss
@@ -1,25 +1,27 @@
@import '../shared/schemes/normal';
@import '../shared/main';
- .cmd-button {
- display: inline-block;
- padding: 8px 15px;
- font-size: 14px;
- cursor: pointer;
- text-align: center;
- text-decoration: none;
- outline: none;
- color: #fff;
- background-color: #4CAF50;
- border: none;
- border-radius: 5px;
- box-shadow: 0 9px #999;
- }
+.cmd-button {
+ display: inline-block;
+ padding: 8px 15px;
+ font-size: 14px;
+ cursor: pointer;
+ text-align: center;
+ text-decoration: none;
+ outline: none;
+ color: #fff;
+ background-color: #4CAF50;
+ border: none;
+ border-radius: 5px;
+ box-shadow: 0 9px #999;
+}
- .cmd-button:hover {background-color: #3e8e41}
+.cmd-button:hover {
+ background-color: #3e8e41
+}
- .cmd-button:active {
- background-color: #3e8e41;
- box-shadow: 0 5px #666;
- transform: translateY(4px);
- }
\ No newline at end of file
+.cmd-button:active {
+ background-color: #3e8e41;
+ box-shadow: 0 5px #666;
+ transform: translateY(4px);
+}
\ No newline at end of file
diff --git a/webui/src/main/resources/application/tabs.js b/webui/src/main/resources/application/tabs.js
index c161c796..df077274 100644
--- a/webui/src/main/resources/application/tabs.js
+++ b/webui/src/main/resources/application/tabs.js
@@ -2,57 +2,89 @@ const observer = new MutationObserver(updateTabs);
const observerOptions = {childList: true, subtree: true};
const tabCache = new Map();
-function updateTabs() {
+export function updateTabs() {
const tabButtons = document.querySelectorAll('.tab-button');
+ const tabsContainers = new Set();
tabButtons.forEach(button => {
+ const tabsContainer = button.closest('.tabs-container');
+ tabsContainers.add(tabsContainer);
if (button.hasListener) return;
button.hasListener = true;
- // console.log(`Adding click event listener to tab button: ${button.getAttribute('data-for-tab')}`);
+ // console.log(`Adding click event listener to tab button: ${button.getAttribute('data-for-tab')}, button element:`, button);
button.addEventListener('click', (event) => {
- // console.log(`Tab button clicked: ${button.getAttribute('data-for-tab')}`);
+ // console.log(`Tab button clicked: ${button.getAttribute('data-for-tab')}, event:`, event);
event.stopPropagation();
const forTab = button.getAttribute('data-for-tab');
const tabsContainerId = button.closest('.tabs-container').id;
- // console.log(`Tabs container ID: ${tabsContainerId}`);
- // console.log(`Saving selected tab to localStorage: selectedTab_${tabsContainerId} = ${forTab}`);
+ // console.log(`Tabs container ID: ${tabsContainerId}, button:`, button);
+ // console.log(`Saving selected tab to localStorage: selectedTab_${tabsContainerId} = ${forTab}, button:`, button);
try {
localStorage.setItem(`selectedTab_${tabsContainerId}`, forTab);
+ tabCache.set(tabsContainerId, forTab); // Update the cache
} catch (e) {
console.warn('Failed to save tab state to localStorage:', e);
}
let tabsParent = button.closest('.tabs-container');
const tabButtons = tabsParent.querySelectorAll('.tab-button');
- for (let i = 0; i < tabButtons.length; i++) {
- if (tabButtons[i].closest('.tabs-container') === tabsParent) {
- tabButtons[i].classList.remove('active');
+ tabButtons.forEach(btn => {
+ if (btn.closest('.tabs-container') === tabsParent) {
+ btn.classList.remove('active');
}
- }
+ });
button.classList.add('active');
- // console.log(`Active tab set to: ${forTab}`);
+ // console.log(`Active tab set to: ${forTab}, button:`, button);
let selectedContent = null;
const tabContents = tabsParent.querySelectorAll('.tab-content');
- for (let i = 0; i < tabContents.length; i++) {
- const content = tabContents[i];
- if (content.closest('.tabs-container') !== tabsParent) continue;
+ tabContents.forEach(content => {
+ if (content.closest('.tabs-container') !== tabsParent) return;
if (content.getAttribute('data-tab') === forTab) {
content.classList.add('active');
content.style.display = 'block'; // Ensure the content is displayed
- // console.log(`Content displayed for tab: ${forTab}`);
+ // console.log(`Content displayed for tab: ${forTab}, content element:`, content);
selectedContent = content;
} else {
content.classList.remove('active');
content.style.display = 'none'; // Ensure the content is hidden
- // console.log(`Content hidden for tab: ${content.getAttribute('data-tab')}`);
+ // console.log(`Content hidden for tab: ${content.getAttribute('data-tab')}, content element:`, content);
}
- }
+ });
if (selectedContent !== null) {
requestAnimationFrame(() => updateNestedTabs(selectedContent));
}
});
- // Check if the current button should be activated based on localStorage
- const savedTab = getSavedTab(button.closest('.tabs-container').id);
- if (button.getAttribute('data-for-tab') === savedTab) {
- button.dispatchEvent(new Event('click'));
+ });
+
+ // Restore the selected tabs from localStorage
+ tabsContainers.forEach(tabsContainer => {
+ const savedTab = getSavedTab(tabsContainer.id);
+ if (savedTab) {
+ const savedButton = tabsContainer.querySelector(`.tab-button[data-for-tab="${savedTab}"]`);
+ if (savedButton) {
+ savedButton.click(); // Simulate a click to activate the tab
+ // console.log(`Restored saved tab: ${savedTab}`);
+ }
+ } else {
+ tabsContainer.querySelector('.tab-button')?.click(); // Activate the first tab
+ }
+ });
+}
+
+export function restoreTabs() {
+ // Restore the selected tabs from localStorage before adding event listeners
+ document.querySelectorAll('.tabs-container').forEach(tabsContainer => {
+ const savedTab = localStorage.getItem(`selectedTab_${tabsContainer.id}`);
+ if (savedTab) {
+ const savedButton = tabsContainer.querySelector(`.tab-button[data-for-tab="${savedTab}"]`);
+ if (savedButton) {
+ savedButton.classList.add('active');
+ const forTab = savedButton.getAttribute('data-for-tab');
+ const selectedContent = tabsContainer.querySelector(`.tab-content[data-tab="${forTab}"]`);
+ if (selectedContent) {
+ selectedContent.classList.add('active');
+ selectedContent.style.display = 'block';
+ }
+ console.log(`Restored saved tab: ${savedTab}`);
+ }
}
});
}
@@ -71,65 +103,47 @@ function getSavedTab(containerId) {
}
}
-function updateNestedTabs(element) {
- const tabsContainers = element.querySelectorAll('.tabs-container');
- for (let i = 0; i < tabsContainers.length; i++) {
- const tabsContainer = tabsContainers[i];
- try {
- // console.log(`Updating nested tabs for container: ${tabsContainer.id}`);
- let hasActiveButton = false;
- const nestedButtons = tabsContainer.querySelectorAll('.tab-button');
- for (let j = 0; j < nestedButtons.length; j++) {
- const nestedButton = nestedButtons[j];
- }
- if (nestedButton.classList.contains('active')) {
- hasActiveButton = true;
- // console.log(`Found active nested button: ${nestedButton.getAttribute('data-for-tab')}`);
- }
- if (!hasActiveButton) {
- /* Determine if a tab-content element in this tabs-container has the active class. If so, use its data-tab value to find the matching button and ensure it is marked active */
- const activeContent = tabsContainer.querySelector('.tab-content.active');
- if (activeContent) {
- const activeTab = activeContent.getAttribute('data-tab');
- const activeButton = tabsContainer.querySelector(`.tab-button[data-for-tab="${activeTab}"]`);
- if (activeButton !== null) {
- activeButton.classList.add('active');
- // console.log(`Set active nested button: ${activeTab}`);
- }
- } else {
- /* Add 'active' to the class list of the first button */
- const firstButton = tabsContainer.querySelector('.tab-button');
- if (firstButton !== null) {
- firstButton.classList.add('active');
- // console.log(`Set first nested button as active: ${firstButton.getAttribute('data-for-tab')}`);
- }
- }
- }
- const savedTab = getSavedTab(tabsContainer.id);
- // console.log(`Retrieved saved tab from localStorage: selectedTab_${tabsContainer.id} = ${savedTab}`);
- if (savedTab) {
- const savedButton = tabsContainer.querySelector(`.tab-button[data-for-tab="${savedTab}"]`);
- if (savedButton) {
- savedButton.classList.add('active');
- const forTab = savedButton.getAttribute('data-for-tab');
- const selectedContent = tabsContainer.querySelector(`.tab-content[data-tab="${forTab}"]`);
- if (selectedContent) {
- selectedContent.classList.add('active');
- selectedContent.style.display = 'block';
+ function updateNestedTabs(element) {
+ const tabsContainers = element.querySelectorAll('.tabs-container');
+ tabsContainers.forEach(tabsContainer => {
+ try {
+ let hasActiveButton = false;
+ const nestedButtons = tabsContainer.querySelectorAll('.tab-button');
+ nestedButtons.forEach(nestedButton => {
+ // console.log(`Checking nested button: ${nestedButton.getAttribute('data-for-tab')}, nestedButton element:`, nestedButton);
+ if (nestedButton.classList.contains('active')) {
+ hasActiveButton = true;
+ }
+ });
+ if (!hasActiveButton) {
+ const activeContent = tabsContainer.querySelector('.tab-content.active');
+ if (activeContent) {
+ const activeTab = activeContent.getAttribute('data-tab');
+ const activeButton = tabsContainer.querySelector(`.tab-button[data-for-tab="${activeTab}"]`);
+ if (activeButton) {
+ activeButton.click(); // Simulate a click to activate the tab
+ }
+ } else {
+ tabsContainer.querySelector('.tab-button')?.click(); // Activate the first tab
+ }
+ }
+ const savedTab = getSavedTab(tabsContainer.id);
+ if (savedTab) {
+ const savedButton = tabsContainer.querySelector(`.tab-button[data-for-tab="${savedTab}"]`);
+ if (savedButton) {
+ if (!savedButton.classList.contains('active')) {
+ savedButton.click(); // Simulate a click to activate the tab only if it's not already active
}
- // console.log(`Restored saved tab: ${savedTab}`);
- }
- }
- } catch (e) {
- // console.log("Error updating tabs: " + e);
- }
- }
-}
+ }
+ }
+ } catch (e) {
+ console.warn('Failed to update nested tabs:', e);
+ }
+ });
+ }
document.addEventListener('DOMContentLoaded', () => {
- // console.log('Document loaded. Initializing tabs...');
updateTabs();
- updateNestedTabs(document);
observer.observe(document.body, observerOptions);
});
diff --git a/webui/src/main/resources/shared/_main.scss b/webui/src/main/resources/shared/_main.scss
index b0a25e1d..52aab999 100644
--- a/webui/src/main/resources/shared/_main.scss
+++ b/webui/src/main/resources/shared/_main.scss
@@ -157,12 +157,16 @@ body {
border-radius: $border-radius;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
transition: box-shadow 0.3s ease, background-color 0.3s ease;
-
&:hover {
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
}
}
+pre {
+ max-height: 70vh;
+ overflow: auto;
+}
+
.user-message {
background-color: lighten($user-message-bg, 5%);
border-left: 4px solid $user-message-border;
diff --git a/webui/src/test/kotlin/com/simiacryptus/diff/IterativePatchUtilTest.kt b/webui/src/test/kotlin/com/simiacryptus/diff/IterativePatchUtilTest.kt
index fd85a009..8acbbff2 100644
--- a/webui/src/test/kotlin/com/simiacryptus/diff/IterativePatchUtilTest.kt
+++ b/webui/src/test/kotlin/com/simiacryptus/diff/IterativePatchUtilTest.kt
@@ -1,6 +1,7 @@
package com.simiacryptus.diff
import org.junit.jupiter.api.Assertions
+import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
class IterativePatchUtilTest {
@@ -18,7 +19,7 @@ class IterativePatchUtilTest {
line3
""".trimIndent()
val result = IterativePatchUtil.applyPatch(source, patch)
- Assertions.assertEquals(source.trim().replace("\r\n", "\n"), result.replace("\r\n", "\n"))
+ assertEquals(source.trim().replace("\r\n", "\n"), result.replace("\r\n", "\n"))
}
// ... (other existing tests)
@@ -42,7 +43,7 @@ class IterativePatchUtilTest {
line3
""".trimIndent()
val result = IterativePatchUtil.applyPatch(source, patch)
- Assertions.assertEquals(expected.trim().replace("\r\n", "\n"), result.replace("\r\n", "\n"))
+ assertEquals(expected.trim().replace("\r\n", "\n"), result.replace("\r\n", "\n"))
}
@Test
@@ -64,7 +65,31 @@ class IterativePatchUtilTest {
line3
""".trimIndent()
val result = IterativePatchUtil.applyPatch(source, patch)
- Assertions.assertEquals(expected.trim().replace("\r\n", "\n"), result.replace("\r\n", "\n"))
+ assertEquals(expected.trim().replace("\r\n", "\n"), result.replace("\r\n", "\n"))
+ }
+
+ @Test
+ fun testPatchModifyLineWithComments() {
+ val source = """
+ line1
+ line3
+ line2
+ """.trimIndent()
+ val patch = """
+ line1
+ line3
+ // This comment should be ignored
+ -line2
+ +modifiedLine2
+ # LLMs sometimes get chatty and add stuff to patches__
+ """.trimIndent()
+ val expected = """
+ line1
+ line3
+ modifiedLine2
+ """.trimIndent()
+ val result = IterativePatchUtil.applyPatch(source, patch)
+ assertEquals(expected.trim().replace("\r\n", "\n"), result.replace("\r\n", "\n"))
}
@Test
@@ -84,7 +109,65 @@ class IterativePatchUtilTest {
line3
""".trimIndent()
val result = IterativePatchUtil.applyPatch(source, patch)
- Assertions.assertEquals(expected.trim().replace("\r\n", "\n"), result.replace("\r\n", "\n"))
+ assertEquals(expected.trim().replace("\r\n", "\n"), result.replace("\r\n", "\n"))
+ }
+
+ @Test
+ fun testPatchAdd2Line2() {
+ val source = """
+ line1
+
+ line2
+ line3
+ """.trimIndent()
+ val patch = """
+ line1
+ + lineA
+
+ + lineB
+
+ line2
+ line3
+ """.trimIndent()
+ val expected = """
+ line1
+ lineA
+ lineB
+
+ line2
+ line3
+ """.trimIndent()
+ val result = IterativePatchUtil.applyPatch(source, patch)
+ assertEquals(expected.trim().replace("\r\n", "\n"), result.replace("\r\n", "\n"))
+ }
+
+ @Test
+ fun testPatchAdd2Line3() {
+ val source = """
+ line1
+
+ line2
+ line3
+ """.trimIndent()
+ val patch = """
+ line1
+ // extraneous comment
+ + lineA
+ + lineB
+ // llms sometimes get chatty and add stuff to patches
+ line2
+ line3
+ """.trimIndent()
+ val expected = """
+ line1
+ lineA
+ lineB
+
+ line2
+ line3
+ """.trimIndent()
+ val result = IterativePatchUtil.applyPatch(source, patch)
+ assertEquals(expected.trim().replace("\r\n", "\n"), result.replace("\r\n", "\n"))
}
@Test
@@ -148,12 +231,104 @@ class IterativePatchUtilTest {
}
""".trimIndent()
val result = IterativePatchUtil.applyPatch(source, patch)
- Assertions.assertEquals(
+ assertEquals(
expected.replace("\\s*\r?\n\\s*".toRegex(), "\n"),
result.replace("\\s*\r?\n\\s*".toRegex(), "\n")
)
}
+ @Test
+ fun testFromData2() {
+ val source = """
+ export class StandardChessModel implements GameModel {
+ geometry: BoardGeometry;
+ state: GameState;
+ private moveHistory: MoveHistory;
+
+ constructor(initialBoard?: Piece[]) {
+ this.geometry = new StandardBoardGeometry();
+ this.state = initialBoard ? this.initializeWithBoard(initialBoard) : this.initialize();
+ this.moveHistory = new MoveHistory(this.state.board);
+ }
+
+ redoMove(): GameState {
+ return this.getState();
+ }
+
+ isGameOver(): boolean {
+ return false;
+ }
+
+ getWinner(): 'white' | 'black' | 'draw' | null {
+ return null;
+ }
+
+ importState(stateString: string): GameState {
+ // Implement import state logic
+ const parsedState = JSON.parse(stateString);
+ // Validate and convert the parsed state to GameState
+ // For now, we'll just return the current state
+ return this.getState();
+ }
+
+ }
+
+ // Similar changes for black pawns
+ """.trimIndent()
+ val patch = """
+ | export class StandardChessModel implements GameModel {
+ | // ... other methods ...
+ |
+ |- getWinner(): 'white' | 'black' | 'draw' | null {
+ |+ getWinner(): ChessColor | 'draw' | null {
+ | return null;
+ | }
+ |
+ | // ... other methods ...
+ | }
+ """.trimMargin()
+ val expected = """
+ export class StandardChessModel implements GameModel {
+ geometry: BoardGeometry;
+ state: GameState;
+ private moveHistory: MoveHistory;
+
+ constructor(initialBoard?: Piece[]) {
+ this.geometry = new StandardBoardGeometry();
+ this.state = initialBoard ? this.initializeWithBoard(initialBoard) : this.initialize();
+ this.moveHistory = new MoveHistory(this.state.board);
+ }
+
+ redoMove(): GameState {
+ return this.getState();
+ }
+
+ isGameOver(): boolean {
+ return false;
+ }
+
+ getWinner(): ChessColor | 'draw' | null {
+ return null;
+ }
+
+ importState(stateString: string): GameState {
+ // Implement import state logic
+ const parsedState = JSON.parse(stateString);
+ // Validate and convert the parsed state to GameState
+ // For now, we'll just return the current state
+ return this.getState();
+ }
+
+ }
+
+ // Similar changes for black pawns
+ """.trimIndent()
+ val result = IterativePatchUtil.applyPatch(source, patch)
+ assertEquals(
+ expected.replace("\\s*\r?\n\\s*".toRegex(), "\n"),
+ result.replace("\\s*\r?\n\\s*".toRegex(), "\n")
+ )
+ }
@Test
@@ -167,7 +342,7 @@ class IterativePatchUtilTest {
val result = IterativePatchUtil.generatePatch(oldCode, newCode)
val expected = """
""".trimMargin()
- Assertions.assertEquals(expected.trim().replace("\r\n", "\n"), result.trim().replace("\r\n", "\n"))
+ assertEquals(expected.trim().replace("\r\n", "\n"), result.trim().replace("\r\n", "\n"))
}
@Test
@@ -188,8 +363,9 @@ class IterativePatchUtilTest {
| line1
| line2
|+ newLine
+ | line3
""".trimMargin()
- Assertions.assertEquals(expected.trim().replace("\r\n", "\n"), result.trim().replace("\r\n", "\n"))
+ assertEquals(expected.trim().replace("\r\n", "\n"), result.trim().replace("\r\n", "\n"))
}
@Test
@@ -209,7 +385,7 @@ class IterativePatchUtilTest {
|- line2
| line3
""".trimMargin()
- Assertions.assertEquals(expected.trim().replace("\r\n", "\n"), result.trim().replace("\r\n", "\n"))
+ assertEquals(expected.trim().replace("\r\n", "\n"), result.trim().replace("\r\n", "\n"))
}
@Test
@@ -229,8 +405,9 @@ class IterativePatchUtilTest {
| line1
|- line2
|+ modifiedLine2
+ | line3
""".trimMargin()
- Assertions.assertEquals(
+ assertEquals(
expected.trim().replace("\r\n", "\n"),
result.trim().replace("\r\n", "\n")
)
@@ -263,10 +440,214 @@ class IterativePatchUtilTest {
|+ // Modified comment
|+ let x = 5;
|+ return x > 0;
+ | }
""".trimMargin()
- Assertions.assertEquals(
+ assertEquals(
expected.trim().replace("\r\n", "\n"),
result.trim().replace("\r\n", "\n")
)
}
+
+ @Test
+ fun testGeneratePatchMoveLineUpwardsMultiplePositions() {
+ val oldCode = """
+ line1
+ line2
+ line3
+ line4
+ line5
+ line6
+ """.trimIndent()
+
+ val newCode = """
+ line1
+ line5
+ line2
+ line3
+ line4
+ line6
+ """.trimIndent()
+
+ val expectedPatch = """
+ line1
+ - line2
+ - line3
+ - line4
+ line5
+ + line2
+ + line3
+ + line4
+ line6
+ """.trimIndent()
+
+ val actualPatch = IterativePatchUtil.generatePatch(oldCode, newCode)
+ assertEquals(expectedPatch, actualPatch)
+ }
+
+ @Test
+ fun testGeneratePatchMoveLineDownwardsMultiplePositions() {
+ val oldCode = """
+ line1
+ line2
+ line3
+ line4
+ line5
+ line6
+ """.trimIndent()
+
+ val newCode = """
+ line1
+ line3
+ line4
+ line5
+ line6
+ line2
+ """.trimIndent()
+
+ val expectedPatch = """
+ line1
+ - line2
+ line3
+ line4
+ line5
+ line6
+ + line2
+ """.trimIndent()
+
+ val actualPatch = IterativePatchUtil.generatePatch(oldCode, newCode)
+ assertEquals(expectedPatch, actualPatch)
+ }
+
+ @Test
+ fun testGeneratePatchSwapLines() {
+ val oldCode = """
+ line1
+ line2
+ line3
+ line4
+ line5
+ line6
+ """.trimIndent()
+
+ val newCode = """
+ line1
+ line4
+ line3
+ line2
+ line5
+ line6
+ """.trimIndent()
+
+ val expectedPatch = """
+ line1
+ - line2
+ - line3
+ line4
+ + line3
+ + line2
+ line5
+ line6
+ """.trimIndent()
+
+ val actualPatch = IterativePatchUtil.generatePatch(oldCode, newCode)
+ assertEquals(expectedPatch, actualPatch)
+ }
+
+ @Test
+ fun testGeneratePatchMoveAdjacentLines() {
+ val oldCode = """
+ line1
+ line2
+ line3
+ line4
+ line5
+ line6
+ """.trimIndent()
+
+ val newCode = """
+ line1
+ line4
+ line5
+ line2
+ line3
+ line6
+ """.trimIndent()
+
+ val expectedPatch = """
+ line1
+ - line2
+ - line3
+ line4
+ line5
+ + line2
+ + line3
+ line6
+ """.trimIndent()
+
+ val actualPatch = IterativePatchUtil.generatePatch(oldCode, newCode)
+ assertEquals(expectedPatch, actualPatch)
+ }
+
+ @Test
+ fun testGeneratePatchMoveLineUpwards() {
+ val oldCode = """
+ line1
+ line2
+ line3
+ line4
+ line5
+ line6
+ """.trimIndent()
+ val newCode = """
+ line1
+ line2
+ line5
+ line3
+ line4
+ line6
+ """.trimIndent()
+ val expectedPatch = """
+ line1
+ line2
+ - line3
+ - line4
+ line5
+ + line3
+ + line4
+ line6
+ """.trimIndent()
+ val actualPatch = IterativePatchUtil.generatePatch(oldCode, newCode)
+ assertEquals(expectedPatch, actualPatch)
+ }
+
+ @Test
+ fun testGeneratePatchMoveLineDownwards() {
+ val oldCode = """
+ line1
+ line2
+ line3
+ line4
+ line5
+ line6
+ """.trimIndent()
+ val newCode = """
+ line1
+ line3
+ line4
+ line5
+ line2
+ line6
+ """.trimIndent()
+ val expectedPatch = """
+ line1
+ - line2
+ line3
+ line4
+ line5
+ + line2
+ line6
+ """.trimIndent()
+ val actualPatch = IterativePatchUtil.generatePatch(oldCode, newCode)
+ assertEquals(expectedPatch, actualPatch)
+ }
}