Skip to content

Commit 3af0821

Browse files
authored
Merge pull request #3 from SOORAJTS2001/feat/mode-activation
Linting and formatting has been skipped, would be finished in the later part
2 parents 863e148 + 28fd727 commit 3af0821

26 files changed

+30253
-4
lines changed

app.py

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
# ruff : noqa
2+
import socket
3+
from contextlib import asynccontextmanager
4+
5+
import qrcode
6+
import uvicorn
7+
from fastapi import FastAPI, WebSocket, WebSocketDisconnect
8+
from fastapi.responses import HTMLResponse
9+
from fastapi.staticfiles import StaticFiles
10+
from rich.columns import Columns
11+
from rich.console import Console
12+
from rich.panel import Panel
13+
14+
PORT = 8000
15+
HOST = "0.0.0.0"
16+
RELOAD = False
17+
console = Console()
18+
19+
20+
def get_wifi_ip():
21+
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
22+
try:
23+
s.connect(("8.8.8.8", 80))
24+
ip = s.getsockname()[0]
25+
finally:
26+
s.close()
27+
return ip
28+
29+
30+
def get_server_url():
31+
ip = get_wifi_ip()
32+
return f"http://{ip}:{PORT}"
33+
34+
35+
def generate_qr_ascii(url: str) -> str:
36+
qr = qrcode.QRCode(border=1)
37+
qr.add_data(url)
38+
qr.make()
39+
# Capture ASCII QR into a string
40+
import io, sys
41+
42+
buf = io.StringIO()
43+
sys_stdout = sys.stdout
44+
sys.stdout = buf
45+
qr.print_ascii(invert=True)
46+
sys.stdout = sys_stdout
47+
return buf.getvalue()
48+
49+
50+
@asynccontextmanager
51+
async def lifespan(app: FastAPI):
52+
url = get_server_url()
53+
mobile_page_url = f"{url}/mobile_page"
54+
qr_ascii = generate_qr_ascii(mobile_page_url)
55+
56+
qr_panel = Panel.fit(qr_ascii, title="Scan to Open", border_style="green")
57+
steps_panel = Panel.fit(
58+
"\n".join(
59+
[
60+
f"[bold cyan]1.[/bold cyan] Connect to the same Wi-Fi network",
61+
f"[bold cyan]2.[/bold cyan] Scan the QR code",
62+
f"[bold cyan] [/bold cyan] Or [yellow]{mobile_page_url}[/yellow]",
63+
f"[bold cyan]3.[/bold cyan] Go to the scroll area and enjoy!",
64+
]
65+
),
66+
title="Steps",
67+
border_style="blue",
68+
)
69+
70+
console.print(Columns([qr_panel, steps_panel]))
71+
yield
72+
73+
74+
app = FastAPI(lifespan=lifespan)
75+
app.mount("/static", StaticFiles(directory="mobile_page"), name="static")
76+
77+
# Store connected clients
78+
connected_clients: list[WebSocket] = []
79+
80+
81+
@app.get("/mobile_page")
82+
async def get_page2():
83+
with open("mobile_page/index.html") as f:
84+
return HTMLResponse(f.read())
85+
86+
87+
@app.websocket("/ws")
88+
async def websocket_endpoint(websocket: WebSocket):
89+
await websocket.accept()
90+
connected_clients.append(websocket)
91+
try:
92+
while True:
93+
data = await websocket.receive_text()
94+
for client in connected_clients:
95+
if client != websocket:
96+
await client.send_text(data)
97+
except WebSocketDisconnect:
98+
connected_clients.remove(websocket)
99+
100+
101+
if __name__ == "__main__":
102+
uvicorn.run("app:app", host=HOST, port=PORT, reload=RELOAD)

browser_extension/content.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
function injectWhenReady() {
2+
if (!document.head || !document.body) {
3+
// Retry until DOM is ready
4+
requestAnimationFrame(injectWhenReady);
5+
return;
6+
}
7+
const css = document.createElement('link');
8+
css.rel = 'stylesheet';
9+
css.href = chrome.runtime.getURL('runtime/pyscript.css');
10+
document.head.appendChild(css);
11+
12+
const pyscriptJs = document.createElement('script');
13+
pyscriptJs.src = chrome.runtime.getURL('runtime/pyscript.js');
14+
pyscriptJs.defer = true;
15+
document.head.appendChild(pyscriptJs);
16+
17+
pyscriptJs.onload = () => {
18+
fetch(chrome.runtime.getURL('main.py'))
19+
.then(res => res.text())
20+
.then(code => {
21+
const pyTag = document.createElement('py-script');
22+
pyTag.textContent = code;
23+
document.body.appendChild(pyTag);
24+
});
25+
}}
26+
injectWhenReady()

0 commit comments

Comments
 (0)