Skip to content

Liyourong added pop-up window feature to Tina-Pop-Up #3

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
Binary file added .DS_Store
Binary file not shown.
87 changes: 86 additions & 1 deletion chrome-extension/js/background.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
(function () {
"use strict";

let popupPort = null;
const eventHistoryMap = {};
const recordingStateMap = {};


const taskIdMap = {};
const lastPageGoToTimestamps = {};
let collectorHost = "127.0.0.1";
Expand Down Expand Up @@ -124,6 +129,57 @@
} else if (port.name === 'content-script') {
handleContentScriptConnection(port);
}
else if (port.name === 'popup') {
popupPort = port;
port.onDisconnect.addListener(() => { popupPort = null; });

chrome.tabs.query({ active: true, lastFocusedWindow: true }, tabs => {
const tid = tabs[0]?.id;
console.log(`🔍 [BG→popup] popup connected, querying history for tabId =`, tid,
'history length =', (eventHistoryMap[tid] || []).length);
if (tid != null) {
(eventHistoryMap[tid] || []).forEach((ev, i) => {
console.log(` → event[${i}] =`, ev);
popupPort.postMessage({ type: 'event', event: ev });
});
popupPort.postMessage({
type: 'recording-state',
tabId: tid,
recording: !!recordingStateMap[tid]
});
}
});

port.onMessage.addListener(msg => {
const { type, tabId, desc } = msg;

// —— 1) 拉回历史
if (type === 'get-history' && typeof tabId === 'number') {
const history = eventHistoryMap[tabId] || [];
history.forEach(ev => popupPort.postMessage({ type: 'event', event: ev }));
return;
}

// —— 2) 拉回当前录制状态
if (type === 'get-recording-state' && typeof tabId === 'number') {
const isRec = !!recordingStateMap[tabId];
popupPort.postMessage({ type: 'recording-state', tabId, recording: isRec });
return;
}

// —— 3) popup 发来的开始/停止录制命令
if (type === 'start-record' && typeof tabId === 'number') {
const p = devToolsPorts[tabId];
if (p) p.postMessage({ type: 'popup-start', desc });
return;
}
if (type === 'stop-record' && typeof tabId === 'number') {
const p = devToolsPorts[tabId];
if (p) p.postMessage({ type: 'popup-stop' });
return;
}
});
}
});

var devToolsPorts = {};
Expand All @@ -149,7 +205,20 @@
console.log('[OTA DOM Background]: Script injected successfully', injectionResults);
}
});
} else {
}
else if (message.type === 'panel-record-started') {
recordingStateMap[tabId] = true;
if (popupPort) {
popupPort.postMessage({ type: 'recording-state', tabId, recording: true });
}
}
else if (message.type === 'panel-record-stopped') {
recordingStateMap[tabId] = false;
if (popupPort) {
popupPort.postMessage({ type: 'recording-state', tabId, recording: false });
}
}
else {
//pass message from DevTools panel to a content script
if (contentScriptPorts[tabId]) {
contentScriptPorts[tabId].postMessage(message);
Expand All @@ -175,9 +244,22 @@
console.log('[OTA DOM Background]: content script status: ', message.type, ', tab ID: ', tabId);

//pass message from content script to the appropriate DevTools panel
if (message.type === 'event') { //如果有信息就缓存起来,到时候给popup发
if (!eventHistoryMap[tabId]) eventHistoryMap[tabId] = [];
eventHistoryMap[tabId].push(message.event);
console.log(
`🔍 [BG] eventHistoryMap[${tabId}].length =`,
eventHistoryMap[tabId].length
);
}else{
console.log('[BG] NOT caching type=', message.type);
}
if (devToolsPorts[tabId]) {
devToolsPorts[tabId].postMessage(message);
}
if (popupPort) {
popupPort.postMessage(message);
}
};

port.onMessage.addListener(messageListener);
Expand All @@ -191,6 +273,9 @@
type: 'disconnected'
});
}
if (popupPort) {
popupPort.postMessage({ type: 'disconnected' });
}
});
}

Expand Down
38 changes: 38 additions & 0 deletions chrome-extension/js/panel.js
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,8 @@
taskVisibilityBtn.innerText = 'Hide Task';
taskSection.style.display = 'block'; // Task visible by default

sendToBG({ type: 'panel-record-started' });

