Skip to content

Commit 8fe8dec

Browse files
authored
Chrome extension stuff (#727)
* Chrome extension stuff * Changes * Signalify * More refactors * Component tracking start * Component name display * Fix issues * Add components re-rendering * Formatting * Changeset
1 parent b45c2b6 commit 8fe8dec

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+6845
-1001
lines changed

.changeset/nervous-pugs-switch.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@preact/signals-debug": minor
3+
---
4+
5+
Add devtools capabilities and component tracking

.changeset/perfect-mirrors-whisper.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@preact/signals": minor
3+
---
4+
5+
Call into component tracking of the chrome extension

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ yarn-debug.log*
66
yarn-error.log*
77
lerna-debug.log*
88
.pnpm-debug.log*
9+
*.local.*
910

1011
# Diagnostic reports (https://nodejs.org/api/report.html)
1112
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json

docs/demos/index.tsx

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
1+
import "@preact/signals-debug";
12
import { render } from "preact";
23
import { LocationProvider, Router, useLocation, lazy } from "preact-iso";
3-
import { signal, useSignal } from "@preact/signals";
4+
import { signal, useComputed, useSignal } from "@preact/signals";
45
import { setFlashingEnabled, constrainFlashToChildren } from "./render-flasher";
5-
import "@preact/signals-debug";
66

77
// disable flashing during initial render:
88
setFlashingEnabled(false);
99
setTimeout(setFlashingEnabled, 100, true);
1010

1111
const demos = {
1212
Counter,
13+
Sum,
1314
GlobalCounter,
1415
DuelingCounters,
1516
Nesting: lazy(() => import("./nesting")),
@@ -58,7 +59,7 @@ function displayName(name: string) {
5859
}
5960

6061
function Counter() {
61-
const count = useSignal(0, "counter");
62+
const count = useSignal(0, { name: "counter" });
6263

6364
return (
6465
<div class="card">
@@ -69,7 +70,40 @@ function Counter() {
6970
);
7071
}
7172

72-
const globalCount = signal(0, "global-counter");
73+
function Sum() {
74+
const a = useSignal(0, { name: "a" });
75+
const b = useSignal(0, { name: "b" });
76+
77+
const sum = useComputed(() => a.value + b.value, { name: "sum" });
78+
79+
return (
80+
<div class="card">
81+
<p>
82+
<label>
83+
A:{" "}
84+
<input
85+
type="number"
86+
value={a}
87+
onInput={e => (a.value = +e.currentTarget.value)}
88+
/>
89+
</label>
90+
</p>
91+
<p>
92+
<label>
93+
B:{" "}
94+
<input
95+
type="number"
96+
value={b}
97+
onInput={e => (b.value = +e.currentTarget.value)}
98+
/>
99+
</label>
100+
</p>
101+
<output>Sum: {sum}</output>
102+
</div>
103+
);
104+
}
105+
106+
const globalCount = signal(0, { name: "global-counter" });
73107
function GlobalCounter({ explain = true }) {
74108
return (
75109
<>

extension/.gitignore

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Extension artifacts
2+
*.crx
3+
*.pem
4+
*.zip
5+
6+
# Build outputs
7+
dist/
8+
build/
9+
10+
# Node modules
11+
node_modules/
12+
13+
# Temporary files
14+
.tmp/
15+
.cache/
16+
17+
# OS files
18+
.DS_Store
19+
Thumbs.db
20+
21+
# IDE files
22+
.vscode/
23+
.idea/
24+
*.swp
25+
*.swo
26+
27+
# Web-ext artifacts
28+
web-ext-artifacts/
29+
30+
# Logs
31+
*.log
32+
npm-debug.log*
33+
yarn-debug.log*
34+
yarn-error.log*

extension/background.js

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
// Background service worker for the extension
2+
3+
// Maps to store connections by tab ID
4+
const contentConnections = new Map(); // tab ID -> content script port
5+
const devtoolsConnections = new Map(); // tab ID -> devtools port
6+
7+
chrome.runtime.onConnect.addListener(port => {
8+
if (port.name === "content-to-background") {
9+
handleContentScriptConnection(port);
10+
} else if (port.name === "devtools-to-background") {
11+
handleDevToolsConnection(port);
12+
} else {
13+
console.warn("Unknown connection type:", port.name);
14+
port.disconnect();
15+
}
16+
});
17+
18+
function handleContentScriptConnection(port) {
19+
const tabId = port.sender?.tab?.id;
20+
21+
if (!tabId) {
22+
console.error("Content script connection missing tab ID");
23+
port.disconnect();
24+
return;
25+
}
26+
27+
contentConnections.set(tabId, port);
28+
29+
port.onMessage.addListener(message => {
30+
// Forward message to devtools if connected
31+
const devtoolsPort = devtoolsConnections.get(tabId);
32+
if (devtoolsPort) {
33+
try {
34+
devtoolsPort.postMessage(message);
35+
} catch (error) {
36+
console.error("Failed to forward message to devtools:", error);
37+
devtoolsConnections.delete(tabId);
38+
}
39+
} else {
40+
console.log(
41+
`No devtools connection for tab ${tabId}, message queued:`,
42+
message.type
43+
);
44+
}
45+
});
46+
47+
port.onDisconnect.addListener(() => {
48+
contentConnections.delete(tabId);
49+
50+
// Notify devtools if connected
51+
const devtoolsPort = devtoolsConnections.get(tabId);
52+
if (devtoolsPort) {
53+
try {
54+
devtoolsPort.postMessage({ type: "CONTENT_SCRIPT_DISCONNECTED" });
55+
} catch (error) {
56+
console.error(
57+
"Failed to notify devtools of content script disconnect:",
58+
error
59+
);
60+
}
61+
}
62+
});
63+
}
64+
65+
function handleDevToolsConnection(port) {
66+
let tabId = null;
67+
let isInitialized = false;
68+
69+
// Listen for the initial tab ID message
70+
const tabIdListener = message => {
71+
if (message.type === "DEVTOOLS_TAB_ID" && !isInitialized) {
72+
tabId = message.tabId;
73+
isInitialized = true;
74+
75+
devtoolsConnections.set(tabId, port);
76+
77+
// Remove the tab ID listener
78+
port.onMessage.removeListener(tabIdListener);
79+
80+
// Set up the main message listener
81+
port.onMessage.addListener(message => {
82+
// Forward message to content script if connected
83+
const contentPort = contentConnections.get(tabId);
84+
if (contentPort) {
85+
try {
86+
contentPort.postMessage(message);
87+
} catch (error) {
88+
console.error(
89+
"Failed to forward message to content script:",
90+
error
91+
);
92+
contentConnections.delete(tabId);
93+
}
94+
} else {
95+
console.log(`No content script connection for tab ${tabId}`);
96+
}
97+
});
98+
99+
// Send initial status to devtools
100+
const contentPort = contentConnections.get(tabId);
101+
try {
102+
port.postMessage({
103+
type: "BACKGROUND_READY",
104+
contentScriptConnected: !!contentPort,
105+
});
106+
} catch (error) {
107+
console.error("Failed to send initial status to devtools:", error);
108+
}
109+
}
110+
};
111+
112+
port.onMessage.addListener(tabIdListener);
113+
114+
port.onDisconnect.addListener(() => {
115+
if (tabId) {
116+
devtoolsConnections.delete(tabId);
117+
}
118+
});
119+
}
120+
121+
chrome.action.onClicked.addListener(tab => {
122+
chrome.tabs.sendMessage(tab.id, { type: "OPEN_DEVTOOLS_HINT" });
123+
});
124+
125+
// Clean up connections when tabs are closed
126+
chrome.tabs.onRemoved.addListener(tabId => {
127+
contentConnections.delete(tabId);
128+
devtoolsConnections.delete(tabId);
129+
});

0 commit comments

Comments
 (0)