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 { li.dynamic {
flex: 1 1 0%; flex: 1 1 0%;
} }
li.separator {
height: 32px;
border: 1px solid var(--border-color);
}
li.separator:has(+ li.hidden) {
display: none;
}
li.hidden { li.hidden {
display: none; display: none;
} }
li.disabled { li.disabled {
opacity: 0.5; opacity: 0.5;
cursor: not-allowed;
pointer-events: none; pointer-events: none;
} }
button { button {
@@ -62,12 +70,13 @@
input { input {
width: 100%; width: 100%;
min-width: 0; min-width: 0;
margin: 0 10px; margin: 0 5px;
padding: 6px 10px; padding: 6px 10px;
border-radius: 4px; border-radius: 4px;
font-size: 18px; font-size: 18px;
outline: none; outline: none;
border: none; border: none;
cursor: text;
box-sizing: border-box; box-sizing: border-box;
color: var(--primary-color); color: var(--primary-color);
background-color: var(--border-color); background-color: var(--border-color);
@@ -90,6 +99,17 @@
</svg> </svg>
</button> </button>
</li> </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> <li>
<button id="previous"> <button id="previous">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
@@ -110,9 +130,32 @@
</svg> </svg>
</button> </button>
</li> </li>
<li class="separator"></li>
<li class="dynamic"> <li class="dynamic">
<input id="url" type="text" spellcheck="false" /> <input id="url" type="text" spellcheck="false" />
</li> </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> <li>
<button id="minus"> <button id="minus">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
@@ -133,26 +176,6 @@
</svg> </svg>
</button> </button>
</li> </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> </menu>
<script> <script>
const { ipcRenderer } = require("electron"); 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.viewUrls = urls;
WEBVIEW.viewZoom = zoom; WEBVIEW.viewZoom = zoom;
WEBVIEW.viewTheme = theme; WEBVIEW.viewTheme = theme;
WEBVIEW.pagerEnabled = widget;
WEBVIEW.widgetTheme = theme; WEBVIEW.widgetTheme = theme;
WEBVIEW.widgetEnabled = widget; WEBVIEW.widgetEnabled = widget;
WEBVIEW.navigationTheme = theme; 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 // Init global widget
WEBVIEW.widget = new WebContentsView({ WEBVIEW.widget = new WebContentsView({
transparent: true, transparent: true,
@@ -151,7 +164,11 @@ const updateView = () => {
if (!WEBVIEW.viewActive) { if (!WEBVIEW.viewActive) {
return; 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}`); console.log(`Update View: ${title}`);
WEBVIEW.window.setTitle(title); WEBVIEW.window.setTitle(title);
@@ -250,6 +267,11 @@ const toggleNavigation = () => {
width: window.width, width: window.width,
height: height, height: height,
}); });
if (height > 0) {
WEBVIEW.navigation.webContents.focus();
} else {
WEBVIEW.views[WEBVIEW.viewActive].webContents.focus();
}
// Resize webview // Resize webview
resizeView(); 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 reloadView = () => {
const view = WEBVIEW.views[WEBVIEW.viewActive]; const view = WEBVIEW.views[WEBVIEW.viewActive];
@@ -318,7 +363,7 @@ const reloadView = () => {
const currentUrl = view.webContents.getURL(); const currentUrl = view.webContents.getURL();
// Reload the default url or refresh the page // Reload the default url or refresh the page
if (currentUrl != defaultUrl) { if (currentUrl.startsWith("data:")) {
view.webContents.loadURL(defaultUrl); view.webContents.loadURL(defaultUrl);
} else { } else {
view.webContents.reloadIgnoringCache(); view.webContents.reloadIgnoringCache();
@@ -332,6 +377,7 @@ const reloadView = () => {
const resizeView = () => { const resizeView = () => {
const window = WEBVIEW.window.getBounds(); const window = WEBVIEW.window.getBounds();
const navigation = WEBVIEW.navigation.getBounds(); const navigation = WEBVIEW.navigation.getBounds();
const pager = { width: 20, height: window.height };
const widget = { width: 60, height: 200 }; const widget = { width: 60, height: 200 };
// Update view size // 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 // Update widget size
if (WEBVIEW.widgetEnabled) { if (WEBVIEW.widgetEnabled) {
WEBVIEW.widget.setBounds({ WEBVIEW.widget.setBounds({
x: window.width - 15, x: window.width - 20,
y: parseInt(window.height / 2 - widget.height / 2, 10), y: parseInt(window.height / 2 - widget.height / 2, 10),
width: widget.width, width: widget.width,
height: widget.height, height: widget.height,
@@ -400,9 +457,6 @@ const windowEvents = () => {
WEBVIEW.window.on("leave-full-screen", update); WEBVIEW.window.on("leave-full-screen", update);
// Handle global shortcut events // Handle global shortcut events
globalShortcut.register("F5", () => {
reloadView();
});
globalShortcut.register("Control+Left", () => { globalShortcut.register("Control+Left", () => {
previousView(); previousView();
}); });
@@ -468,7 +522,7 @@ const widgetEvents = () => {
// Hide widget // Hide widget
WEBVIEW.widget.setBounds({ WEBVIEW.widget.setBounds({
x: window.width - 15, x: window.width - 20,
y: widget.y, y: widget.y,
width: widget.width, width: widget.width,
height: widget.height, height: widget.height,
@@ -481,7 +535,6 @@ const widgetEvents = () => {
switch (button.id) { switch (button.id) {
case "keyboard": case "keyboard":
const toggle = hardware.getKeyboardVisibility() === "ON" ? "OFF" : "ON"; const toggle = hardware.getKeyboardVisibility() === "ON" ? "OFF" : "ON";
hardware.setKeyboardVisibility(toggle);
switch (toggle) { switch (toggle) {
case "OFF": case "OFF":
WEBVIEW.window.restore(); WEBVIEW.window.restore();
@@ -494,6 +547,9 @@ const widgetEvents = () => {
WEBVIEW.window.maximize(); WEBVIEW.window.maximize();
break; break;
} }
hardware.setKeyboardVisibility(toggle, () => {
WEBVIEW.views[WEBVIEW.viewActive].webContents.focus();
});
break; break;
case "fullscreen": case "fullscreen":
if (WEBVIEW.window.isFullScreen()) { if (WEBVIEW.window.isFullScreen()) {
@@ -505,7 +561,9 @@ const widgetEvents = () => {
WEBVIEW.window.unmaximize(); WEBVIEW.window.unmaximize();
WEBVIEW.window.setFullScreen(true); WEBVIEW.window.setFullScreen(true);
} }
hardware.setKeyboardVisibility("OFF"); hardware.setKeyboardVisibility("OFF", () => {
WEBVIEW.views[WEBVIEW.viewActive].webContents.focus();
});
break; break;
case "minimize": case "minimize":
WEBVIEW.window.restore(); WEBVIEW.window.restore();
@@ -583,6 +641,14 @@ const navigationEvents = () => {
ipcMain.on("button-click", (e, button) => { ipcMain.on("button-click", (e, button) => {
const view = WEBVIEW.views[WEBVIEW.viewActive]; const view = WEBVIEW.views[WEBVIEW.viewActive];
switch (button.id) { switch (button.id) {
case "home":
homeView();
hardware.setKeyboardVisibility("OFF");
break;
case "refresh":
reloadView();
hardware.setKeyboardVisibility("OFF");
break;
case "previous": case "previous":
previousView(); previousView();
hardware.setKeyboardVisibility("OFF"); hardware.setKeyboardVisibility("OFF");
@@ -591,10 +657,6 @@ const navigationEvents = () => {
nextView(); nextView();
hardware.setKeyboardVisibility("OFF"); hardware.setKeyboardVisibility("OFF");
break; break;
case "home":
reloadView();
hardware.setKeyboardVisibility("OFF");
break;
case "minus": case "minus":
zoomMinus(); zoomMinus();
hardware.setKeyboardVisibility("OFF"); hardware.setKeyboardVisibility("OFF");
@@ -668,20 +730,19 @@ const viewEvents = () => {
// Webview url changed // Webview url changed
view.webContents.on("did-navigate-in-page", (e, url, mainframe) => { view.webContents.on("did-navigate-in-page", (e, url, mainframe) => {
if (mainframe) { if (mainframe) {
update(); updateView();
} }
}); });
view.webContents.on("did-navigate", () => { view.webContents.on("did-navigate", () => {
update(); updateView();
}); });
// Handle webview mouse events // 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 now = new Date();
const then = WEBVIEW.pointer.time; const then = WEBVIEW.pointer.time;
const delta = Math.abs(now - then) / 1000; const delta = Math.abs(now - then) / 1000;
WEBVIEW.pointer.time = now; WEBVIEW.pointer.time = now;
switch (mouse.type) { switch (mouse.type) {
case "mouseMove": case "mouseMove":
if (delta > 30) { if (delta > 30) {