a few fixes (#20)
fix: ios improvements: always display full ui & no magnifier on double tap anymore fix: regression: connect to p2p peers
This commit is contained in:
parent
b6d236d430
commit
14a9a2bf18
20 changed files with 13080 additions and 56 deletions
3
.github/workflows/ci.yml
vendored
3
.github/workflows/ci.yml
vendored
|
|
@ -12,11 +12,10 @@ jobs:
|
|||
run: npm i -g pnpm
|
||||
- run: pnpm install
|
||||
- run: pnpm build
|
||||
- run: nohup pnpm prod-start & node cypress/minecraft-server.mjs &
|
||||
- uses: cypress-io/github-action@v5
|
||||
with:
|
||||
install: false
|
||||
# start: pnpm prod-start & node cypress/minecraft-server.mjs
|
||||
start: pnpm prod-start
|
||||
- uses: actions/upload-artifact@v3
|
||||
if: failure()
|
||||
with:
|
||||
|
|
|
|||
10
.github/workflows/preview.yml
vendored
10
.github/workflows/preview.yml
vendored
|
|
@ -3,11 +3,17 @@ env:
|
|||
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
|
||||
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
# todo skip already created deploys on that commit
|
||||
if: >-
|
||||
github.event.issue.pull_request != '' &&
|
||||
(
|
||||
contains(github.event.comment.body, '/deploy')
|
||||
)
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
|
|
|||
1
.npmrc
1
.npmrc
|
|
@ -1,4 +1,3 @@
|
|||
package-lock=false
|
||||
public-hoist-pattern=*
|
||||
ignore-workspace-root-check=true
|
||||
shell-emulator=true
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ it('Loads & renders singleplayer', () => {
|
|||
})
|
||||
})
|
||||
|
||||
it.skip('Joins to server', () => {
|
||||
it('Joins to server', () => {
|
||||
cy.visit('/')
|
||||
setLocalStorageSettings()
|
||||
// todo replace with data-test
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ const ctx = await esbuild.context({
|
|||
],
|
||||
metafile: true,
|
||||
plugins,
|
||||
sourcesContent: process.argv.includes('--no-sources'),
|
||||
sourcesContent: !process.argv.includes('--no-sources'),
|
||||
minify: process.argv.includes('--minify'),
|
||||
define: {
|
||||
'process.env.NODE_ENV': JSON.stringify(dev ? 'development' : 'production'),
|
||||
|
|
|
|||
|
|
@ -37,8 +37,8 @@
|
|||
<link rel="manifest" href="manifest.json" crossorigin="use-credentials">
|
||||
</head>
|
||||
<body>
|
||||
<div id="react-root"></div>
|
||||
<div id="ui-root">
|
||||
<div id="react-root"></div>
|
||||
<pmui-hud id="hud" style="display: none;"></pmui-hud>
|
||||
<pmui-pausescreen id="pause-screen" style="display: none;"></pmui-pausescreen>
|
||||
<pmui-loading-error-screen id="loading-error-screen" style="display: none;"></pmui-loading-error-screen>
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
"author": "PrismarineJS",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@dimaka/interface": "0.0.1",
|
||||
"@dimaka/interface": "0.0.3-alpha.0",
|
||||
"@emotion/css": "^11.11.2",
|
||||
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.11",
|
||||
"@types/react": "^18.2.20",
|
||||
|
|
@ -46,9 +46,7 @@
|
|||
"net-browserify": "github:PrismarineJS/net-browserify",
|
||||
"peerjs": "^1.5.0",
|
||||
"pretty-bytes": "^6.1.1",
|
||||
"prismarine-world": "^3.6.2",
|
||||
"qrcode.react": "^3.1.0",
|
||||
"querystring": "^0.2.1",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-refresh": "^0.14.0",
|
||||
|
|
@ -100,7 +98,8 @@
|
|||
"webpack-dev-middleware": "^6.1.1",
|
||||
"webpack-dev-server": "^4.15.1",
|
||||
"webpack-merge": "^5.9.0",
|
||||
"workbox-webpack-plugin": "^6.6.0"
|
||||
"workbox-webpack-plugin": "^6.6.0",
|
||||
"yaml": "^2.3.2"
|
||||
},
|
||||
"pnpm": {
|
||||
"overrides": {
|
||||
|
|
|
|||
12974
pnpm-lock.yaml
generated
Normal file
12974
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -37,21 +37,22 @@
|
|||
"devDependencies": {
|
||||
"assert": "^2.0.0",
|
||||
"buffer": "^6.0.3",
|
||||
"process": "^0.11.10",
|
||||
"minecraft-assets": "^1.9.0",
|
||||
"canvas": "^2.11.2",
|
||||
"filesize": "^10.0.12",
|
||||
"fs-extra": "^11.0.0",
|
||||
"jest": "^27.0.4",
|
||||
"jest-puppeteer": "^6.0.0",
|
||||
"minecraft-assets": "^1.9.0",
|
||||
"minecraft-wrap": "^1.3.0",
|
||||
"minecrafthawkeye": "^1.2.5",
|
||||
"mineflayer": "^4.0.0",
|
||||
"mineflayer-pathfinder": "^2.0.0",
|
||||
"prismarine-schematic": "^1.2.0",
|
||||
"minecrafthawkeye": "^1.2.5",
|
||||
"prismarine-viewer": "file:./",
|
||||
"process": "^0.11.10",
|
||||
"puppeteer": "^16.0.0",
|
||||
"standard": "^17.0.0",
|
||||
"webpack": "^5.10.2",
|
||||
"webpack-cli": "^5.1.1",
|
||||
"canvas": "^2.11.2",
|
||||
"fs-extra": "^11.0.0"
|
||||
"webpack-cli": "^5.1.1"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
26
scripts/updateGitPackages.mjs
Normal file
26
scripts/updateGitPackages.mjs
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
// pnpm bug workaround
|
||||
import fs from 'fs'
|
||||
import { parse } from 'yaml'
|
||||
|
||||
const lockfile = parse(fs.readFileSync('./pnpm-lock.yaml', 'utf8'))
|
||||
|
||||
const depsKeys = ['dependencies', 'devDependencies']
|
||||
|
||||
for (const importer of Object.values(lockfile.importers)) {
|
||||
for (const depsKey of depsKeys) {
|
||||
for (const [depName, { specifier, version }] of Object.entries(importer[depsKey])) {
|
||||
if (!specifier.startsWith('github:')) continue
|
||||
let branch = specifier.match(/#(.*)$/)?.[1] ?? ''
|
||||
if (branch) branch = `/${branch}`
|
||||
const sha = version.split('/').slice(3).join('/').replace(/\(.+/, '')
|
||||
const repo = version.split('/').slice(1, 3).join('/')
|
||||
const lastCommitJson = await fetch(`https://api.github.com/repos/${repo}/commits${branch}?per_page=1`).then(res => res.json())
|
||||
const lastCommitActual = lastCommitJson ?? lastCommitJson[0]
|
||||
const lastCommitActualSha = lastCommitActual?.sha
|
||||
if (lastCommitActualSha === undefined) debugger
|
||||
if (sha !== lastCommitActualSha) {
|
||||
console.log(`Outdated ${depName} github.com/${repo} : ${sha} -> ${lastCommitActualSha} (${lastCommitActual.commit.message})`)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -28,6 +28,7 @@ class Cursor {
|
|||
static instance = null
|
||||
|
||||
constructor (viewer, renderer, /** @type {import('mineflayer').Bot} */bot) {
|
||||
bot.on('physicsTick', () => { if (this.lastBlockPlaced < 4) this.lastBlockPlaced++ })
|
||||
if (Cursor.instance) return Cursor.instance
|
||||
|
||||
// Init state
|
||||
|
|
@ -108,7 +109,6 @@ class Cursor {
|
|||
bot.attack(entity)
|
||||
}
|
||||
})
|
||||
bot.on('physicsTick', () => { if (this.lastBlockPlaced < 4) this.lastBlockPlaced++ })
|
||||
}
|
||||
|
||||
// todo this shouldnt be done in the render loop, migrate the code to dom events to avoid delays on lags
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import EventEmitter from 'events'
|
||||
|
||||
import Client from 'minecraft-protocol/src/client'
|
||||
import CustomChannelClient from 'minecraft-protocol/src/customChannelClient'
|
||||
|
||||
window.serverDataChannel ??= {}
|
||||
export const customCommunication = {
|
||||
|
|
@ -27,7 +27,7 @@ export class LocalServer extends EventEmitter.EventEmitter {
|
|||
}
|
||||
|
||||
listen() {
|
||||
this.emit('connection', new Client(true, this.version, this.customPackets, this.hideErrors, customCommunication))
|
||||
this.emit('connection', new CustomChannelClient(true, this.version, customCommunication))
|
||||
}
|
||||
|
||||
close() { }
|
||||
|
|
|
|||
40
src/index.ts
40
src/index.ts
|
|
@ -136,6 +136,7 @@ const minPitch = -0.5 * Math.PI
|
|||
const renderer = new THREE.WebGLRenderer()
|
||||
renderer.setPixelRatio(window.devicePixelRatio || 1) // todo this value is too high on ios, need to check, probably we should use avg, also need to make it configurable
|
||||
renderer.setSize(window.innerWidth, window.innerHeight)
|
||||
renderer.domElement.id = 'viewer-canvas'
|
||||
document.body.appendChild(renderer.domElement)
|
||||
|
||||
// Create viewer
|
||||
|
|
@ -196,14 +197,21 @@ const optionsScrn = document.getElementById('options-screen')
|
|||
const pauseMenu = document.getElementById('pause-screen')
|
||||
|
||||
let mouseMovePostHandle = (e) => { }
|
||||
let lastMouseCall
|
||||
function onMouseMove(e) {
|
||||
let lastMouseMove: number
|
||||
let cursor: Cursor
|
||||
let debugMenu
|
||||
const updateCursor = () => {
|
||||
cursor.update(bot)
|
||||
debugMenu ??= hud.shadowRoot.querySelector('#debug-overlay')
|
||||
debugMenu.cursorBlock = cursor.cursorBlock
|
||||
}
|
||||
function onCameraMove(e) {
|
||||
if (e.type !== 'touchmove' && !pointerLock.hasPointerLock) return
|
||||
e.stopPropagation?.()
|
||||
const now = performance.now()
|
||||
// todo: limit camera movement for now to avoid unexpected jumps
|
||||
if (now - lastMouseCall < 4) return
|
||||
lastMouseCall = now
|
||||
if (now - lastMouseMove < 4) return
|
||||
lastMouseMove = now
|
||||
let { mouseSensX, mouseSensY } = optionsScrn
|
||||
if (mouseSensY === true) mouseSensY = mouseSensX
|
||||
// debugPitch.innerText = +debugPitch.innerText + e.movementX
|
||||
|
|
@ -211,8 +219,10 @@ function onMouseMove(e) {
|
|||
x: e.movementX * mouseSensX * 0.0001,
|
||||
y: e.movementY * mouseSensY * 0.0001
|
||||
})
|
||||
// todo do it also on every block update within radius 5
|
||||
updateCursor()
|
||||
}
|
||||
window.addEventListener('mousemove', onMouseMove, { capture: true })
|
||||
window.addEventListener('mousemove', onCameraMove, { capture: true })
|
||||
|
||||
|
||||
function hideCurrentScreens() {
|
||||
|
|
@ -289,8 +299,6 @@ async function connect(connectOptions: {
|
|||
timeouts.push(id)
|
||||
return id
|
||||
}
|
||||
const debugMenu = hud.shadowRoot.querySelector('#debug-overlay')
|
||||
|
||||
const { renderDistance, maxMultiplayerRenderDistance } = options
|
||||
const hostprompt = connectOptions.server
|
||||
const proxyprompt = connectOptions.proxy
|
||||
|
|
@ -478,6 +486,9 @@ async function connect(connectOptions: {
|
|||
handleError(err)
|
||||
}
|
||||
if (!bot) return
|
||||
cursor = new Cursor(viewer, renderer, bot)
|
||||
// bot.on('move', () => updateCursor())
|
||||
|
||||
let p2pConnectTimeout = p2pMultiplayer ? setTimeout(() => { throw new Error('Spawn timeout. There might be error on other side, check console.') }, 20_000) : undefined
|
||||
hud.preload(bot)
|
||||
|
||||
|
|
@ -546,8 +557,11 @@ async function connect(connectOptions: {
|
|||
})
|
||||
optionsScrn.addEventListener('fov_changed', updateFov)
|
||||
|
||||
bot.on('physicsTick', () => updateCursor())
|
||||
viewer.setVersion(version)
|
||||
|
||||
const debugMenu = hud.shadowRoot.querySelector('#debug-overlay')
|
||||
|
||||
window.viewer = viewer
|
||||
window.loadedData = mcData
|
||||
window.worldView = worldView
|
||||
|
|
@ -559,11 +573,8 @@ async function connect(connectOptions: {
|
|||
|
||||
initVR(bot, renderer, viewer)
|
||||
|
||||
const cursor = new Cursor(viewer, renderer, bot)
|
||||
postRenderFrameFn = () => {
|
||||
viewer.setFirstPersonCamera(null, bot.entity.yaw, bot.entity.pitch)
|
||||
cursor.update(bot)
|
||||
debugMenu.cursorBlock = cursor.cursorBlock
|
||||
// viewer.setFirstPersonCamera(null, bot.entity.yaw, bot.entity.pitch)
|
||||
}
|
||||
|
||||
try {
|
||||
|
|
@ -615,6 +626,7 @@ async function connect(connectOptions: {
|
|||
let virtualClickTimeout
|
||||
let screenTouches = 0
|
||||
let capturedPointer: { id; x; y; sourceX; sourceY; activateCameraMove; time } | null
|
||||
document.body.addEventListener('touchstart', (e) => e.preventDefault(), { passive: false })
|
||||
registerListener(document, 'pointerdown', (e) => {
|
||||
const clickedEl = e.composedPath()[0]
|
||||
if (!isGameActive(true) || !miscUiState.currentTouch || clickedEl !== cameraControlEl || e.pointerId === undefined) {
|
||||
|
|
@ -656,7 +668,7 @@ async function connect(connectOptions: {
|
|||
if (capturedPointer.activateCameraMove) {
|
||||
clearTimeout(virtualClickTimeout)
|
||||
}
|
||||
onMouseMove({ movementX: e.pageX - capturedPointer.x, movementY: e.pageY - capturedPointer.y, type: 'touchmove' })
|
||||
onCameraMove({ movementX: e.pageX - capturedPointer.x, movementY: e.pageY - capturedPointer.y, type: 'touchmove' })
|
||||
capturedPointer.x = e.pageX
|
||||
capturedPointer.y = e.pageY
|
||||
}, { passive: false })
|
||||
|
|
@ -666,7 +678,7 @@ async function connect(connectOptions: {
|
|||
let { x, z } = vector
|
||||
if (Math.abs(x) < 0.18) x = 0
|
||||
if (Math.abs(z) < 0.18) z = 0
|
||||
onMouseMove({ movementX: x * 10, movementY: z * 10, type: 'touchmove' })
|
||||
onCameraMove({ movementX: x * 10, movementY: z * 10, type: 'touchmove' })
|
||||
})
|
||||
|
||||
registerListener(document, 'lostpointercapture', (e) => {
|
||||
|
|
@ -766,6 +778,7 @@ downloadAndOpenFile().then((downloadAction) => {
|
|||
if (peerId) {
|
||||
let username = options.guestUsername
|
||||
if (options.askGuestName) username = prompt('Enter your username', username)
|
||||
if (!username) return
|
||||
options.guestUsername = username
|
||||
connect({
|
||||
server: '', port: '', proxy: '', password: '',
|
||||
|
|
@ -775,6 +788,7 @@ downloadAndOpenFile().then((downloadAction) => {
|
|||
})
|
||||
}
|
||||
})
|
||||
if (document.getElementById('hud').isReady) window.dispatchEvent(new Event('hud-ready'))
|
||||
}, (err) => {
|
||||
console.error(err)
|
||||
alert(`Failed to download file: ${err}`)
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ export const openToWanAndCopyJoinLink = async (writeText: (text) => void, doCopy
|
|||
peer.on('connection', (connection) => {
|
||||
console.log('connection')
|
||||
const serverDuplex = new CustomDuplex({}, (data) => connection.send(data))
|
||||
const client = new Client(true, localServer.options.version, undefined, false, undefined, /* true */);
|
||||
const client = new Client(true, localServer.options.version, undefined)
|
||||
client.setSocket(serverDuplex)
|
||||
localServer._server.emit('connection', client)
|
||||
|
||||
|
|
@ -62,7 +62,7 @@ export const openToWanAndCopyJoinLink = async (writeText: (text) => void, doCopy
|
|||
console.log('connection.close')
|
||||
serverDuplex.end()
|
||||
connection.close()
|
||||
};
|
||||
}
|
||||
serverDuplex.on('end', endConnection)
|
||||
serverDuplex.on('force-close', endConnection)
|
||||
client.on('end', endConnection)
|
||||
|
|
@ -120,7 +120,7 @@ export const connectToPeer = async (peerId: string) => {
|
|||
connection.once('error', (error) => {
|
||||
console.log(error.type, error.name)
|
||||
console.log(error)
|
||||
return reject(error.message);
|
||||
return reject(error.message)
|
||||
})
|
||||
connection.once('open', resolve)
|
||||
}))
|
||||
|
|
@ -128,7 +128,7 @@ export const connectToPeer = async (peerId: string) => {
|
|||
const clientDuplex = new CustomDuplex({}, (data) => {
|
||||
// todo rm debug
|
||||
console.debug('sending', data.toString())
|
||||
connection.send(data);
|
||||
connection.send(data)
|
||||
})
|
||||
connection.on('data', (data: any) => {
|
||||
console.debug('received', Buffer.from(data).toString())
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ class HealthBar extends LitElement {
|
|||
static get styles () {
|
||||
return css`
|
||||
.health {
|
||||
position: absolute;
|
||||
position: fixed;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
left: calc(50% - 91px);
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ class Hotbar extends LitElement {
|
|||
static get styles () {
|
||||
return css`
|
||||
.hotbar {
|
||||
position: absolute;
|
||||
position: fixed;
|
||||
bottom: ${unsafeCSS(isProbablyIphone() ? '40px' : '0')};
|
||||
left: 50%;
|
||||
transform: translate(-50%);
|
||||
|
|
|
|||
|
|
@ -10,18 +10,19 @@ export const guiIcons1_16_4 = require('minecraft-assets/minecraft-assets/data/1.
|
|||
|
||||
class Hud extends LitElement {
|
||||
firstUpdated () {
|
||||
this.isReady = true
|
||||
window.dispatchEvent(new CustomEvent('hud-ready', { detail: this }))
|
||||
}
|
||||
|
||||
static get styles () {
|
||||
return css`
|
||||
:host {
|
||||
position: absolute;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: -2;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
height: 100vh;
|
||||
touch-action: none;
|
||||
}
|
||||
|
||||
|
|
@ -30,7 +31,7 @@ class Hud extends LitElement {
|
|||
height: 16px;
|
||||
background: url('${unsafeCSS(guiIcons1_17_1)}');
|
||||
background-size: 256px;
|
||||
position: absolute;
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
|
|
@ -38,7 +39,7 @@ class Hud extends LitElement {
|
|||
}
|
||||
|
||||
#xp-label {
|
||||
position: absolute;
|
||||
position: fixed;
|
||||
top: -8px;
|
||||
left: 50%;
|
||||
transform: translate(-50%);
|
||||
|
|
@ -50,7 +51,7 @@ class Hud extends LitElement {
|
|||
}
|
||||
|
||||
#xp-bar-bg {
|
||||
position: absolute;
|
||||
position: fixed;
|
||||
left: 50%;
|
||||
bottom: 24px;
|
||||
transform: translate(-50%);
|
||||
|
|
@ -72,7 +73,7 @@ class Hud extends LitElement {
|
|||
.mobile-top-btns {
|
||||
display: none;
|
||||
flex-direction: row;
|
||||
position: absolute;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 50%;
|
||||
transform: translate(-50%);
|
||||
|
|
@ -318,17 +319,17 @@ class Hud extends LitElement {
|
|||
render () {
|
||||
return html`
|
||||
<div class="mobile-top-btns" id="mobile-top">
|
||||
<button class="debug-btn" @click=${(e) => {
|
||||
<button class="debug-btn" @pointerdown=${(e) => {
|
||||
window.dispatchEvent(new MouseEvent('mousedown', { button: 1, }))
|
||||
}}>Select</button>
|
||||
<button class="debug-btn" @click=${(e) => {
|
||||
<button class="debug-btn" @pointerdown=${(e) => {
|
||||
this.shadowRoot.getElementById('debug-overlay').showOverlay = !this.shadowRoot.getElementById('debug-overlay').showOverlay
|
||||
}}>F3</button>
|
||||
<button class="chat-btn" @click=${(e) => {
|
||||
<button class="chat-btn" @pointerdown=${(e) => {
|
||||
e.stopPropagation()
|
||||
this.shadowRoot.querySelector('#chat').enableChat()
|
||||
}}></button>
|
||||
<button class="pause-btn" @click=${(e) => {
|
||||
<button class="pause-btn" @pointerdown=${(e) => {
|
||||
e.stopPropagation()
|
||||
showModal(document.getElementById('pause-screen'))
|
||||
}}></button>
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ class PlayScreen extends LitElement {
|
|||
}
|
||||
|
||||
.edit-boxes {
|
||||
position: absolute;
|
||||
position: fixed;
|
||||
top: 59px;
|
||||
left: 50%;
|
||||
display: flex;
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import { createPortal } from 'react-dom'
|
|||
useInterfaceState.setState({
|
||||
isFlying: false,
|
||||
uiCustomization: {
|
||||
touchButtonSize: isProbablyIphone() ? 30 : 40,
|
||||
touchButtonSize: 40,
|
||||
},
|
||||
updateCoord: ([coord, state]) => {
|
||||
const coordToAction = [
|
||||
|
|
|
|||
|
|
@ -12,6 +12,11 @@
|
|||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
#react-root {
|
||||
z-index: 9;
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
a {
|
||||
color: white;
|
||||
}
|
||||
|
|
@ -63,8 +68,8 @@ body {
|
|||
user-select: none;
|
||||
}
|
||||
|
||||
canvas {
|
||||
position: absolute;
|
||||
#viewer-canvas {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
|
|
@ -75,14 +80,14 @@ canvas {
|
|||
}
|
||||
|
||||
#ui-root {
|
||||
position: absolute;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
transform-origin: top left;
|
||||
transform: scale(var(--guiScale));
|
||||
width: calc(100% / var(--guiScale));
|
||||
height: calc(100% / var(--guiScale));
|
||||
z-index: 10;
|
||||
z-index: 8;
|
||||
image-rendering: optimizeSpeed;
|
||||
image-rendering: -moz-crisp-edges;
|
||||
image-rendering: -webkit-optimize-contrast;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue