feat: side panel pager #46

This commit is contained in:
leukipp
2025-08-31 19:24:50 +02:00
parent bf77d9b2a0
commit d78088e6af
3 changed files with 205 additions and 39 deletions
+44 -21
View File
@@ -46,11 +46,19 @@
li.dynamic {
flex: 1 1 0%;
}
li.separator {
height: 32px;
border: 1px solid var(--border-color);
}
li.separator:has(+ li.hidden) {
display: none;
}
li.hidden {
display: none;
}
li.disabled {
opacity: 0.5;
cursor: not-allowed;
pointer-events: none;
}
button {
@@ -62,12 +70,13 @@
input {
width: 100%;
min-width: 0;
margin: 0 10px;
margin: 0 5px;
padding: 6px 10px;
border-radius: 4px;
font-size: 18px;
outline: none;
border: none;
cursor: text;
box-sizing: border-box;
color: var(--primary-color);
background-color: var(--border-color);
@@ -90,6 +99,17 @@
</svg>
</button>
</li>
<li>
<button id="refresh">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<title>refresh-circle</title>
<path
d="M12 2A10 10 0 1 0 22 12A10 10 0 0 0 12 2M18 11H13L14.81 9.19A3.94 3.94 0 0 0 12 8A4 4 0 1 0 15.86 13H17.91A6 6 0 1 1 12 6A5.91 5.91 0 0 1 16.22 7.78L18 6Z"
/>
</svg>
</button>
</li>
<li class="separator"></li>
<li>
<button id="previous">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
@@ -110,9 +130,32 @@
</svg>
</button>
</li>
<li class="separator"></li>
<li class="dynamic">
<input id="url" type="text" spellcheck="false" />
</li>
<li class="separator"></li>
<li>
<button id="backward">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<title>history</title>
<path
d="M13.5,8H12V13L16.28,15.54L17,14.33L13.5,12.25V8M13,3A9,9 0 0,0 4,12H1L4.96,16.03L9,12H6A7,7 0 0,1 13,5A7,7 0 0,1 20,12A7,7 0 0,1 13,19C11.07,19 9.32,18.21 8.06,16.94L6.64,18.36C8.27,20 10.5,21 13,21A9,9 0 0,0 22,12A9,9 0 0,0 13,3"
/>
</svg>
</button>
</li>
<li>
<button id="forward">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<title>update</title>
<path
d="M21,10.12H14.22L16.96,7.3C14.23,4.6 9.81,4.5 7.08,7.2C4.35,9.91 4.35,14.28 7.08,17C9.81,19.7 14.23,19.7 16.96,17C18.32,15.65 19,14.08 19,12.1H21C21,14.08 20.12,16.65 18.36,18.39C14.85,21.87 9.15,21.87 5.64,18.39C2.14,14.92 2.11,9.28 5.62,5.81C9.13,2.34 14.76,2.34 18.27,5.81L21,3V10.12M12.5,8V12.25L16,14.33L15.28,15.54L11,13V8H12.5Z"
/>
</svg>
</button>
</li>
<li class="separator"></li>
<li>
<button id="minus">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
@@ -133,26 +176,6 @@
</svg>
</button>
</li>
<li>
<button id="backward">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<title>arrow-left-circle</title>
<path
d="M2,12A10,10 0 0,1 12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12M18,11H10L13.5,7.5L12.08,6.08L6.16,12L12.08,17.92L13.5,16.5L10,13H18V11Z"
/>
</svg>
</button>
</li>
<li>
<button id="forward">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<title>arrow-right-circle</title>
<path
d="M22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2A10,10 0 0,1 22,12M6,13H14L10.5,16.5L11.92,17.92L17.84,12L11.92,6.08L10.5,7.5L14,11H6V13Z"
/>
</svg>
</button>
</li>
</menu>
<script>
const { ipcRenderer } = require("electron");
+82
View File
@@ -0,0 +1,82 @@
<!doctype html>
<html>
<head>
<style>
[data-theme="hidden"] {
--primary-color: rgba(0, 0, 0, 0);
--border-color: rgba(0, 0, 0, 0);
--background-color: rgba(0, 0, 0, 0);
}
::-webkit-scrollbar {
display: none;
}
* {
margin: 0;
padding: 0;
cursor: none;
}
body {
display: flex;
align-items: center;
width: 100vw;
height: 100vh;
}
menu {
display: flex;
flex-direction: column;
align-items: center;
width: 100vw;
height: 100vh;
list-style-type: none;
background: var(--background-color);
}
li {
display: flex;
align-items: center;
flex: 0 0 auto;
}
button {
width: 100vw;
height: 50vh;
border: none;
background-color: transparent;
}
#previous {
cursor: w-resize;
}
#next {
cursor: e-resize;
}
</style>
</head>
<body>
<menu>
<li>
<button id="previous"></button>
</li>
<li>
<button id="next"></button>
</li>
</menu>
<script>
const { ipcRenderer } = require("electron");
const registerSendEvents = () => {
document.querySelectorAll("button").forEach((button) => {
button.addEventListener("click", (e) => {
ipcRenderer.send("button-click", { id: e.target.id });
});
});
};
const registerReceiveEvents = () => {
ipcRenderer.on("data-theme", (e, data) => {
document.documentElement.setAttribute("data-theme", data.theme);
});
};
registerSendEvents();
registerReceiveEvents();
</script>
</body>
</html>
+79 -18
View File
@@ -43,6 +43,7 @@ const init = async () => {
WEBVIEW.viewUrls = urls;
WEBVIEW.viewZoom = zoom;
WEBVIEW.viewTheme = theme;
WEBVIEW.pagerEnabled = widget;
WEBVIEW.widgetTheme = theme;
WEBVIEW.widgetEnabled = widget;
WEBVIEW.navigationTheme = theme;
@@ -80,6 +81,18 @@ const init = async () => {
});
});
// Init global pager
WEBVIEW.pager = new WebContentsView({
transparent: true,
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
},
});
WEBVIEW.pager.setBackgroundColor("#00000000");
WEBVIEW.window.contentView.addChildView(WEBVIEW.pager);
WEBVIEW.pager.webContents.loadFile(path.join(app.getAppPath(), "html", "pager.html"));
// Init global widget
WEBVIEW.widget = new WebContentsView({
transparent: true,
@@ -151,7 +164,11 @@ const updateView = () => {
if (!WEBVIEW.viewActive) {
return;
}
const title = `TouchKio - ${new URL(WEBVIEW.viewUrls[WEBVIEW.viewActive]).host} (${WEBVIEW.viewActive})`;
const url = WEBVIEW.views[WEBVIEW.viewActive].webContents.getURL();
const host = url.startsWith("data:") ? "whoopsie" : new URL(url).host;
const title = `TouchKio - ${host} (${WEBVIEW.viewActive})`;
// Update window title
console.log(`Update View: ${title}`);
WEBVIEW.window.setTitle(title);
@@ -250,6 +267,11 @@ const toggleNavigation = () => {
width: window.width,
height: height,
});
if (height > 0) {
WEBVIEW.navigation.webContents.focus();
} else {
WEBVIEW.views[WEBVIEW.viewActive].webContents.focus();
}
// Resize webview
resizeView();
@@ -310,7 +332,30 @@ const nextView = () => {
};
/**
* Reloads the current or default url on the active webview.
* Reloads the default url and settings on the active webview.
*/
const homeView = () => {
const view = WEBVIEW.views[WEBVIEW.viewActive];
const defaultUrl = WEBVIEW.viewUrls[WEBVIEW.viewActive];
const currentUrl = view.webContents.getURL();
// Reload the default url or refresh the page
if (currentUrl != defaultUrl) {
view.webContents.loadURL(defaultUrl);
} else {
view.webContents.reloadIgnoringCache();
}
// Reset page zoom and history
view.webContents.setZoomFactor(WEBVIEW.viewZoom);
setTimeout(() => {
view.webContents.navigationHistory.clear();
update();
}, 2000);
};
/**
* Reloads the current url on the active webview.
*/
const reloadView = () => {
const view = WEBVIEW.views[WEBVIEW.viewActive];
@@ -318,7 +363,7 @@ const reloadView = () => {
const currentUrl = view.webContents.getURL();
// Reload the default url or refresh the page
if (currentUrl != defaultUrl) {
if (currentUrl.startsWith("data:")) {
view.webContents.loadURL(defaultUrl);
} else {
view.webContents.reloadIgnoringCache();
@@ -332,6 +377,7 @@ const reloadView = () => {
const resizeView = () => {
const window = WEBVIEW.window.getBounds();
const navigation = WEBVIEW.navigation.getBounds();
const pager = { width: 20, height: window.height };
const widget = { width: 60, height: 200 };
// Update view size
@@ -344,10 +390,21 @@ const resizeView = () => {
});
});
// Update pager size
if (WEBVIEW.pagerEnabled) {
WEBVIEW.pager.setBounds({
x: window.width - pager.width,
y: 0,
width: pager.width,
height: pager.height,
});
WEBVIEW.pager.webContents.send("data-theme", { theme: "hidden" });
}
// Update widget size
if (WEBVIEW.widgetEnabled) {
WEBVIEW.widget.setBounds({
x: window.width - 15,
x: window.width - 20,
y: parseInt(window.height / 2 - widget.height / 2, 10),
width: widget.width,
height: widget.height,
@@ -400,9 +457,6 @@ const windowEvents = () => {
WEBVIEW.window.on("leave-full-screen", update);
// Handle global shortcut events
globalShortcut.register("F5", () => {
reloadView();
});
globalShortcut.register("Control+Left", () => {
previousView();
});
@@ -468,7 +522,7 @@ const widgetEvents = () => {
// Hide widget
WEBVIEW.widget.setBounds({
x: window.width - 15,
x: window.width - 20,
y: widget.y,
width: widget.width,
height: widget.height,
@@ -481,7 +535,6 @@ const widgetEvents = () => {
switch (button.id) {
case "keyboard":
const toggle = hardware.getKeyboardVisibility() === "ON" ? "OFF" : "ON";
hardware.setKeyboardVisibility(toggle);
switch (toggle) {
case "OFF":
WEBVIEW.window.restore();
@@ -494,6 +547,9 @@ const widgetEvents = () => {
WEBVIEW.window.maximize();
break;
}
hardware.setKeyboardVisibility(toggle, () => {
WEBVIEW.views[WEBVIEW.viewActive].webContents.focus();
});
break;
case "fullscreen":
if (WEBVIEW.window.isFullScreen()) {
@@ -505,7 +561,9 @@ const widgetEvents = () => {
WEBVIEW.window.unmaximize();
WEBVIEW.window.setFullScreen(true);
}
hardware.setKeyboardVisibility("OFF");
hardware.setKeyboardVisibility("OFF", () => {
WEBVIEW.views[WEBVIEW.viewActive].webContents.focus();
});
break;
case "minimize":
WEBVIEW.window.restore();
@@ -583,6 +641,14 @@ const navigationEvents = () => {
ipcMain.on("button-click", (e, button) => {
const view = WEBVIEW.views[WEBVIEW.viewActive];
switch (button.id) {
case "home":
homeView();
hardware.setKeyboardVisibility("OFF");
break;
case "refresh":
reloadView();
hardware.setKeyboardVisibility("OFF");
break;
case "previous":
previousView();
hardware.setKeyboardVisibility("OFF");
@@ -591,10 +657,6 @@ const navigationEvents = () => {
nextView();
hardware.setKeyboardVisibility("OFF");
break;
case "home":
reloadView();
hardware.setKeyboardVisibility("OFF");
break;
case "minus":
zoomMinus();
hardware.setKeyboardVisibility("OFF");
@@ -668,20 +730,19 @@ const viewEvents = () => {
// Webview url changed
view.webContents.on("did-navigate-in-page", (e, url, mainframe) => {
if (mainframe) {
update();
updateView();
}
});
view.webContents.on("did-navigate", () => {
update();
updateView();
});
// Handle webview mouse events
view.webContents.on("before-mouse-event", (event, mouse) => {
view.webContents.on("before-mouse-event", (e, mouse) => {
const now = new Date();
const then = WEBVIEW.pointer.time;
const delta = Math.abs(now - then) / 1000;
WEBVIEW.pointer.time = now;
switch (mouse.type) {
case "mouseMove":
if (delta > 30) {