Compare commits
3 Commits
e651e51c50
...
b62f6feaea
| Author | SHA1 | Date | |
|---|---|---|---|
| b62f6feaea | |||
| b6dec385dc | |||
| a12581ed0e |
@ -9,9 +9,17 @@ import { MenuPosition as MenuPosition } from '@src/common/interfaces/ClientUiMen
|
|||||||
import { CommandInterface } from '@src/common/interfaces/SettingsInterface';
|
import { CommandInterface } from '@src/common/interfaces/SettingsInterface';
|
||||||
import { SiteSupportLevel } from '@src/common/enums/SiteSupportLevel.enum';
|
import { SiteSupportLevel } from '@src/common/enums/SiteSupportLevel.enum';
|
||||||
import { ComponentLogger } from '../logging/ComponentLogger';
|
import { ComponentLogger } from '../logging/ComponentLogger';
|
||||||
|
import { setupVideoAlignmentIndicatorInteraction } from '@ui/utils/video-alignment-indicator-handling';
|
||||||
|
|
||||||
import alignmentIndicatorSvg from '!!raw-loader!@ui/res/img/alignment-indicators.svg';
|
import alignmentIndicatorSvg from '!!raw-loader!@ui/res/img/alignment-indicators.svg';
|
||||||
import lockBarIndicatorSvg from '!!raw-loader!@ui/res/img/lock-bar-indicators.svg';
|
import lockBarIndicatorSvg from '!!raw-loader!@ui/res/img/lock-bar-indicators.svg';
|
||||||
|
import VideoAlignmentType from '../../../common/enums/VideoAlignmentType.enum';
|
||||||
|
import { setVideoAlignmentIndicatorState } from '../../../ui/utils/video-alignment-indicator-handling';
|
||||||
|
import { UwuiWindow } from './UwuiWindow';
|
||||||
|
|
||||||
|
import { createApp } from 'vue';
|
||||||
|
import SettingsWindowContent from '@components/SettingsWindowContent.vue';
|
||||||
|
// import jsonEditorCSS from 'vanilla-jsoneditor/themes/jse-theme-dark.css?inline'
|
||||||
|
|
||||||
if (process.env.CHANNEL !== 'stable'){
|
if (process.env.CHANNEL !== 'stable'){
|
||||||
console.info("Loading: UI");
|
console.info("Loading: UI");
|
||||||
@ -31,6 +39,18 @@ class UI {
|
|||||||
private extensionMenu: ClientMenu;
|
private extensionMenu: ClientMenu;
|
||||||
private logger: ComponentLogger;
|
private logger: ComponentLogger;
|
||||||
|
|
||||||
|
private uiState = {
|
||||||
|
lockXY: true,
|
||||||
|
zoom: { // log2 scale — 100% is 0
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
},
|
||||||
|
videoAlignment: {
|
||||||
|
x: VideoAlignmentType.Default,
|
||||||
|
y: VideoAlignmentType.Default
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public interfaceId,
|
public interfaceId,
|
||||||
public uiConfig, // {parentElement?, eventBus?, isGlobal?, playerData}
|
public uiConfig, // {parentElement?, eventBus?, isGlobal?, playerData}
|
||||||
@ -239,7 +259,7 @@ class UI {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- X/Y LOCK, LINK BAR EDITION -->
|
<!-- X/Y LOCK, LINK BAR EDITION -->
|
||||||
<div class="uw-slider-lock-bar-container uw-linked">
|
<div id="slider-lock" class="uw-slider-lock-bar-container uw-linked">
|
||||||
${lockBarIndicatorSvg}
|
${lockBarIndicatorSvg}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -248,8 +268,8 @@ class UI {
|
|||||||
id="_button_toggle_aspect_lock"
|
id="_button_toggle_aspect_lock"
|
||||||
class="uw-freeform-zoom-button uw-link-button uw-linked"
|
class="uw-freeform-zoom-button uw-link-button uw-linked"
|
||||||
>
|
>
|
||||||
<svg id="_button_toggle_aspect_lock_locked" class="uw-linked" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><title>link-variant</title><path d="M10.59,13.41C11,13.8 11,14.44 10.59,14.83C10.2,15.22 9.56,15.22 9.17,14.83C7.22,12.88 7.22,9.71 9.17,7.76V7.76L12.71,4.22C14.66,2.27 17.83,2.27 19.78,4.22C21.73,6.17 21.73,9.34 19.78,11.29L18.29,12.78C18.3,11.96 18.17,11.14 17.89,10.36L18.36,9.88C19.54,8.71 19.54,6.81 18.36,5.64C17.19,4.46 15.29,4.46 14.12,5.64L10.59,9.17C9.41,10.34 9.41,12.24 10.59,13.41M13.41,9.17C13.8,8.78 14.44,8.78 14.83,9.17C16.78,11.12 16.78,14.29 14.83,16.24V16.24L11.29,19.78C9.34,21.73 6.17,21.73 4.22,19.78C2.27,17.83 2.27,14.66 4.22,12.71L5.71,11.22C5.7,12.04 5.83,12.86 6.11,13.65L5.64,14.12C4.46,15.29 4.46,17.19 5.64,18.36C6.81,19.54 8.71,19.54 9.88,18.36L13.41,14.83C14.59,13.66 14.59,11.76 13.41,10.59C13,10.2 13,9.56 13.41,9.17Z" /></svg>
|
<svg id="_button_toggle_aspect_lock_locked" class="svg-icon uw-linked" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><title>link-variant</title><path d="M10.59,13.41C11,13.8 11,14.44 10.59,14.83C10.2,15.22 9.56,15.22 9.17,14.83C7.22,12.88 7.22,9.71 9.17,7.76V7.76L12.71,4.22C14.66,2.27 17.83,2.27 19.78,4.22C21.73,6.17 21.73,9.34 19.78,11.29L18.29,12.78C18.3,11.96 18.17,11.14 17.89,10.36L18.36,9.88C19.54,8.71 19.54,6.81 18.36,5.64C17.19,4.46 15.29,4.46 14.12,5.64L10.59,9.17C9.41,10.34 9.41,12.24 10.59,13.41M13.41,9.17C13.8,8.78 14.44,8.78 14.83,9.17C16.78,11.12 16.78,14.29 14.83,16.24V16.24L11.29,19.78C9.34,21.73 6.17,21.73 4.22,19.78C2.27,17.83 2.27,14.66 4.22,12.71L5.71,11.22C5.7,12.04 5.83,12.86 6.11,13.65L5.64,14.12C4.46,15.29 4.46,17.19 5.64,18.36C6.81,19.54 8.71,19.54 9.88,18.36L13.41,14.83C14.59,13.66 14.59,11.76 13.41,10.59C13,10.2 13,9.56 13.41,9.17Z" /></svg>
|
||||||
<svg id="_button_toggle_aspect_lock_unlocked" class="uw-unlinked" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><title>link-variant-off</title><path d="M2,5.27L3.28,4L20,20.72L18.73,22L13.9,17.17L11.29,19.78C9.34,21.73 6.17,21.73 4.22,19.78C2.27,17.83 2.27,14.66 4.22,12.71L5.71,11.22C5.7,12.04 5.83,12.86 6.11,13.65L5.64,14.12C4.46,15.29 4.46,17.19 5.64,18.36C6.81,19.54 8.71,19.54 9.88,18.36L12.5,15.76L10.88,14.15C10.87,14.39 10.77,14.64 10.59,14.83C10.2,15.22 9.56,15.22 9.17,14.83C8.12,13.77 7.63,12.37 7.72,11L2,5.27M12.71,4.22C14.66,2.27 17.83,2.27 19.78,4.22C21.73,6.17 21.73,9.34 19.78,11.29L18.29,12.78C18.3,11.96 18.17,11.14 17.89,10.36L18.36,9.88C19.54,8.71 19.54,6.81 18.36,5.64C17.19,4.46 15.29,4.46 14.12,5.64L10.79,8.97L9.38,7.55L12.71,4.22M13.41,9.17C13.8,8.78 14.44,8.78 14.83,9.17C16.2,10.54 16.61,12.5 16.06,14.23L14.28,12.46C14.23,11.78 13.94,11.11 13.41,10.59C13,10.2 13,9.56 13.41,9.17Z" /></svg>
|
<svg id="_button_toggle_aspect_lock_unlocked" class="svg-icon uw-unlinked" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><title>link-variant-off</title><path d="M2,5.27L3.28,4L20,20.72L18.73,22L13.9,17.17L11.29,19.78C9.34,21.73 6.17,21.73 4.22,19.78C2.27,17.83 2.27,14.66 4.22,12.71L5.71,11.22C5.7,12.04 5.83,12.86 6.11,13.65L5.64,14.12C4.46,15.29 4.46,17.19 5.64,18.36C6.81,19.54 8.71,19.54 9.88,18.36L12.5,15.76L10.88,14.15C10.87,14.39 10.77,14.64 10.59,14.83C10.2,15.22 9.56,15.22 9.17,14.83C8.12,13.77 7.63,12.37 7.72,11L2,5.27M12.71,4.22C14.66,2.27 17.83,2.27 19.78,4.22C21.73,6.17 21.73,9.34 19.78,11.29L18.29,12.78C18.3,11.96 18.17,11.14 17.89,10.36L18.36,9.88C19.54,8.71 19.54,6.81 18.36,5.64C17.19,4.46 15.29,4.46 14.12,5.64L10.79,8.97L9.38,7.55L12.71,4.22M13.41,9.17C13.8,8.78 14.44,8.78 14.83,9.17C16.2,10.54 16.61,12.5 16.06,14.23L14.28,12.46C14.23,11.78 13.94,11.11 13.41,10.59C13,10.2 13,9.56 13.41,9.17Z" /></svg>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -258,7 +278,7 @@ class UI {
|
|||||||
id="_button_reset_zoom"
|
id="_button_reset_zoom"
|
||||||
class="uw-freeform-zoom-button"
|
class="uw-freeform-zoom-button"
|
||||||
>
|
>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><title>restore</title><path d="M13,3A9,9 0 0,0 4,12H1L4.89,15.89L4.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,3Z" /></svg>
|
<svg class="svg-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><title>restore</title><path d="M13,3A9,9 0 0,0 4,12H1L4.89,15.89L4.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,3Z" /></svg>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -268,20 +288,142 @@ class UI {
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Align',
|
label: 'Align video to ...',
|
||||||
subitems: [{label: 'todo pls'}]
|
subitems: [{
|
||||||
|
customId: 'videoAlignmentController',
|
||||||
|
customClassList: '',
|
||||||
|
customHTML: `<div class="svg-icon">
|
||||||
|
${alignmentIndicatorSvg}
|
||||||
|
</div>`,
|
||||||
|
}]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'todo: open settings'
|
label: 'Extension settings',
|
||||||
|
action: () => this.createSettingsWindow(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'todo: first-time "customize/hide menu" option'
|
label: 'Cropping issues?',
|
||||||
|
action: () => this.createSettingsWindow('ui-settings'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Report a problem',
|
||||||
|
action: () => this.createSettingsWindow('about'),
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
this.extensionMenu = new ClientMenu(menuConfig);
|
this.extensionMenu = new ClientMenu(menuConfig);
|
||||||
this.extensionMenu.mount(this.uiConfig.parentElement);
|
this.extensionMenu.mount(this.uiConfig.parentElement);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SETUP MENU INTERACTIONS
|
||||||
|
* ———————————————————————————————————
|
||||||
|
* Interactions are needed for the following things:
|
||||||
|
* 0. [ ] both sliders. Needs to read the state of the sliders and update the labels
|
||||||
|
* 1. [ ] lock X/Y button — needs to update icon state of the button and the linked bar
|
||||||
|
* 2. [ ] reset button — just needs to reset, no icon changes necessary
|
||||||
|
* 3. [X] alignment indicator — also needs to update indicator state
|
||||||
|
*/
|
||||||
|
const menuElement = this.extensionMenu.root;
|
||||||
|
|
||||||
|
// 0. SLIDERS —————————————————————————————————————————————————————————————————————————————
|
||||||
|
const zoomWidthSlider: HTMLInputElement = menuElement.querySelector('#_input_zoom_slider');
|
||||||
|
const zoomHeightSlider: HTMLInputElement = menuElement.querySelector('#_input_zoom_slider_2');
|
||||||
|
const zoomWidthLabel: HTMLDivElement = menuElement.querySelector('#zoomWidth');
|
||||||
|
const zoomHeightLabel: HTMLDivElement = menuElement.querySelector('#zoomHeight');
|
||||||
|
|
||||||
|
const updateZoomDisplayValues = () => {
|
||||||
|
zoomWidthLabel.textContent = `${Math.round((Math.exp(this.uiState.zoom.x) * 100))}%`;
|
||||||
|
zoomHeightLabel.textContent = `${Math.round((Math.exp(this.uiState.zoom.y) * 100))}%`;
|
||||||
|
zoomWidthSlider.value = this.uiState.zoom.x.toString();
|
||||||
|
zoomHeightSlider.value = this.uiState.zoom.y.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const updateZoom = (event: InputEvent, axis: 'x' | 'y') => {
|
||||||
|
const parsedValue = parseFloat((event.currentTarget as HTMLInputElement).value);
|
||||||
|
|
||||||
|
if (this.uiState.lockXY) {
|
||||||
|
this.uiState.zoom = {x: parsedValue, y: parsedValue};
|
||||||
|
} else {
|
||||||
|
this.uiState.zoom[axis] = parsedValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateZoomDisplayValues();
|
||||||
|
|
||||||
|
this.eventBus?.send('set-zoom', {
|
||||||
|
zoom: {
|
||||||
|
x: Math.pow(2, this.uiState.zoom.x),
|
||||||
|
y: Math.pow(2, this.uiState.zoom.y)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
zoomWidthSlider.addEventListener('input', (event: InputEvent) => updateZoom(event, 'x'));
|
||||||
|
zoomHeightSlider.addEventListener('input', (event: InputEvent) => updateZoom(event, 'y'));
|
||||||
|
|
||||||
|
// 1. LOCK X/Y ————————————————————————————————————————————————————————————————————————————
|
||||||
|
const lockXYButton = menuElement.querySelector('#_button_toggle_aspect_lock');
|
||||||
|
const sliderLockBar = menuElement.querySelector('#slider-lock');
|
||||||
|
lockXYButton.addEventListener('click', () => {
|
||||||
|
this.uiState.lockXY = !this.uiState.lockXY;
|
||||||
|
|
||||||
|
if (this.uiState.lockXY) {
|
||||||
|
lockXYButton.classList.add('uw-linked');
|
||||||
|
lockXYButton.classList.remove('uw-unlinked');
|
||||||
|
sliderLockBar.classList.add('uw-linked');
|
||||||
|
sliderLockBar.classList.remove('uw-unlinked');
|
||||||
|
} else {
|
||||||
|
lockXYButton.classList.add('uw-unlinked');
|
||||||
|
lockXYButton.classList.remove('uw-linked');
|
||||||
|
sliderLockBar.classList.add('uw-unlinked');
|
||||||
|
sliderLockBar.classList.remove('uw-linked');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 2. ZOOM RESET BUTTON ———————————————————————————————————————————————————————————————————
|
||||||
|
const zoomResetButton = menuElement.querySelector('#_button_reset_zoom');
|
||||||
|
zoomResetButton.addEventListener('click', () => {
|
||||||
|
this.eventBus?.send('set-zoom', { zoom: {x: 1, y: 1} });
|
||||||
|
this.uiState.zoom = {x: 0, y: 0};
|
||||||
|
updateZoomDisplayValues();
|
||||||
|
});
|
||||||
|
|
||||||
|
// 3. VIDEO ALIGNMENT INDICATOR ———————————————————————————————————————————————————————————
|
||||||
|
const videoAlignmentIndicatorElement: SVGSVGElement = menuElement.querySelector('#_uw_ui_alignment_indicator');
|
||||||
|
setupVideoAlignmentIndicatorInteraction(
|
||||||
|
videoAlignmentIndicatorElement,
|
||||||
|
(x: VideoAlignmentType, y: VideoAlignmentType) => {
|
||||||
|
setVideoAlignmentIndicatorState(videoAlignmentIndicatorElement, x, y);
|
||||||
|
if (this.eventBus) {
|
||||||
|
this.eventBus?.send('set-alignment', {x,y});
|
||||||
|
this.uiState.videoAlignment = {x,y};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
createSettingsWindow(path?: string) {
|
||||||
|
const iframe = document.createElement('iframe');
|
||||||
|
|
||||||
|
iframe.src = chrome.runtime.getURL(`ui/pages/settings/index.html#ui${path ? `/${path}` : ''}`);
|
||||||
|
iframe.setAttribute('allowtransparency', 'true');
|
||||||
|
Object.assign(iframe.style, {
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
border: 'none',
|
||||||
|
background: 'transparent', // important
|
||||||
|
});
|
||||||
|
|
||||||
|
new UwuiWindow({
|
||||||
|
title: 'Ultrawidify settings (iframe)',
|
||||||
|
width: 1200,
|
||||||
|
height: 800,
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
content: iframe,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
setUiVisibility(visible) {
|
setUiVisibility(visible) {
|
||||||
|
|||||||
336
src/ext/lib/uwui/UwuiWindow.ts
Normal file
336
src/ext/lib/uwui/UwuiWindow.ts
Normal file
@ -0,0 +1,336 @@
|
|||||||
|
import extensionCss from '@src/main.css?inline';
|
||||||
|
|
||||||
|
|
||||||
|
type WindowContent = string | HTMLElement;
|
||||||
|
type WindowBackgroundMode = 'transparent' | 'blur' | 'solid';
|
||||||
|
|
||||||
|
interface WindowOptions {
|
||||||
|
title?: string;
|
||||||
|
content: WindowContent;
|
||||||
|
background?: WindowBackgroundMode;
|
||||||
|
width?: number;
|
||||||
|
height?: number;
|
||||||
|
x?: number;
|
||||||
|
y?: number;
|
||||||
|
extraStyles?: string[];
|
||||||
|
onFocus?: () => void;
|
||||||
|
onClose?: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class UwuiWindow {
|
||||||
|
private static baseZ = 1000000;
|
||||||
|
private static instances: UwuiWindow[] = [];
|
||||||
|
|
||||||
|
private host: HTMLDivElement;
|
||||||
|
private shadow: ShadowRoot;
|
||||||
|
private windowEl: HTMLDivElement;
|
||||||
|
private titleBar: HTMLDivElement;
|
||||||
|
|
||||||
|
private dragging = false;
|
||||||
|
private resizing = false;
|
||||||
|
private dragOffsetX = 0;
|
||||||
|
private dragOffsetY = 0;
|
||||||
|
|
||||||
|
private background: WindowBackgroundMode = 'blur';
|
||||||
|
private extraStyles?: string[];
|
||||||
|
|
||||||
|
private onFocus?: () => void;
|
||||||
|
private onClose?: () => void;
|
||||||
|
|
||||||
|
|
||||||
|
constructor(options: WindowOptions) {
|
||||||
|
this.onFocus = options.onFocus;
|
||||||
|
this.onClose = options.onClose;
|
||||||
|
|
||||||
|
this.host = document.createElement('div');
|
||||||
|
this.host.style.position = 'fixed';
|
||||||
|
this.host.style.inset = '0';
|
||||||
|
this.host.style.pointerEvents = 'none';
|
||||||
|
|
||||||
|
this.background = options.background ?? 'blur';
|
||||||
|
this.extraStyles = options.extraStyles;
|
||||||
|
|
||||||
|
document.body.appendChild(this.host);
|
||||||
|
|
||||||
|
this.shadow = this.host.attachShadow({ mode: 'closed' });
|
||||||
|
this.shadow.innerHTML = this.template(options);
|
||||||
|
|
||||||
|
|
||||||
|
this.windowEl = this.shadow.querySelector('.uw-window')!;
|
||||||
|
this.titleBar = this.shadow.querySelector('.uw-titlebar')!;
|
||||||
|
|
||||||
|
this.setGeometry(
|
||||||
|
options.x ?? 100,
|
||||||
|
options.y ?? 100,
|
||||||
|
options.width ?? 320,
|
||||||
|
options.height ?? 220
|
||||||
|
);
|
||||||
|
|
||||||
|
this.injectContent(options.content);
|
||||||
|
this.bindEvents();
|
||||||
|
|
||||||
|
UwuiWindow.instances.unshift(this);
|
||||||
|
UwuiWindow.recomputeZIndices();
|
||||||
|
|
||||||
|
this.applyBackground();
|
||||||
|
|
||||||
|
this.bindBackgroundMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets window stacking order
|
||||||
|
* @param order stacking order (0 = topmost)
|
||||||
|
*/
|
||||||
|
setOrder(order: number) {
|
||||||
|
const list = UwuiWindow.instances;
|
||||||
|
|
||||||
|
// Remove this window from current position
|
||||||
|
const currentIndex = list.indexOf(this);
|
||||||
|
if (currentIndex !== -1) {
|
||||||
|
list.splice(currentIndex, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clamp target index
|
||||||
|
const clamped = Math.max(0, Math.min(order, list.length));
|
||||||
|
|
||||||
|
// Insert at new position
|
||||||
|
list.splice(clamped, 0, this);
|
||||||
|
|
||||||
|
// Re-assign z-indexes
|
||||||
|
UwuiWindow.recomputeZIndices();
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy() {
|
||||||
|
this.onClose?.();
|
||||||
|
|
||||||
|
const list = UwuiWindow.instances;
|
||||||
|
const index = list.indexOf(this);
|
||||||
|
if (index !== -1) {
|
||||||
|
list.splice(index, 1);
|
||||||
|
UwuiWindow.recomputeZIndices();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.host.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static recomputeZIndices() {
|
||||||
|
const base = UwuiWindow.baseZ;
|
||||||
|
|
||||||
|
UwuiWindow.instances.forEach((win, index) => {
|
||||||
|
// index 0 = topmost → highest z-index
|
||||||
|
win.host.style.zIndex = String(base + (UwuiWindow.instances.length - index));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private bringToFront = () => {
|
||||||
|
this.setOrder(0);
|
||||||
|
this.onFocus?.();
|
||||||
|
};
|
||||||
|
|
||||||
|
//#region setup
|
||||||
|
private template(options: WindowOptions): string {
|
||||||
|
|
||||||
|
return `
|
||||||
|
<style>
|
||||||
|
${this.generateStyles()}
|
||||||
|
|
||||||
|
${this.extraStyles?.join('\n')}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div class="uw-window">
|
||||||
|
<div class="uw-titlebar">
|
||||||
|
<span class="uw-title">${options.title ?? ''}</span>
|
||||||
|
|
||||||
|
<div class="uw-actions">
|
||||||
|
<div class="uw-bg-toggle">
|
||||||
|
<!-- YOUR SVG ICON GOES HERE -->
|
||||||
|
<button type="button" class="uw-bg-button">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><title>layers-outline</title><path d="M12,18.54L19.37,12.8L21,14.07L12,21.07L3,14.07L4.62,12.81L12,18.54M12,16L3,9L12,2L21,9L12,16M12,4.53L6.26,9L12,13.47L17.74,9L12,4.53Z" /></svg>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div class="uw-bg-menu">
|
||||||
|
<button data-bg="transparent">Transparent</button>
|
||||||
|
<button data-bg="blur">Blur</button>
|
||||||
|
<button data-bg="solid">Solid</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="button" class="uw-close">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><title>close</title><path d="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z" /></svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="uw-content"></div>
|
||||||
|
<div class="uw-resize-handle"></div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private generateStyles() {
|
||||||
|
let css = extensionCss
|
||||||
|
.trim()
|
||||||
|
.replace(/'html, body \{/g, ':host {')
|
||||||
|
.replace(/'body \{/g, ':host {')
|
||||||
|
;
|
||||||
|
|
||||||
|
// this is bad but I can't be bothered to do it the proper way
|
||||||
|
const cssArr: string[] = css.split('@font-face');
|
||||||
|
const cssRemainder = cssArr[cssArr.length - 1].split('}').slice(1).join('}');
|
||||||
|
|
||||||
|
css = `
|
||||||
|
${cssArr[0]}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Heebo';
|
||||||
|
src: url(__FONT_HEEBO__) format('truetype-variations');
|
||||||
|
font-weight: 100 900;
|
||||||
|
font-stretch: 75% 125%;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Code Pro';
|
||||||
|
src: url(__FONT_SCP__) format('truetype-variations');
|
||||||
|
font-weight: 200 900;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Code Pro';
|
||||||
|
src: url(__FONT_SCPI__) format('truetype-variations');
|
||||||
|
font-weight: 200 900;
|
||||||
|
font-style: italic;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
${cssRemainder};
|
||||||
|
`;
|
||||||
|
|
||||||
|
return css
|
||||||
|
.replace('__FONT_HEEBO__', chrome.runtime.getURL('/ui/res/fonts/Heebo.ttf'))
|
||||||
|
.replace('__FONT_SCP__', chrome.runtime.getURL('/ui/res/fonts/SourceCodePro.ttf'))
|
||||||
|
.replace('__FONT_SCPI__', chrome.runtime.getURL('/ui/res/fonts/SourceCodePro-Italic.ttf'))
|
||||||
|
.replaceAll('html,', '')
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
private injectContent(content: WindowContent) {
|
||||||
|
const container = this.shadow.querySelector('.uw-content')!;
|
||||||
|
if (typeof content === 'string') {
|
||||||
|
container.innerHTML = content;
|
||||||
|
} else {
|
||||||
|
container.appendChild(content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
//#region interactivity
|
||||||
|
private bindEvents() {
|
||||||
|
this.windowEl.addEventListener('mousedown', this.bringToFront);
|
||||||
|
|
||||||
|
this.titleBar.addEventListener('mousedown', this.startDrag);
|
||||||
|
|
||||||
|
this.shadow.querySelector('.uw-close')!
|
||||||
|
.addEventListener('click', () => this.destroy());
|
||||||
|
|
||||||
|
this.shadow.querySelector('.uw-resize-handle')!
|
||||||
|
.addEventListener('mousedown', this.startResize);
|
||||||
|
|
||||||
|
window.addEventListener('mousemove', this.onMouseMove);
|
||||||
|
window.addEventListener('mouseup', this.stopActions);
|
||||||
|
}
|
||||||
|
|
||||||
|
private startDrag = (e: MouseEvent) => {
|
||||||
|
const rect = this.windowEl.getBoundingClientRect();
|
||||||
|
this.dragging = true;
|
||||||
|
this.dragOffsetX = e.clientX - rect.left;
|
||||||
|
this.dragOffsetY = e.clientY - rect.top;
|
||||||
|
e.preventDefault();
|
||||||
|
};
|
||||||
|
|
||||||
|
private startResize = (e: MouseEvent) => {
|
||||||
|
this.resizing = true;
|
||||||
|
e.preventDefault();
|
||||||
|
};
|
||||||
|
|
||||||
|
private onMouseMove = (e: MouseEvent) => {
|
||||||
|
const rect = this.windowEl.getBoundingClientRect();
|
||||||
|
|
||||||
|
if (this.dragging) {
|
||||||
|
const x = Math.min(
|
||||||
|
window.innerWidth - rect.width,
|
||||||
|
Math.max(0, e.clientX - this.dragOffsetX)
|
||||||
|
);
|
||||||
|
const y = Math.min(
|
||||||
|
window.innerHeight - rect.height,
|
||||||
|
Math.max(0, e.clientY - this.dragOffsetY)
|
||||||
|
);
|
||||||
|
|
||||||
|
this.windowEl.style.left = `${x}px`;
|
||||||
|
this.windowEl.style.top = `${y}px`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.resizing) {
|
||||||
|
const width = Math.max(160, e.clientX - rect.left);
|
||||||
|
const height = Math.max(120, e.clientY - rect.top);
|
||||||
|
|
||||||
|
this.windowEl.style.width = `${width}px`;
|
||||||
|
this.windowEl.style.height = `${height}px`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private stopActions = () => {
|
||||||
|
this.dragging = false;
|
||||||
|
this.resizing = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
private setGeometry(x: number, y: number, w: number, h: number) {
|
||||||
|
Object.assign(this.windowEl.style, {
|
||||||
|
left: `${x}px`,
|
||||||
|
top: `${y}px`,
|
||||||
|
width: `${w}px`,
|
||||||
|
height: `${h}px`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//#endregion interactivity
|
||||||
|
|
||||||
|
//#region bakcground handling
|
||||||
|
setBackground(mode: WindowBackgroundMode) {
|
||||||
|
this.background = mode;
|
||||||
|
this.applyBackground();
|
||||||
|
}
|
||||||
|
|
||||||
|
private applyBackground() {
|
||||||
|
this.windowEl.classList.remove(
|
||||||
|
'uw-window-bg-transparent',
|
||||||
|
'uw-window-bg-blurred',
|
||||||
|
'uw-window-bg-solid'
|
||||||
|
);
|
||||||
|
|
||||||
|
switch (this.background) {
|
||||||
|
case 'transparent':
|
||||||
|
this.windowEl.classList.add('uw-window-bg-transparent');
|
||||||
|
break;
|
||||||
|
case 'blur':
|
||||||
|
this.windowEl.classList.add('uw-window-bg-blurred');
|
||||||
|
break;
|
||||||
|
case 'solid':
|
||||||
|
this.windowEl.classList.add('uw-window-bg-solid');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bindBackgroundMenu() {
|
||||||
|
this.shadow.querySelectorAll('[data-bg]')
|
||||||
|
.forEach(el => {
|
||||||
|
el.addEventListener('click', () => {
|
||||||
|
const mode = el.getAttribute('data-bg') as WindowBackgroundMode;
|
||||||
|
this.setBackground(mode);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//#endregion
|
||||||
|
}
|
||||||
@ -73,7 +73,11 @@ class Resizer {
|
|||||||
if (this._lastAr?.type !== x.type || this._lastAr?.ratio !== x.ratio) {
|
if (this._lastAr?.type !== x.type || this._lastAr?.ratio !== x.ratio) {
|
||||||
this.eventBus.send('uw-config-broadcast', {type: 'ar', config: x});
|
this.eventBus.send('uw-config-broadcast', {type: 'ar', config: x});
|
||||||
}
|
}
|
||||||
|
if (x) {
|
||||||
this._lastAr = x;
|
this._lastAr = x;
|
||||||
|
} else {
|
||||||
|
this._lastAr = {type: AspectRatioType.Initial};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
get lastAr() {
|
get lastAr() {
|
||||||
return this._lastAr;
|
return this._lastAr;
|
||||||
@ -489,8 +493,9 @@ class Resizer {
|
|||||||
|
|
||||||
toFixedAr() {
|
toFixedAr() {
|
||||||
// converting to fixed AR means we also turn off autoAR
|
// converting to fixed AR means we also turn off autoAR
|
||||||
|
|
||||||
this.setAr({
|
this.setAr({
|
||||||
ratio: this.lastAr.ratio,
|
ratio: this.lastAr.ratio ?? this.getFileAr(),
|
||||||
type: AspectRatioType.Fixed
|
type: AspectRatioType.Fixed
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,14 +7,15 @@
|
|||||||
@import '@ui/res/styles/buttons.css';
|
@import '@ui/res/styles/buttons.css';
|
||||||
|
|
||||||
@import '@ui/res/styles/player-menu.css';
|
@import '@ui/res/styles/player-menu.css';
|
||||||
|
@import '@ui/res/styles/player-window.css';
|
||||||
|
|
||||||
|
|
||||||
:host {
|
:host {
|
||||||
@apply bg-stone-950 text-stone-300;
|
@apply bg-transparent text-stone-300;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
}
|
}
|
||||||
html, body {
|
html, body {
|
||||||
@apply bg-stone-950 text-stone-300;
|
@apply bg-transparent text-stone-300;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -95,6 +95,15 @@
|
|||||||
></SiteExtensionSettings>
|
></SiteExtensionSettings>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<template v-if="settings && selectedTab === 'window.site-extension-settings'" >
|
||||||
|
<h3>Settings for {{site?.host}}</h3>
|
||||||
|
<SiteExtensionSettings
|
||||||
|
:settings="settings"
|
||||||
|
:siteSettings="siteSettings"
|
||||||
|
:isDefaultConfiguration="false"
|
||||||
|
></SiteExtensionSettings>
|
||||||
|
</template>
|
||||||
|
|
||||||
<template v-if="settings && selectedTab === 'embedded-extension-settings'" >
|
<template v-if="settings && selectedTab === 'embedded-extension-settings'" >
|
||||||
<h3>Settings for embedded sites</h3>
|
<h3>Settings for embedded sites</h3>
|
||||||
<FrameSiteSettings
|
<FrameSiteSettings
|
||||||
@ -223,6 +232,15 @@ const AVAILABLE_TABS = {
|
|||||||
{ id: 'default-extension-settings', label: 'Default settings' }
|
{ id: 'default-extension-settings', label: 'Default settings' }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
'window.site-extension-settings': {
|
||||||
|
id: 'window.site-extension-settings', label: 'Site and Extension options', icon: 'cogs',
|
||||||
|
children: [
|
||||||
|
{ id: 'window.site-extension-settings', label: 'For this site', },
|
||||||
|
{ id: 'embedded-extension-settings', label: 'For embedded sites', disabled: true, badgeCount: 0, },
|
||||||
|
{ id: 'default-extension-settings', label: 'Default settings' },
|
||||||
|
{ id: 'website-extension-settings', label: 'Website exceptions' },
|
||||||
|
]
|
||||||
|
},
|
||||||
'default-extension-settings': {
|
'default-extension-settings': {
|
||||||
id: 'default-extension-settings', label: 'Site and Extension options', icon: 'cogs',
|
id: 'default-extension-settings', label: 'Site and Extension options', icon: 'cogs',
|
||||||
children: [
|
children: [
|
||||||
@ -258,7 +276,15 @@ const TAB_LOADOUT = {
|
|||||||
'debugging',
|
'debugging',
|
||||||
],
|
],
|
||||||
'ui': [
|
'ui': [
|
||||||
'window.player-element-settings'
|
'window.site-extension-settings',
|
||||||
|
'window.player-element-settings',
|
||||||
|
'autodetectionSettings',
|
||||||
|
'ui-settings',
|
||||||
|
'keyboardShortcuts',
|
||||||
|
'changelog',
|
||||||
|
'about',
|
||||||
|
'import-export-settings',
|
||||||
|
'debugging',
|
||||||
],
|
],
|
||||||
'updated': [
|
'updated': [
|
||||||
'updated',
|
'updated',
|
||||||
|
|||||||
@ -23,6 +23,7 @@
|
|||||||
@change="settings.saveWithoutReload"
|
@change="settings.saveWithoutReload"
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="w-full grid grid-cols-1 min-[1200px]:grid-cols-2 min-[1920px]:grid-cols-3 max-w-[2300px] gap-8">
|
<div class="w-full grid grid-cols-1 min-[1200px]:grid-cols-2 min-[1920px]:grid-cols-3 max-w-[2300px] gap-8">
|
||||||
<div class="grow shrink">
|
<div class="grow shrink">
|
||||||
<h3 class="mb-4">Logger configuration</h3>
|
<h3 class="mb-4">Logger configuration</h3>
|
||||||
|
|||||||
@ -1,14 +1,18 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="
|
<div
|
||||||
|
class="
|
||||||
w-full h-[100dvh] overflow-hidden
|
w-full h-[100dvh] overflow-hidden
|
||||||
p-1 popup-lg:py-2 popup-lg:px-2 window:py-4 window:px-8
|
|
||||||
flex flex-row justify-center items-center
|
flex flex-row justify-center items-center
|
||||||
">
|
"
|
||||||
|
:class="{
|
||||||
|
'p-1 popup-lg:py-2 popup-lg:px-2 window:py-4 window:px-8 bg-stone-950': role !== 'ui'
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
|
||||||
<!-- page content -->
|
<!-- page content -->
|
||||||
<div
|
<div
|
||||||
class="w-full h-[100dvh] overflow-hidden flex flex-col"
|
class="w-full h-[100dvh] overflow-hidden flex flex-col"
|
||||||
:class="{'max-w-[1920px]': !isDebugging}"
|
:class="{'max-w-[1920px]': !isDebugging && role !== 'ui'}"
|
||||||
>
|
>
|
||||||
<PopupHead
|
<PopupHead
|
||||||
v-if="role === 'popup' && settings && siteSettings && eventBus"
|
v-if="role === 'popup' && settings && siteSettings && eventBus"
|
||||||
@ -18,6 +22,9 @@
|
|||||||
:eventBus="eventBus"
|
:eventBus="eventBus"
|
||||||
>
|
>
|
||||||
</PopupHead>
|
</PopupHead>
|
||||||
|
<div v-else-if="role === 'ui'" class="flex flex-row font-mono text-[0.8rem] text-stone-500 border-stone-500">
|
||||||
|
url: {{getUrl}}
|
||||||
|
</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<h1 class="text-[3em] grow-0 shrink-0">Ultrawidify settings</h1>
|
<h1 class="text-[3em] grow-0 shrink-0">Ultrawidify settings</h1>
|
||||||
</div>
|
</div>
|
||||||
@ -78,6 +85,11 @@ export default defineComponent({
|
|||||||
isDebugging: false,
|
isDebugging: false,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
getUrl() {
|
||||||
|
return window.location.href;
|
||||||
|
}
|
||||||
|
},
|
||||||
async created() {
|
async created() {
|
||||||
const [segment, ...path] = window.location.hash.split('/');
|
const [segment, ...path] = window.location.hash.split('/');
|
||||||
this.initialPath = path;
|
this.initialPath = path;
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en"; style="position: relative; width: 799px; min-width: 320px; height: 600px; overflow: hidden;">
|
<html lang="en"; style="position: relative; width: 799px; min-width: 320px; height: 600px; overflow: hidden; background-color: transparent;">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="color-scheme" content="light dark">
|
<meta name="color-scheme" content="light dark">
|
||||||
|
|||||||
@ -1,14 +1,6 @@
|
|||||||
@import "tailwindcss";
|
@import "tailwindcss";
|
||||||
@import "tailwindcss/utilities";
|
@import "tailwindcss/utilities";
|
||||||
|
|
||||||
svg {
|
|
||||||
@apply text-inherit fill-current stroke-current;
|
|
||||||
|
|
||||||
* {
|
|
||||||
@apply text-inherit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.uw-menu-root {
|
.uw-menu-root {
|
||||||
@apply h-full w-full font-mono text-stone-200 text-[16px] flex flex-row bg-transparent;
|
@apply h-full w-full font-mono text-stone-200 text-[16px] flex flex-row bg-transparent;
|
||||||
|
|
||||||
@ -61,6 +53,18 @@ svg {
|
|||||||
/* * {
|
/* * {
|
||||||
@apply font-mono text-stone-200 text-[1em];
|
@apply font-mono text-stone-200 text-[1em];
|
||||||
} */
|
} */
|
||||||
|
|
||||||
|
svg {
|
||||||
|
@apply text-inherit fill-current stroke-transparent;
|
||||||
|
|
||||||
|
* {
|
||||||
|
@apply text-inherit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.svg-icon {
|
||||||
|
@apply fill-stone-300 stroke-transparent border-transparent;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.uw-hidden {
|
.uw-hidden {
|
||||||
@ -154,7 +158,6 @@ svg {
|
|||||||
@apply inline-flex flex-row items-center !gap-4 rounded-[0.5rem] relative;
|
@apply inline-flex flex-row items-center !gap-4 rounded-[0.5rem] relative;
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
@apply fill-current stroke-current;
|
|
||||||
height: 1em;
|
height: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -171,7 +174,6 @@ svg {
|
|||||||
border: 1px solid;
|
border: 1px solid;
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
@apply fill-current stroke-current;
|
|
||||||
height: 1.5em;
|
height: 1.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -189,7 +191,6 @@ svg {
|
|||||||
@apply flex flex-row items-center justify-center w-[420px];
|
@apply flex flex-row items-center justify-center w-[420px];
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
@apply fill-current stroke-current text-inherit;
|
|
||||||
height: 1.5em;
|
height: 1.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -250,6 +251,26 @@ svg {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#videoAlignmentController {
|
||||||
|
@apply text-stone-400;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
@apply border-transparent fill-stone-300 stroke-transparent;
|
||||||
|
|
||||||
|
* {
|
||||||
|
@apply border-transparent text-inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
rect {
|
||||||
|
stroke: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selected {
|
||||||
|
@apply fill-primary-400 stroke-primary-400;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.uw-link-button.uw-linked .uw-unlinked {
|
.uw-link-button.uw-linked .uw-unlinked {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|||||||
169
src/ui/res/styles/player-window.css
Normal file
169
src/ui/res/styles/player-window.css
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
@import "tailwindcss";
|
||||||
|
@import "tailwindcss/utilities";
|
||||||
|
|
||||||
|
:host {
|
||||||
|
all: initial;
|
||||||
|
color-scheme: dark light;
|
||||||
|
|
||||||
|
@apply font-mono text-stone-300;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uw-window {
|
||||||
|
@apply fixed flex flex-col pointer-events-auto border-stone-500;
|
||||||
|
|
||||||
|
.uw-content {
|
||||||
|
@apply p-4 flex grow-1 font-sans overflow-hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.uw-window-bg-transparent {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
&.uw-window-bg-blurred {
|
||||||
|
@apply bg-stone-950/75;
|
||||||
|
backdrop-filter: brightness(50%) saturate(50%) blur(16px);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.uw-window-bg-solid {
|
||||||
|
@apply bg-stone-950;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.uw-resize-handle {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
cursor: nwse-resize;
|
||||||
|
background:
|
||||||
|
linear-gradient(135deg, transparent 50%, #666 50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.uw-titlebar {
|
||||||
|
@apply flex flex-row gap-4 items-center cursor-move select-none justify-between
|
||||||
|
bg-stone-950/80 border-b border-b-stone-700;
|
||||||
|
|
||||||
|
.uw-title {
|
||||||
|
@apply px-4 py-2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uw-actions {
|
||||||
|
@apply flex flex-row;
|
||||||
|
|
||||||
|
button {
|
||||||
|
@apply h-[32px] w-[28px] relative p-0 m-0
|
||||||
|
text-stone-300 hover:text-primary-200 bg-transparent border-none border-transparent px-2;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
@apply h-[24px] w-[24px] fill-current;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.uw-close {
|
||||||
|
@apply w-[40px] px-[8px];
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
@apply bg-red-600 text-[#fff];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.uw-bg-toggle {
|
||||||
|
@apply relative;
|
||||||
|
|
||||||
|
.uw-bg-menu {
|
||||||
|
@apply absolute top-[100%] right-0 bg-stone-950/75;
|
||||||
|
|
||||||
|
display: none;
|
||||||
|
z-index: 1;
|
||||||
|
|
||||||
|
button {
|
||||||
|
@apply w-full text-left px-4 py-2 hover:bg-stone-800;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
&:hover .uw-bg-menu {
|
||||||
|
@apply flex flex-col gap-2
|
||||||
|
min-w-[220px]
|
||||||
|
text-stone-300 hover:text-primary-200
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-categories {
|
||||||
|
@apply
|
||||||
|
relative
|
||||||
|
w-[4.5em] popup:w-[18em] window:w-[24em]
|
||||||
|
mr-[1em]
|
||||||
|
grow-0 shrink-0 flex flex-col
|
||||||
|
|
||||||
|
text-right;
|
||||||
|
|
||||||
|
.tab-column {
|
||||||
|
@apply absolute popup:relative top-0 right-0
|
||||||
|
w-[18em] window:w-full h-full z-[1000]
|
||||||
|
hover:bg-stone-950 popup:hover:bg-transparent
|
||||||
|
border-r border-r-stone-800
|
||||||
|
hover:translate-x-[calc(100%-4.5em)] popup:hover:translate-x-0
|
||||||
|
transition-transform duration-200;
|
||||||
|
|
||||||
|
.tab {
|
||||||
|
@apply
|
||||||
|
flex flex-col
|
||||||
|
cursor-pointer
|
||||||
|
border-r-2 border-r-transparent
|
||||||
|
border-r-primary-400;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
.main-tab {
|
||||||
|
@apply !bg-transparent !text-primary-300 bg-gradient-to-r from-transparent to-black hover:!text-primary-200 ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.disabled {
|
||||||
|
@apply pointer-events-none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-tab {
|
||||||
|
@apply
|
||||||
|
px-[1em] py-[0.5em]
|
||||||
|
flex flex-row gap-4 justify-end items-center
|
||||||
|
text-[1.125em] text-stone-300 text-right font-mono
|
||||||
|
cursor-pointer
|
||||||
|
hover:bg-stone-800
|
||||||
|
hover:text-primary-200
|
||||||
|
hover:border-r-stone-600;
|
||||||
|
|
||||||
|
.label {
|
||||||
|
@apply grow-0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.suboption {
|
||||||
|
@apply
|
||||||
|
px-6 py-2
|
||||||
|
uppercase text-stone-400/75 font-mono
|
||||||
|
relative
|
||||||
|
hover:bg-stone-800
|
||||||
|
hover:text-primary-200
|
||||||
|
hover:border-r-stone-600;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
@apply !bg-transparent !text-primary-300 bg-gradient-to-r from-transparent to-black hover:!text-primary-200;
|
||||||
|
}
|
||||||
|
&.disabled {
|
||||||
|
@apply pointer-events-none text-stone-600/75;
|
||||||
|
}
|
||||||
|
.badge {
|
||||||
|
@apply absolute right-[0.25rem] bottom-0 w-4 h-4 text-stone-950 bg-primary-300 rounded flex justify-center items-center font-bold text-[0.8rem];
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
@apply
|
||||||
|
mb-2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user