/* Optional intro fade-out (unchanged) */
if (intro.style.display !== 'none') {
intro.animate([{ opacity: 1 }, { opacity: 0 }], 300)
Expand Down Expand Up @@ -185,6 +187,8 @@

eventTable.clear();

sendToBG({ type: 'panel-record-stopped' });

/* Optional intro fade-out (unchanged) */
intro.style.display = 'block'; // make it visible immediately
intro.style.opacity = 0; // start transparent
Expand Down Expand Up @@ -384,6 +388,40 @@
eventTable.clear();
break;


case 'popup-start': {
// 来自 popup 的开始录制
const desc = message.desc;
// 复用 recordBtnHandler 里的 Start 流程(不重复 UI 逻辑)
ContentScriptProxy.startRecording(desc);
// 更新面板 UI
showLabel(desc);
recordBtn.innerText = 'Finish Record';
pauseResumeBtn.disabled = false;
recording = true;
// 请求并显示 taskId
setTimeout(() => getCurrentTaskId(), 1000);
// 通知 background “面板已开始”
sendToBG({ type: 'panel-record-started' });
break;
}

case 'popup-stop': {
// 来自 popup 的停止录制
ContentScriptProxy.finishRecording();
// 恢复面板 UI
showInput(true);
recordBtn.innerText = 'Start Record';
pauseResumeBtn.disabled = true;
taskVisibilityBtn.hidden = true;
eventTable.clear();
recording = false;
// 通知 background “面板已停止”
sendToBG({ type: 'panel-record-stopped' });
break;
}


/* you may add more cases as needed */

default:
Expand Down
62 changes: 62 additions & 0 deletions chrome-extension/js/popup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
(function(){
"use strict";

// —— UI 绑定 ——
const clearBtn = document.querySelector('.clear');
const recordBtn = document.querySelector('.record-btn');
const descInput = document.querySelector('.task-desc-input');
const table = document.querySelector('.events');
const eventTable = new EventTable(table);

let currentTabId, recording = false;

// —— 建立和 background 的长连接 ——
const port = chrome.runtime.connect({ name: 'popup' });
port.onMessage.addListener(msg => {
console.log('📝 popup received message:', msg);
const { tabId, event, recording: isRec } = msg;
//if (tabId !== currentTabId) return;

// —— 只要有 event 对象,就渲染它 ——
if (event) {
eventTable.addEvent(event);
const rowCount = document.querySelector('.events tbody').children.length;
console.log(`📝 popup: tbody now has ${rowCount} rows`);
return;
}

// —— 同步录制状态 ——
if (msg.type === 'recording-state') {
recording = isRec;
recordBtn.innerText = recording ? 'Stop Record' : 'Start Record';
descInput.disabled = recording;
}
});

// —— 一打开就抓 tabId ——
chrome.tabs.query({ active: true, currentWindow: true }, tabs => {
currentTabId = tabs[0].id;
});

// —— 清空表格 ——
clearBtn.addEventListener('click', () => {
eventTable.clear();
});

// —— 开始/停止录制 ——
recordBtn.addEventListener('click', () => {
const desc = descInput.value.trim();
console.log('📝 popup click recordBtn, recording=', recording, 'desc=', desc);

if (!recording) {
if (!desc) {
console.warn('⚠️ popup: empty description, abort start-record');
descInput.classList.add('invalid');
return;
}
port.postMessage({ type: 'start-record', tabId: currentTabId, desc });
} else {
port.postMessage({ type: 'stop-record', tabId: currentTabId });
}
});
})();
7 changes: 7 additions & 0 deletions chrome-extension/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@
"devtools_page": "devtools.html",
"content_security_policy": {
"extension_pages": "script-src 'self'; object-src 'self'"
},
"action": {
"default_icon": {
"128": "ico/ota-logo-128.png",
"48": "ico/ota-logo-48.png"
},
"default_popup": "popup.html"
}
}

71 changes: 71 additions & 0 deletions chrome-extension/popup.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>OTA Events</title>
<link rel="stylesheet" href="css/normalize.css">
<link rel="stylesheet" href="css/skeleton.css">
<link rel="stylesheet" href="css/panel.css">

<style>
html, body {
width: 800px;
height: 800px;
overflow: auto;
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<nav>
<input type="text"
class="task-desc-input"
placeholder="Describe your task…"/>
<button class="button-primary record-btn">Start Record</button>
<button class="clear">Clear</button>
</nav>
<main>
<table class="events u-full-width">
<thead>
<tr>
<!-- 复制过来的过滤 UI -->
<th>
<div class="type-filters">
<div class="checkbox nodes-added" title="nodes added">
<input type="checkbox" id="nodes-added-filter" checked/>
<label for="nodes-added-filter"></label>
</div>
<div class="checkbox nodes-removed" title="nodes removed">
<input type="checkbox" id="nodes-removed-filter" checked/>
<label for="nodes-removed-filter"></label>
</div>
<div class="checkbox attribute-changed" title="attribute changed">
<input type="checkbox" id="attribute-changed-filter" checked/>
<label for="attribute-changed-filter"></label>
</div>
<div class="checkbox text-changed" title="text changed">
<input type="checkbox" id="text-changed-filter" checked/>
<label for="text-changed-filter"></label>
</div>
</div>
Event <span class="counter">(0)</span>
</th>
<!-- 目标过滤 -->
<th>
<input type="text" placeholder="filter" class="target-filter"/>
Target
</th>
<th>Details</th>
</tr>
</thead>
<tbody></tbody>
</table>

</main>

<script src="js/EventTable.js"></script>
<script src="js/popup.js"></script>
</body>
</html>