Files
templisandClaude Opus 5 e65f9db3ae feat: Linux bulk importer for the Allflame Voyage Solver
Mirrors the solver's Windows-only voyage-import.ahk on KDE Wayland:

- poe-clip-bridge.py: clipboard watcher that keeps only PoE item text,
  serves it on loopback and can trigger a sweep from the browser
- voyage-sweep.py: hovers every cell of the in-game chart grid, Ctrl+C's
  it and hands over the whole batch in one go
- userscript: feeds the batch into the solver page without focusing it

Includes notes on the four Wayland pointer pitfalls this ran into.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-04 21:39:19 +02:00

68 lines
1.9 KiB
JavaScript

// ==UserScript==
// @name Allflame Voyage Solver - clipboard auto-paste
// @namespace templis
// @version 1.0
// @description Polls the local clipboard bridge and feeds copied PoE items into the solver without focusing the browser.
// @match https://one-more-map.github.io/allflame-voyage-solver/*
// @grant GM_xmlhttpRequest
// @connect 127.0.0.1
// @run-at document-idle
// ==/UserScript==
(function () {
"use strict";
const ENDPOINT = "http://127.0.0.1:8477/clip";
const INTERVAL = 400;
let lastSeq = -1;
const toast = document.createElement("div");
toast.style.cssText =
"position:fixed;bottom:12px;right:12px;z-index:99999;padding:6px 10px;" +
"border-radius:6px;font:12px/1.4 monospace;background:#222;color:#8f8;" +
"opacity:0;transition:opacity .2s;pointer-events:none";
document.body.appendChild(toast);
function flash(msg, color) {
toast.textContent = msg;
toast.style.color = color;
toast.style.opacity = "1";
setTimeout(() => (toast.style.opacity = "0"), 1500);
}
function feed(text) {
const dt = new DataTransfer();
dt.setData("text/plain", text);
document.dispatchEvent(
new ClipboardEvent("paste", {
clipboardData: dt,
bubbles: true,
cancelable: true,
})
);
flash("item pasted", "#8f8");
}
function poll() {
GM_xmlhttpRequest({
method: "GET",
url: ENDPOINT,
onload: (r) => {
try {
const data = JSON.parse(r.responseText);
if (data.seq !== lastSeq) {
lastSeq = data.seq;
if (data.text) feed(data.text); // also feeds the last item on page load
}
} catch (e) {
/* ignore malformed response */
}
},
onerror: () => flash("bridge offline", "#f88"),
});
}
setInterval(poll, INTERVAL);
poll();
})();