mirror of
https://github.com/ShinkoNet/Wplace-Overlay-Pro.git
synced 2026-01-11 22:40:37 +00:00
Json remote loading
This commit is contained in:
parent
500ae13b6a
commit
3ddb431ea9
7 changed files with 97 additions and 10 deletions
56
src/app.ts
56
src/app.ts
|
|
@ -1,8 +1,61 @@
|
|||
/// <reference types="tampermonkey" />
|
||||
import { loadConfig, applyTheme } from './core/store';
|
||||
import { config, loadConfig, applyTheme } from './core/store';
|
||||
import { ensureHook, setUpdateUI } from './core/hook';
|
||||
import { injectStyles } from './ui/styles';
|
||||
import { createUI, updateUI } from './ui/panel';
|
||||
import { displayImageFromData } from './core/overlay';
|
||||
import { showToast } from './core/toast';
|
||||
import { urlToDataURL } from './core/gm';
|
||||
|
||||
async function applyTemplateFromUrl() {
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const templateUrl = urlParams.get('template');
|
||||
if (!templateUrl) return;
|
||||
|
||||
try {
|
||||
console.log(`Fetching template from URL: ${templateUrl}`);
|
||||
const res = await fetch(templateUrl);
|
||||
if (!res.ok) {
|
||||
const errorText = await res.text();
|
||||
throw new Error(
|
||||
`Failed to fetch template: ${res.status} ${res.statusText} - ${errorText}`,
|
||||
);
|
||||
}
|
||||
const json = await res.json();
|
||||
|
||||
if (!json.record || !json.record.imageUrl) {
|
||||
throw new Error('Invalid template format: missing `record.imageUrl`');
|
||||
}
|
||||
const { name, imageUrl, pixelUrl, offsetX, offsetY, opacity } = json.record;
|
||||
|
||||
if (config.overlays.some(o => o.name === name || o.imageUrl === imageUrl)) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Fetching image from: ${imageUrl}`);
|
||||
const imageBase64 = await urlToDataURL(imageUrl);
|
||||
|
||||
const newOverlay = {
|
||||
id: crypto.randomUUID(),
|
||||
name,
|
||||
enabled: true,
|
||||
imageUrl,
|
||||
isLocal: false,
|
||||
imageBase64,
|
||||
pixelUrl,
|
||||
offsetX,
|
||||
offsetY,
|
||||
opacity,
|
||||
};
|
||||
|
||||
console.log('Adding new overlay from URL template:', newOverlay);
|
||||
await displayImageFromData(newOverlay);
|
||||
showToast(`Template "${name}" loaded from URL`, 'success');
|
||||
} catch (err) {
|
||||
console.error('Error loading template from URL:', err);
|
||||
showToast(`Error: ${err.message}`, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
export async function bootstrapApp() {
|
||||
injectStyles();
|
||||
|
|
@ -11,5 +64,6 @@ export async function bootstrapApp() {
|
|||
createUI();
|
||||
setUpdateUI(() => updateUI());
|
||||
ensureHook();
|
||||
await applyTemplateFromUrl();
|
||||
console.log('Overlay Pro UI ready.');
|
||||
}
|
||||
|
|
@ -12,6 +12,10 @@ export function setUpdateUI(cb: () => void) {
|
|||
updateUICallback = cb;
|
||||
}
|
||||
|
||||
export function getUpdateUI() {
|
||||
return updateUICallback;
|
||||
}
|
||||
|
||||
export function overlaysNeedingHook() {
|
||||
const hasImage = config.overlays.some(o => o.enabled && o.imageBase64);
|
||||
const placing = !!config.autoCapturePixelUrl && !!config.activeOverlayId;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
import { createCanvas, createHTMLCanvas, canvasToBlob, blobToImage, loadImage } from './canvas';
|
||||
import { createCanvas, canvasToBlob, blobToImage, loadImage } from './canvas';
|
||||
import { MINIFY_SCALE, MINIFY_SCALE_SYMBOL, TILE_SIZE, MAX_OVERLAY_DIM } from './constants';
|
||||
import { imageDecodeCache, overlayCache, tooLargeOverlays, paletteDetectionCache, baseMinifyCache } from './cache';
|
||||
import { imageDecodeCache, overlayCache, tooLargeOverlays, paletteDetectionCache, baseMinifyCache, clearOverlayCache } from './cache';
|
||||
import { showToast } from './toast';
|
||||
import { config } from './store';
|
||||
import { config, saveConfig, type OverlayItem } from './store';
|
||||
import { WPLACE_FREE, WPLACE_PAID, SYMBOL_TILES, SYMBOL_W, SYMBOL_H } from './palette';
|
||||
import { getUpdateUI, ensureHook } from './hook';
|
||||
|
||||
const ALL_COLORS = [...WPLACE_FREE, ...WPLACE_PAID];
|
||||
const colorIndexMap = new Map<string, number>();
|
||||
|
|
@ -370,9 +371,13 @@ export async function composeTileUnified(
|
|||
const scale = config.minifyStyle === 'symbols' ? MINIFY_SCALE_SYMBOL : MINIFY_SCALE;
|
||||
const w = originalImage.width, h = originalImage.height;
|
||||
|
||||
const baseCacheKey = `base:${originalBlob.size}:${w}x${h}:${scale}:${config.minifyStyle}`;
|
||||
const arrayBuffer = await originalBlob.arrayBuffer();
|
||||
const view = new DataView(arrayBuffer);
|
||||
const hash = view.getUint32(0, true) ^ view.getUint32(view.byteLength - 4, true);
|
||||
|
||||
const baseCacheKey = `base:${originalBlob.size}:${hash}:${w}x${h}:${scale}:${config.minifyStyle}`;
|
||||
let scaledBaseImageData = baseMinifyCache.get(baseCacheKey);
|
||||
|
||||
|
||||
if (!scaledBaseImageData) {
|
||||
const baseCanvas = createCanvas(w * scale, h * scale) as any;
|
||||
const baseCtx = baseCanvas.getContext('2d', { willReadFrequently: true })!;
|
||||
|
|
@ -424,4 +429,20 @@ export async function composeTileUnified(
|
|||
}
|
||||
return await canvasToBlob(canvas);
|
||||
}
|
||||
}
|
||||
|
||||
export async function displayImageFromData(newOverlay: OverlayItem) {
|
||||
if (!config.overlays) {
|
||||
config.overlays = [];
|
||||
}
|
||||
config.overlays.push(newOverlay);
|
||||
await saveConfig();
|
||||
|
||||
clearOverlayCache();
|
||||
ensureHook();
|
||||
|
||||
const updateUI = getUpdateUI();
|
||||
if (updateUI) {
|
||||
updateUI();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,10 @@
|
|||
import { config } from './store';
|
||||
|
||||
export function showToast(message: string, duration = 3000) {
|
||||
export function showToast(
|
||||
message: string,
|
||||
type: 'info' | 'error' | 'success' = 'info',
|
||||
duration = 3000,
|
||||
) {
|
||||
let stack = document.getElementById('op-toast-stack');
|
||||
if (!stack) {
|
||||
stack = document.createElement('div');
|
||||
|
|
@ -11,7 +15,7 @@ export function showToast(message: string, duration = 3000) {
|
|||
stack.classList.toggle('op-dark', config.theme === 'dark');
|
||||
|
||||
const t = document.createElement('div');
|
||||
t.className = 'op-toast';
|
||||
t.className = `op-toast op-toast-${type}`;
|
||||
t.textContent = message;
|
||||
stack.appendChild(t);
|
||||
requestAnimationFrame(() => t.classList.add('show'));
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// ==UserScript==
|
||||
// @name Wplace Overlay Pro
|
||||
// @namespace http://tampermonkey.net/
|
||||
// @version 3.1.3
|
||||
// @version 3.1.5
|
||||
// @description Overlays tiles on wplace.live. Can also resize, and color-match your overlay to wplace's palette. Make sure to comply with the site's Terms of Service, and rules! This script is not affiliated with Wplace.live in any way, use at your own risk. This script is not affiliated with TamperMonkey. The author of this userscript is not responsible for any damages, issues, loss of data, or punishment that may occur as a result of using this script. This script is provided "as is" under GPLv3.
|
||||
// @author shinkonet
|
||||
// @match https://wplace.live/*
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ export function createUI() {
|
|||
<div class="op-mode-setting" data-setting="minify">
|
||||
<div class="op-row"><label>Style</label>
|
||||
<div class="op-row"><input type="radio" name="minify-style" value="dots" id="op-style-dots"><label for="op-style-dots">Dots</label></div>
|
||||
<div class="op-row"><input type="radio" name="minify-style" value="symbols" id="op-style-symbols"><label for="op-style-symbols">Symbols</label></div>
|
||||
<div class="op-row"><input type="radio" name="minify-style" value="symbols" id="op-style-symbols"><label for="op-style-symbols">Symbols (slow and buggy, wait 4 fix!)</label></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -94,6 +94,10 @@ export function injectStyles() {
|
|||
.op-toast { background: rgba(255,255,255,0.98); border: 1px solid #e6ebf2; color: #111827; padding: 8px 12px; border-radius: 10px; font-size: 12px; box-shadow: 0 6px 16px rgba(16,24,40,0.12); opacity: 0; transform: translateY(-6px); transition: opacity .18s ease, transform .18s ease; max-width: 100%; text-align: center; }
|
||||
.op-toast.show { opacity: 1; transform: translateY(0); }
|
||||
.op-toast-stack.op-dark .op-toast { background: rgba(27,30,36,0.98); border-color: #2a2f3a; color: #f5f6f9; }
|
||||
.op-toast.op-toast-error { background: #fee2e2; border-color: #fecaca; color: #7f1d1d; }
|
||||
.op-toast-stack.op-dark .op-toast.op-toast-error { background: #4a1f1f; border-color: #5b2d2d; color: #fecaca; }
|
||||
.op-toast.op-toast-success { background: #dcfce7; border-color: #bbf7d0; color: #14532d; }
|
||||
.op-toast-stack.op-dark .op-toast.op-toast-success { background: #163822; border-color: #225a35; color: #bbf7d0; }
|
||||
|
||||
.op-cc-backdrop { position: fixed; inset: 0; z-index: 10000; background: rgba(0,0,0,0.45); display: none; }
|
||||
.op-cc-backdrop.show { display: block; }
|
||||
|
|
|
|||
Loading…
Reference in a new issue