fix(entry): resolve floating trigger position, boundary clamping, and refresh flicker
This commit is contained in:
+5
-5
@@ -85,12 +85,12 @@ DOM or live LiteGraph state.
|
||||
- `model_policies.py` owns shared backend rename and protected-category policy.
|
||||
- `model_identity.py` owns file SHA-256 evidence shared with the standalone scanner.
|
||||
|
||||
### Frontend
|
||||
|
||||
- `web/main.js` coordinates extension registration. `browser.js` owns the shared
|
||||
- `web/main.js` coordinates extension registration (with `?v=...` versioned module imports busting aggressive browser ES Module caching and unconditional legacy storage key purging). `browser.js` owns the shared
|
||||
browser class and extracted-method wiring; `browser_entry.js` owns the single
|
||||
browser instance plus floating/topbar/menu entry behavior; and
|
||||
`interface_settings.js` owns language and theme preferences.
|
||||
browser instance plus floating/topbar/menu entry behavior (with Dual-Binding PointerEvents drag capture, `lostpointercapture` fail-safe listeners, zero-drift viewport boundary clamping, flicker-free pre-mount coordinate binding and `anomalous-trigger-initializing` smooth opacity fade reveal, default safe placement in the top-left canvas area at `top: 80px; left: 80px;` gracefully avoiding the left sidebar dock, and versioned `anomalous_trigger_pos_v3` atomic JSON coordinate persistence); `entry_controls.js`
|
||||
owns entry mode, trigger sizing/styling normalization, mathematical viewport-safe boundary
|
||||
clamping (`clampFloatingTriggerPosition` with minimum safe boundary `minX=70` preventing left sidebar dock entrapment), clean `loadSavedTriggerPosition`/`saveTriggerPosition` storage drivers, and non-distorting coordinate validation
|
||||
(`isValidSavedTriggerPosition`); `api/__init__.py` injects an aiohttp no-cache middleware (`Cache-Control: no-cache, no-store, must-revalidate`) for extension static files to eliminate browser memory/disk cache desynchronization across regular page refreshes; and `interface_settings.js` owns language and theme preferences.
|
||||
- `ui_sidebar.js` creates the browser shell and folder navigation.
|
||||
`ui_settings_hub.js` owns settings and model-card preferences;
|
||||
`ui_toolbox.js` owns the tool catalog, fixed shortcut bar, and tool dispatch;
|
||||
|
||||
@@ -8,7 +8,20 @@ from .config import *
|
||||
from .notebooks import *
|
||||
from .parameters import *
|
||||
|
||||
from aiohttp import web
|
||||
|
||||
@web.middleware
|
||||
async def no_cache_extension_middleware(request, handler):
|
||||
response = await handler(request)
|
||||
if request.path.startswith('/extensions/Anomalous_Model_Browser'):
|
||||
response.headers['Cache-Control'] = 'no-cache, no-store, must-revalidate, max-age=0'
|
||||
response.headers['Pragma'] = 'no-cache'
|
||||
response.headers['Expires'] = '0'
|
||||
return response
|
||||
|
||||
def setup_routes(app):
|
||||
if hasattr(app, 'middlewares') and no_cache_extension_middleware not in app.middlewares:
|
||||
app.middlewares.append(no_cache_extension_middleware)
|
||||
app.router.add_get('/anomalous/folders', model_catalog.api_get_folders)
|
||||
app.router.add_get('/anomalous/all_folder_types', folder_types.api_get_all_folder_types)
|
||||
app.router.add_get('/anomalous/models', model_catalog.api_get_models)
|
||||
|
||||
+8
-1
@@ -1,5 +1,12 @@
|
||||
try {
|
||||
if (typeof localStorage !== 'undefined') {
|
||||
localStorage.removeItem('anomalous_btn_x');
|
||||
localStorage.removeItem('anomalous_btn_y');
|
||||
}
|
||||
} catch (_) {}
|
||||
|
||||
import { app } from "../../scripts/app.js";
|
||||
import { createBrowserEntry } from "./modules/browser_entry.js";
|
||||
import { createBrowserEntry } from "./modules/browser_entry.js?v=20260921-flicker-free-1";
|
||||
import { createInterfaceSettings, getCurrentLanguage, setAbyssalScarletTheme, t } from "./modules/interface_settings.js";
|
||||
|
||||
export { setAbyssalScarletTheme };
|
||||
|
||||
+192
-89
@@ -1,11 +1,24 @@
|
||||
try {
|
||||
if (typeof localStorage !== 'undefined') {
|
||||
localStorage.removeItem('anomalous_btn_x');
|
||||
localStorage.removeItem('anomalous_btn_y');
|
||||
}
|
||||
} catch (_) {}
|
||||
|
||||
import { app } from '../../../scripts/app.js';
|
||||
import { AnomalousBrowser } from './browser.js';
|
||||
import {
|
||||
clampFloatingTriggerPosition,
|
||||
clearSavedTriggerPosition,
|
||||
DEFAULT_SAFE_LEFT,
|
||||
DEFAULT_SAFE_TOP,
|
||||
isValidSavedTriggerPosition,
|
||||
loadSavedTriggerPosition,
|
||||
normalizeEntryMode,
|
||||
normalizeFloatingTriggerSize,
|
||||
normalizeFloatingTriggerStyle
|
||||
} from './entry_controls.js';
|
||||
normalizeFloatingTriggerStyle,
|
||||
saveTriggerPosition
|
||||
} from './entry_controls.js?v=20260921-flicker-free-1';
|
||||
import {
|
||||
createShortcutSettingControl,
|
||||
DEFAULT_BROWSER_SHORTCUT,
|
||||
@@ -107,18 +120,18 @@ export function createBrowserEntry({ translate, getCurrentLanguage }) {
|
||||
triggerButton.replaceChildren(icon, label);
|
||||
triggerButton.title = t('mainOpenTitle');
|
||||
triggerButton.setAttribute('aria-label', t('mainOpenTitle'));
|
||||
requestAnimationFrame(() => triggerBoundsUpdater?.());
|
||||
if (loadSavedTriggerPosition()) {
|
||||
requestAnimationFrame(() => triggerBoundsUpdater?.());
|
||||
}
|
||||
}
|
||||
|
||||
function resetPosition() {
|
||||
localStorage.removeItem('anomalous_btn_x');
|
||||
localStorage.removeItem('anomalous_btn_y');
|
||||
clearSavedTriggerPosition();
|
||||
if (!triggerButton) return;
|
||||
triggerButton.style.left = '';
|
||||
triggerButton.style.top = '';
|
||||
triggerButton.style.right = '30px';
|
||||
triggerButton.style.bottom = '30px';
|
||||
requestAnimationFrame(() => triggerBoundsUpdater?.());
|
||||
triggerButton.style.right = '';
|
||||
triggerButton.style.bottom = '';
|
||||
}
|
||||
|
||||
function ensureBrowser() {
|
||||
@@ -241,7 +254,7 @@ export function createBrowserEntry({ translate, getCurrentLanguage }) {
|
||||
});
|
||||
|
||||
async function setup() {
|
||||
const cssUrl = '/extensions/Anomalous_Model_Browser/styles.css?v=' + Date.now();
|
||||
const cssUrl = '/extensions/Anomalous_Model_Browser/styles.css?v=20260921-flicker-free-1';
|
||||
if (!document.querySelector('link[href^="/extensions/Anomalous_Model_Browser/styles.css"]')) {
|
||||
const link = document.createElement('link');
|
||||
link.rel = 'stylesheet';
|
||||
@@ -250,86 +263,6 @@ export function createBrowserEntry({ translate, getCurrentLanguage }) {
|
||||
document.head.appendChild(link);
|
||||
}
|
||||
|
||||
const btn = document.createElement('button');
|
||||
btn.id = 'anomalous-trigger-btn';
|
||||
btn.setAttribute('aria-label', 'Anomalous Model Browser');
|
||||
triggerButton = btn;
|
||||
if (browserInstance) browserInstance.triggerButton = btn;
|
||||
btn.title = t('mainOpenTitle');
|
||||
let isDragging = false;
|
||||
let startX;
|
||||
let startY;
|
||||
let initialX;
|
||||
let initialY;
|
||||
|
||||
btn.addEventListener('mousedown', event => {
|
||||
isDragging = true;
|
||||
startX = event.clientX;
|
||||
startY = event.clientY;
|
||||
const rect = btn.getBoundingClientRect();
|
||||
initialX = rect.left;
|
||||
initialY = rect.top;
|
||||
btn.style.transition = 'none';
|
||||
});
|
||||
window.addEventListener('mousemove', event => {
|
||||
if (!isDragging) return;
|
||||
event.preventDefault();
|
||||
const nextPosition = clampFloatingTriggerPosition({
|
||||
x: initialX + (event.clientX - startX),
|
||||
y: initialY + (event.clientY - startY),
|
||||
width: btn.offsetWidth,
|
||||
height: btn.offsetHeight,
|
||||
viewportWidth: window.innerWidth,
|
||||
viewportHeight: window.innerHeight,
|
||||
margin: 0
|
||||
});
|
||||
btn.style.left = nextPosition.x + 'px';
|
||||
btn.style.top = nextPosition.y + 'px';
|
||||
btn.style.right = 'auto';
|
||||
btn.style.bottom = 'auto';
|
||||
});
|
||||
window.addEventListener('mouseup', event => {
|
||||
if (!isDragging) return;
|
||||
isDragging = false;
|
||||
btn.style.transition = 'transform 0.15s, box-shadow 0.15s';
|
||||
localStorage.setItem('anomalous_btn_x', btn.style.left);
|
||||
localStorage.setItem('anomalous_btn_y', btn.style.top);
|
||||
if (Math.abs(event.clientX - startX) < 5 && Math.abs(event.clientY - startY) < 5) open();
|
||||
});
|
||||
|
||||
let savedX = localStorage.getItem('anomalous_btn_x');
|
||||
let savedY = localStorage.getItem('anomalous_btn_y');
|
||||
const updateBtnBounds = () => {
|
||||
savedX = localStorage.getItem('anomalous_btn_x');
|
||||
savedY = localStorage.getItem('anomalous_btn_y');
|
||||
const position = clampFloatingTriggerPosition({
|
||||
x: btn.style.left || savedX,
|
||||
y: btn.style.top || savedY,
|
||||
width: btn.offsetWidth,
|
||||
height: btn.offsetHeight,
|
||||
viewportWidth: window.innerWidth,
|
||||
viewportHeight: window.innerHeight
|
||||
});
|
||||
btn.style.left = position.x + 'px';
|
||||
btn.style.top = position.y + 'px';
|
||||
btn.style.right = 'auto';
|
||||
btn.style.bottom = 'auto';
|
||||
};
|
||||
triggerBoundsUpdater = updateBtnBounds;
|
||||
|
||||
if (savedX && savedY && savedX !== 'NaN' && savedY !== 'NaN') {
|
||||
btn.style.right = 'auto';
|
||||
btn.style.bottom = 'auto';
|
||||
btn.style.left = savedX;
|
||||
btn.style.top = savedY;
|
||||
}
|
||||
setTimeout(updateBtnBounds, 200);
|
||||
window.addEventListener('resize', () => {
|
||||
if (btn.style.left) updateBtnBounds();
|
||||
syncVisibility();
|
||||
});
|
||||
|
||||
document.body.appendChild(btn);
|
||||
try {
|
||||
const registeredSettings = app.extensionManager?.setting;
|
||||
entryMode = normalizeEntryMode(registeredSettings?.get(ENTRY_MODE_SETTING_ID));
|
||||
@@ -338,11 +271,181 @@ export function createBrowserEntry({ translate, getCurrentLanguage }) {
|
||||
} catch (error) {
|
||||
console.warn('[Anomalous Model Browser] Unable to read entry preferences:', error);
|
||||
}
|
||||
|
||||
const btn = document.createElement('button');
|
||||
btn.id = 'anomalous-trigger-btn';
|
||||
btn.setAttribute('aria-label', 'Anomalous Model Browser');
|
||||
// Prevent FOUC: Start hidden until presentation & exact coordinates are bound
|
||||
btn.classList.add('anomalous-trigger-initializing');
|
||||
triggerButton = btn;
|
||||
if (browserInstance) browserInstance.triggerButton = btn;
|
||||
btn.title = t('mainOpenTitle');
|
||||
|
||||
// Pre-configure content & presentation BEFORE mounting
|
||||
applyPresentation();
|
||||
|
||||
// Pre-calculate and assign exact target coordinates BEFORE appending to DOM
|
||||
const savedInitialPos = loadSavedTriggerPosition();
|
||||
if (savedInitialPos) {
|
||||
const estSize = floatingTriggerSize === 'small' ? 44 : (floatingTriggerSize === 'large' ? 76 : 60);
|
||||
const initialClamped = clampFloatingTriggerPosition({
|
||||
x: savedInitialPos.x,
|
||||
y: savedInitialPos.y,
|
||||
width: estSize,
|
||||
height: estSize,
|
||||
viewportWidth: window.innerWidth,
|
||||
viewportHeight: window.innerHeight,
|
||||
minX: 70
|
||||
});
|
||||
btn.style.position = 'fixed';
|
||||
btn.style.left = initialClamped.x + 'px';
|
||||
btn.style.top = initialClamped.y + 'px';
|
||||
btn.style.right = 'auto';
|
||||
btn.style.bottom = 'auto';
|
||||
}
|
||||
|
||||
let isDragging = false;
|
||||
let hasMoved = false;
|
||||
let startPointerX = 0;
|
||||
let startPointerY = 0;
|
||||
let startBtnLeft = 0;
|
||||
let startBtnTop = 0;
|
||||
let currentDragX = null;
|
||||
let currentDragY = null;
|
||||
let activePointerId = null;
|
||||
|
||||
const onPointerMove = (event) => {
|
||||
if (!isDragging) return;
|
||||
const dx = event.clientX - startPointerX;
|
||||
const dy = event.clientY - startPointerY;
|
||||
if (!hasMoved && Math.hypot(dx, dy) >= 4) {
|
||||
hasMoved = true;
|
||||
}
|
||||
if (!hasMoved) return;
|
||||
event.preventDefault();
|
||||
const btnW = btn.offsetWidth || 60;
|
||||
const btnH = btn.offsetHeight || 60;
|
||||
const clamped = clampFloatingTriggerPosition({
|
||||
x: startBtnLeft + dx,
|
||||
y: startBtnTop + dy,
|
||||
width: btnW,
|
||||
height: btnH,
|
||||
viewportWidth: window.innerWidth,
|
||||
viewportHeight: window.innerHeight,
|
||||
minX: 70
|
||||
});
|
||||
currentDragX = clamped.x;
|
||||
currentDragY = clamped.y;
|
||||
btn.style.left = clamped.x + 'px';
|
||||
btn.style.top = clamped.y + 'px';
|
||||
btn.style.right = 'auto';
|
||||
btn.style.bottom = 'auto';
|
||||
};
|
||||
|
||||
const finishDrag = (event) => {
|
||||
if (!isDragging) return;
|
||||
isDragging = false;
|
||||
|
||||
btn.removeEventListener('pointermove', onPointerMove);
|
||||
btn.removeEventListener('pointerup', finishDrag);
|
||||
btn.removeEventListener('pointercancel', finishDrag);
|
||||
btn.removeEventListener('lostpointercapture', finishDrag);
|
||||
window.removeEventListener('pointermove', onPointerMove);
|
||||
window.removeEventListener('pointerup', finishDrag);
|
||||
window.removeEventListener('pointercancel', finishDrag);
|
||||
|
||||
if (activePointerId != null) {
|
||||
try {
|
||||
if (btn.hasPointerCapture(activePointerId)) {
|
||||
btn.releasePointerCapture(activePointerId);
|
||||
}
|
||||
} catch (_) {}
|
||||
activePointerId = null;
|
||||
}
|
||||
|
||||
btn.style.transition = '';
|
||||
|
||||
if (hasMoved && currentDragX != null && currentDragY != null) {
|
||||
saveTriggerPosition({ x: currentDragX, y: currentDragY });
|
||||
} else if (!hasMoved) {
|
||||
open();
|
||||
}
|
||||
};
|
||||
|
||||
btn.addEventListener('pointerdown', (event) => {
|
||||
if (event.button !== 0) return;
|
||||
isDragging = true;
|
||||
hasMoved = false;
|
||||
startPointerX = event.clientX;
|
||||
startPointerY = event.clientY;
|
||||
activePointerId = event.pointerId;
|
||||
|
||||
const rect = btn.getBoundingClientRect();
|
||||
startBtnLeft = rect.left;
|
||||
startBtnTop = rect.top;
|
||||
currentDragX = rect.left;
|
||||
currentDragY = rect.top;
|
||||
|
||||
btn.style.transition = 'none';
|
||||
|
||||
try {
|
||||
btn.setPointerCapture(event.pointerId);
|
||||
} catch (_) {}
|
||||
|
||||
btn.addEventListener('pointermove', onPointerMove, { passive: false });
|
||||
btn.addEventListener('pointerup', finishDrag);
|
||||
btn.addEventListener('pointercancel', finishDrag);
|
||||
btn.addEventListener('lostpointercapture', finishDrag);
|
||||
window.addEventListener('pointermove', onPointerMove, { passive: false });
|
||||
window.addEventListener('pointerup', finishDrag);
|
||||
window.addEventListener('pointercancel', finishDrag);
|
||||
});
|
||||
|
||||
const updateBtnBounds = () => {
|
||||
const saved = loadSavedTriggerPosition();
|
||||
if (saved) {
|
||||
const estFallback = floatingTriggerSize === 'small' ? 44 : (floatingTriggerSize === 'large' ? 76 : 60);
|
||||
const btnW = btn.offsetWidth || estFallback;
|
||||
const btnH = btn.offsetHeight || btnW;
|
||||
const clamped = clampFloatingTriggerPosition({
|
||||
x: saved.x,
|
||||
y: saved.y,
|
||||
width: btnW,
|
||||
height: btnH,
|
||||
viewportWidth: window.innerWidth,
|
||||
viewportHeight: window.innerHeight,
|
||||
minX: 70
|
||||
});
|
||||
btn.style.left = clamped.x + 'px';
|
||||
btn.style.top = clamped.y + 'px';
|
||||
btn.style.right = 'auto';
|
||||
btn.style.bottom = 'auto';
|
||||
} else {
|
||||
btn.style.left = '';
|
||||
btn.style.top = '';
|
||||
btn.style.right = '';
|
||||
btn.style.bottom = '';
|
||||
}
|
||||
};
|
||||
triggerBoundsUpdater = updateBtnBounds;
|
||||
|
||||
window.addEventListener('resize', () => {
|
||||
updateBtnBounds();
|
||||
syncVisibility();
|
||||
});
|
||||
|
||||
// Mount pre-positioned and styled element to DOM
|
||||
document.body.appendChild(btn);
|
||||
syncVisibility();
|
||||
ensureBrowser();
|
||||
installMaterialsShortcutFallback();
|
||||
|
||||
// Reveal smoothly on first animation frame without any positional jump or flicker
|
||||
requestAnimationFrame(() => {
|
||||
updateBtnBounds();
|
||||
btn.classList.remove('anomalous-trigger-initializing');
|
||||
});
|
||||
|
||||
window.anomalousDragGhostImg = new Image();
|
||||
window.anomalousDragGhostImg.src = "data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='80' height='80'><rect width='76' height='76' x='2' y='2' fill='%23140812' fill-opacity='0.85' rx='16' stroke='%23f59e0b' stroke-width='2'/><text x='40' y='50' font-family='sans-serif' font-size='32' font-weight='bold' fill='%23f59e0b' text-anchor='middle'>W</text></svg>";
|
||||
}
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
// Unconditionally purge legacy contaminated storage keys on module load
|
||||
try {
|
||||
if (typeof localStorage !== 'undefined') {
|
||||
localStorage.removeItem('anomalous_btn_x');
|
||||
localStorage.removeItem('anomalous_btn_y');
|
||||
}
|
||||
} catch (_) {}
|
||||
|
||||
export const FLOATING_TRIGGER_SIZES = new Set(['small', 'medium', 'large']);
|
||||
export const FLOATING_TRIGGER_STYLES = new Set(['icon', 'pill']);
|
||||
export const ENTRY_MODES = new Set(['floating', 'topbar', 'menu']);
|
||||
@@ -14,6 +22,74 @@ export function normalizeEntryMode(value) {
|
||||
return ENTRY_MODES.has(value) ? value : 'floating';
|
||||
}
|
||||
|
||||
export const DEFAULT_SAFE_TOP = 80;
|
||||
export const DEFAULT_SAFE_LEFT = 80; // Safe position outside ComfyUI left sidebar dock
|
||||
export const DEFAULT_SAFE_MARGIN_RIGHT = 24;
|
||||
export const DEFAULT_MIN_SAFE_X = 70; // Avoid ComfyUI left sidebar dock
|
||||
export const DEFAULT_TRIGGER_POSITION = Object.freeze({ top: DEFAULT_SAFE_TOP, left: DEFAULT_SAFE_LEFT });
|
||||
export const TRIGGER_POSITION_STORAGE_KEY = 'anomalous_trigger_pos_v3';
|
||||
export const LEGACY_TRIGGER_X_KEY = 'anomalous_btn_x';
|
||||
export const LEGACY_TRIGGER_Y_KEY = 'anomalous_btn_y';
|
||||
|
||||
export function isValidSavedTriggerPosition(x, y) {
|
||||
if (x == null || y == null) return false;
|
||||
const px = Number.parseFloat(x);
|
||||
const py = Number.parseFloat(y);
|
||||
// Must be a finite number, py >= 0, and px >= DEFAULT_MIN_SAFE_X (70px) to prevent docking inside sidebar
|
||||
return Number.isFinite(px) && Number.isFinite(py) && px >= DEFAULT_MIN_SAFE_X && py >= 0;
|
||||
}
|
||||
|
||||
export function loadSavedTriggerPosition(storage = (typeof localStorage !== 'undefined' ? localStorage : null)) {
|
||||
if (!storage) return null;
|
||||
try {
|
||||
const raw = storage.getItem(TRIGGER_POSITION_STORAGE_KEY);
|
||||
if (raw) {
|
||||
const parsed = JSON.parse(raw);
|
||||
if (isValidSavedTriggerPosition(parsed?.x, parsed?.y)) {
|
||||
return { x: Math.round(Number(parsed.x)), y: Math.round(Number(parsed.y)) };
|
||||
} else {
|
||||
storage.removeItem(TRIGGER_POSITION_STORAGE_KEY);
|
||||
}
|
||||
}
|
||||
} catch (_) {}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function saveTriggerPosition(pos, storage = (typeof localStorage !== 'undefined' ? localStorage : null)) {
|
||||
if (!storage || !pos) return;
|
||||
const x = Number.parseFloat(pos.x);
|
||||
const y = Number.parseFloat(pos.y);
|
||||
if (!isValidSavedTriggerPosition(x, y)) return;
|
||||
try {
|
||||
storage.setItem(TRIGGER_POSITION_STORAGE_KEY, JSON.stringify({
|
||||
x: Math.round(x),
|
||||
y: Math.round(y)
|
||||
}));
|
||||
storage.removeItem(LEGACY_TRIGGER_X_KEY);
|
||||
storage.removeItem(LEGACY_TRIGGER_Y_KEY);
|
||||
storage.removeItem('anomalous_trigger_pos_v2');
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
export function clearSavedTriggerPosition(storage = (typeof localStorage !== 'undefined' ? localStorage : null)) {
|
||||
if (!storage) return;
|
||||
try {
|
||||
storage.removeItem(TRIGGER_POSITION_STORAGE_KEY);
|
||||
storage.removeItem(LEGACY_TRIGGER_X_KEY);
|
||||
storage.removeItem(LEGACY_TRIGGER_Y_KEY);
|
||||
storage.removeItem('anomalous_trigger_pos_v2');
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
export function sanitizeSavedTriggerPosition(x, y) {
|
||||
if (!isValidSavedTriggerPosition(x, y)) return null;
|
||||
return { x: Number.parseFloat(x), y: Number.parseFloat(y) };
|
||||
}
|
||||
|
||||
export function normalizeSavedTriggerPosition(x, y) {
|
||||
return sanitizeSavedTriggerPosition(x, y);
|
||||
}
|
||||
|
||||
export function clampFloatingTriggerPosition({
|
||||
x,
|
||||
y,
|
||||
@@ -21,19 +97,29 @@ export function clampFloatingTriggerPosition({
|
||||
height,
|
||||
viewportWidth,
|
||||
viewportHeight,
|
||||
margin = 30
|
||||
minX = DEFAULT_MIN_SAFE_X,
|
||||
margin = 0
|
||||
}) {
|
||||
const safeWidth = Math.max(1, Number(width) || 60);
|
||||
const safeHeight = Math.max(1, Number(height) || 60);
|
||||
const maxX = Math.max(0, (Number(viewportWidth) || safeWidth) - safeWidth);
|
||||
const maxY = Math.max(0, (Number(viewportHeight) || safeHeight) - safeHeight);
|
||||
const fallbackX = Math.max(0, maxX - margin);
|
||||
const fallbackY = Math.max(0, maxY - margin);
|
||||
const rawVw = Number(viewportWidth) || (typeof window !== 'undefined' ? window.innerWidth : 1024) || 1024;
|
||||
const rawVh = Number(viewportHeight) || (typeof window !== 'undefined' ? window.innerHeight : 768) || 768;
|
||||
const safeVw = rawVw >= 300 ? rawVw : 1024;
|
||||
const safeVh = rawVh >= 200 ? rawVh : 768;
|
||||
const maxX = Math.max(minX, safeVw - safeWidth);
|
||||
const maxY = Math.max(0, safeVh - safeHeight);
|
||||
|
||||
const parsedX = Number.parseFloat(x);
|
||||
const parsedY = Number.parseFloat(y);
|
||||
|
||||
const fallbackX = Math.max(minX, Math.min(maxX, DEFAULT_SAFE_LEFT));
|
||||
const fallbackY = Math.min(maxY, DEFAULT_SAFE_TOP);
|
||||
|
||||
const targetX = Number.isFinite(parsedX) ? parsedX : fallbackX;
|
||||
const targetY = Number.isFinite(parsedY) ? parsedY : fallbackY;
|
||||
|
||||
return {
|
||||
x: Math.min(maxX, Math.max(0, Number.isFinite(parsedX) ? parsedX : fallbackX)),
|
||||
y: Math.min(maxY, Math.max(0, Number.isFinite(parsedY) ? parsedY : fallbackY))
|
||||
x: Math.min(maxX, Math.max(minX, Math.round(targetX))),
|
||||
y: Math.min(maxY, Math.max(0, Math.round(targetY)))
|
||||
};
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
@import url("./styles/00-foundation-models.css?v=20260918-model-sources-1");
|
||||
@import url("./styles/00-foundation-models.css?v=20260921-flicker-free-1");
|
||||
@import url("./styles/01-shell-tools.css?v=20260918-model-sources-1");
|
||||
@import url("./styles/02-notebooks.css?v=20260918-model-sources-1");
|
||||
@import url("./styles/03-gallery-recipes.css?v=20260918-model-sources-1");
|
||||
|
||||
@@ -77,8 +77,10 @@ html.theme-abyssal-scarlet,
|
||||
--anomalous-trigger-font-size: 28px;
|
||||
--anomalous-trigger-pill-padding: 18px;
|
||||
position: fixed;
|
||||
bottom: 30px;
|
||||
right: 30px;
|
||||
top: 80px;
|
||||
left: 80px;
|
||||
right: auto;
|
||||
bottom: auto;
|
||||
width: var(--anomalous-trigger-size);
|
||||
height: var(--anomalous-trigger-size);
|
||||
border-radius: 50%;
|
||||
@@ -93,15 +95,20 @@ html.theme-abyssal-scarlet,
|
||||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45), inset 0 1px 0 rgba(255, 255, 255, 0.12);
|
||||
display: flex !important;
|
||||
visibility: visible !important;
|
||||
opacity: 1 !important;
|
||||
opacity: 1;
|
||||
align-items: center !important;
|
||||
justify-content: center !important;
|
||||
user-select: none;
|
||||
transition: all 0.25s cubic-bezier(0.16, 1, 0.3, 1);
|
||||
transition: opacity 0.18s ease-out, transform 0.2s cubic-bezier(0.16, 1, 0.3, 1), box-shadow 0.2s ease, background 0.2s ease, border-color 0.2s ease;
|
||||
gap: 8px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#anomalous-trigger-btn.anomalous-trigger-initializing {
|
||||
opacity: 0 !important;
|
||||
pointer-events: none !important;
|
||||
}
|
||||
|
||||
#anomalous-trigger-btn .anomalous-trigger-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -195,12 +202,30 @@ body:has(.anomalous-prompt-studio-drawer) #anomalous-trigger-btn {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.anomalous-topbar-entry {
|
||||
.anomalous-topbar-entry,
|
||||
button:has(.anomalous-topbar-entry),
|
||||
[data-testid="anomalous-topbar-entry"],
|
||||
.side-bar-button:has(.anomalous-topbar-entry) {
|
||||
display: none !important;
|
||||
visibility: hidden !important;
|
||||
width: 0 !important;
|
||||
height: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border: none !important;
|
||||
pointer-events: none !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
|
||||
html.anomalous-topbar-entry-enabled .anomalous-topbar-entry {
|
||||
html.anomalous-topbar-entry-enabled .anomalous-topbar-entry,
|
||||
html.anomalous-topbar-entry-enabled button:has(.anomalous-topbar-entry),
|
||||
html.anomalous-topbar-entry-enabled .side-bar-button:has(.anomalous-topbar-entry) {
|
||||
display: inline-flex !important;
|
||||
visibility: visible !important;
|
||||
width: auto !important;
|
||||
height: auto !important;
|
||||
pointer-events: auto !important;
|
||||
overflow: visible !important;
|
||||
}
|
||||
|
||||
html:not(.anomalous-floating-entry-enabled) [data-setting-id="Anomalous.ModelBrowser.FloatingTriggerSize"],
|
||||
|
||||
Reference in New Issue
Block a user