Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
You can view your application's file structure and click on a snapshot to view
your app's state. State can be visualized in a Component Graph, JSON Tree, or
Performance Graph. Snapshot history can be visualized in the History tab.
The Web Metrics tab provides some useful metrics for site performance. The accessibility tab
The Web Metrics tab provides some useful metrics for site performance. The accessibility tab
visualizes an app's accessibility tree per state change.
Snapshots can be compared with the previous snapshot, which can be viewed in Diff mode.
<br>
Expand Down Expand Up @@ -89,7 +89,7 @@ Download the recorded snapshots as a JSON file and upload them to access state t
</p>
<br>

### 🔹 Reconnect and Status
### 🔹 and Status

If Reactime loses its connection to the tab you're monitoring, simply click the "reconnect" button to resume your work. You'll notice a circle located to the right of the button, which will appear as either red (indicating disconnection) or green (signifying a successful reconnection).
<br>
Expand Down
2 changes: 1 addition & 1 deletion src/app/components/Actions/SwitchApp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,4 @@ const SwitchAppDropdown = (): JSX.Element => {
);
};

export default SwitchAppDropdown;
export default SwitchAppDropdown;
2 changes: 1 addition & 1 deletion src/app/components/StateRoute/History.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ function History(props: Record<string, unknown>): JSX.Element {
delete newObj.state; // delete the state property
}
if (newObj.stateSnaphot) {
// if our new object has a stateSnaphot property
// if our new object has a stateSnaphot propertys
newObj.stateSnaphot = statelessCleaning(obj.stateSnaphot); // run statelessCleaning on the stateSnapshot
}

Expand Down
1 change: 0 additions & 1 deletion src/backend/controllers/createTree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,6 @@ export default function createTree(currentFiberNode: Fiber): Tree {
const hooksStates = getHooksStateAndUpdateMethod(memoizedState);
// Obtain variable names by parsing the function definition stored in elementType.
const hooksNames = getHooksNames(elementType.toString());

// Intialize state & index:
componentData.hooksState = {};
componentData.hooksIndex = [];
Expand Down
1 change: 1 addition & 0 deletions src/backend/controllers/timeJump.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export default function timeJumpInitiation(mode: Status) {
* @param targetSnapshot - The target snapshot to re-render. The payload from index.ts is assigned to targetSnapshot
*/
return async function timeJump(targetSnapshot: Tree): Promise<void> {
console.log('Time jump initiated with targetSnapshot:', targetSnapshot); // logging to see if the re-rendering bug lives here
mode.jumping = true;
// Reset mode.navigating
delete mode.navigating;
Expand Down
85 changes: 53 additions & 32 deletions src/extension/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -267,14 +267,14 @@ function changeCurrLocation(tabObj, rootNode, index, name) {

async function getActiveTab() {
return new Promise((resolve, reject) => {
chrome.tabs.query({ active: true, currentWindow: true }, function(tabs) {
chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
if (tabs.length > 0) {
resolve(tabs[0].id);
} else {
reject(new Error('No active tab'))
reject(new Error('No active tab'));
}
});
})
});
}

/*
Expand Down Expand Up @@ -307,19 +307,18 @@ chrome.runtime.onConnect.addListener(async (port) => {
portsArr.push(port); // push each Reactime communication channel object to the portsArr
// sets the current Title of the Reactime panel

/**
* Sends messages to ports in the portsArr array, triggering a tab change action.
*/
/**
* Sends messages to ports in the portsArr array, triggering a tab change action.
*/
function sendMessagesToPorts() {
portsArr.forEach((bg, index) => {
bg.postMessage({
action: 'changeTab',
payload: { tabId: activeTab.id, title: activeTab.title },
});
bg.postMessage({
action: 'changeTab',
payload: { tabId: activeTab.id, title: activeTab.title },
});
});
}
}


if (portsArr.length > 0 && Object.keys(tabsObj).length > 0) {
//if the activeTab is not set during the onActivate API, run a query to get the tabId and set activeTab
if (!activeTab) {
Expand All @@ -330,8 +329,8 @@ chrome.runtime.onConnect.addListener(async (port) => {
activeTab = tab;
sendMessagesToPorts();
}
});
};
});
}
}

if (Object.keys(tabsObj).length > 0) {
Expand All @@ -341,12 +340,24 @@ chrome.runtime.onConnect.addListener(async (port) => {
});
}

// every time devtool is closed, remove the port from portsArr
// Handles port disconnection by removing the disconnected port and attempting reconnection after 1s delay
// This prevents permanent connection loss during idle periods or temporary disconnects -ellie
port.onDisconnect.addListener((e) => {
for (let i = 0; i < portsArr.length; i += 1) {
if (portsArr[i] === e) {
portsArr.splice(i, 1);
chrome.runtime.sendMessage({ action: 'portDisconnect', port: e.name });
setTimeout(async () => {
try {
const response = await chrome.runtime.sendMessage({
action: 'attemptReconnect',
});
if (response && response.success) {
console.log('Port successfully reconnected');
}
} catch (error) {
console.warn('Port reconnection failed:', error);
}
}, 1000);
break;
}
}
Expand Down Expand Up @@ -428,7 +439,6 @@ chrome.runtime.onConnect.addListener(async (port) => {
toggleAxRecord = !toggleAxRecord;

await replaceEmptySnap(tabsObj, tabId, toggleAxRecord);

// sends new tabs obj to devtools
if (portsArr.length > 0) {
portsArr.forEach((bg) =>
Expand Down Expand Up @@ -599,13 +609,26 @@ chrome.runtime.onMessage.addListener(async (request, sender, sendResponse) => {
break;
}

// DUPLICATE SNAPSHOT CHECK
// This may be where the bug is coming from that when Reactime fails to collect
// state. If they happen to take the same actual duration, it won't record the snapshot.
const previousSnap =
tabsObj[tabId]?.currLocation?.stateSnapshot?.children[0]?.componentData?.actualDuration;
const incomingSnap = request.payload.children[0].componentData.actualDuration;
if (previousSnap === incomingSnap) {
// DUPLICATE SNAPSHOT CHECK -ellie
const isDuplicateSnapshot = (previous, incoming) => {
if (!previous || !incoming) return false;
const prevData = previous?.componentData;
const incomingData = incoming?.componentData;

// Check if both snapshots have required data
if (!prevData || !incomingData) return false;

const timeDiff = Math.abs(
(incomingData.timestamp || Date.now()) - (prevData.timestamp || Date.now()),
);
return prevData.actualDuration === incomingData.actualDuration && timeDiff < 1000;
};

const previousSnap = tabsObj[tabId]?.currLocation?.stateSnapshot?.children[0];
const incomingSnap = request.payload.children[0];

if (isDuplicateSnapshot(previousSnap, incomingSnap)) {
console.warn('Duplicate snapshot detected, skipping');
break;
}

Expand All @@ -626,7 +649,6 @@ chrome.runtime.onMessage.addListener(async (request, sender, sendResponse) => {
addedAxSnap = 'emptyAxSnap';
tabsObj[tabId].axSnapshots.push(addedAxSnap);
}

sendToHierarchy(
tabsObj[tabId],
new HistoryNode(tabsObj[tabId], request.payload, addedAxSnap),
Expand Down Expand Up @@ -705,13 +727,12 @@ chrome.tabs.onActivated.addListener((info) => {
if (!tab.pendingUrl?.match('^chrome-extension')) {
activeTab = tab;

/**this setInterval is here to make sure that the app does not stop working even
* if chrome pauses to save energy. There is probably a better solution, but v25 did
* not have time to complete.
*/
setInterval(() => {
console.log(activeTab)
}, 10000);
/**this setKeepAlive is here to make sure that the app does not stop working even
* if chrome pauses to save energy.
*/
chrome.runtime.onStartup.addListener(() => {
chrome.runtime.setKeepAlive(true);
});
if (portsArr.length > 0) {
portsArr.forEach((bg) =>
bg.postMessage({
Expand Down
2 changes: 2 additions & 0 deletions src/extension/contentScript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ window.addEventListener('message', (msg) => {
const { action }: { action: string } = msg.data;
if (action === 'recordSnap') {
if (isRecording) {
// add timestamp to payload for the purposes of duplicate screenshot check in backgroundscript -ellie
msg.data.payload.children[0].componentData.timestamp = Date.now();
chrome.runtime.sendMessage(msg.data);
}
}
Expand Down