Lint JSX (#175)
This commit is contained in:
parent
be78985edf
commit
71289e3ef4
52 changed files with 664 additions and 366 deletions
|
|
@ -103,6 +103,47 @@
|
|||
// "@stylistic/newline-per-chained-call": "error", // not sure if needed
|
||||
"@stylistic/new-parens": "error",
|
||||
"@stylistic/no-confusing-arrow": "error",
|
||||
"@stylistic/wrap-iife": "error",
|
||||
"@stylistic/space-before-blocks": "error",
|
||||
"@stylistic/type-generic-spacing": "error",
|
||||
"@stylistic/template-tag-spacing": "error",
|
||||
"@stylistic/template-curly-spacing": "error",
|
||||
"@stylistic/type-annotation-spacing": "error",
|
||||
"@stylistic/jsx-child-element-spacing": "error",
|
||||
// buggy
|
||||
// "@stylistic/jsx-closing-bracket-location": "error",
|
||||
// "@stylistic/jsx-closing-tag-location": "error",
|
||||
"@stylistic/jsx-curly-brace-presence": "error",
|
||||
"@stylistic/jsx-curly-newline": "error",
|
||||
"@stylistic/jsx-curly-spacing": "error",
|
||||
"@stylistic/jsx-equals-spacing": "error",
|
||||
"@stylistic/jsx-first-prop-new-line": "error",
|
||||
"@stylistic/jsx-function-call-newline": "error",
|
||||
"@stylistic/jsx-max-props-per-line": [
|
||||
"error",
|
||||
{
|
||||
"maximum": 7
|
||||
}
|
||||
],
|
||||
"@stylistic/jsx-pascal-case": "error",
|
||||
"@stylistic/jsx-props-no-multi-spaces": "error",
|
||||
"@stylistic/jsx-self-closing-comp": "error",
|
||||
// "@stylistic/jsx-sort-props": [
|
||||
// "error",
|
||||
// {
|
||||
// "callbacksLast": false,
|
||||
// "shorthandFirst": true,
|
||||
// "shorthandLast": false,
|
||||
// "multiline": "ignore",
|
||||
// "ignoreCase": true,
|
||||
// "noSortAlphabetically": true,
|
||||
// "reservedFirst": [
|
||||
// "key",
|
||||
// "className"
|
||||
// ],
|
||||
// "locale": "auto"
|
||||
// }
|
||||
// ],
|
||||
// perf
|
||||
"import/no-deprecated": "off",
|
||||
// ---
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ After forking the repository, run the following commands to get started:
|
|||
0. Ensure you have [Node.js](https://nodejs.org) and `pnpm` installed. To install pnpm run `npm i -g pnpm@9.0.4`.
|
||||
1. Install dependencies: `pnpm i`
|
||||
2. Start the project in development mode: `pnpm start`
|
||||
3. Read the [Tasks Categories](#tasks-categories) and [Workflow](#workflow) sections below
|
||||
4. Let us know if you are working on something and be sure to open a PR if you got any changes. Happy coding!
|
||||
|
||||
## Project Structure
|
||||
|
||||
|
|
@ -57,6 +59,20 @@ How different modules are used:
|
|||
|
||||
- `mineflayer` - provider `bot` variable and as mineflayer states it is a wrapper for the `node-minecraft-protocol` module and is used to connect and interact with real Java Minecraft servers. However not all events & properties are exposed and sometimes you have to use `bot._client.on('packet_name', data => ...)` to handle packets that are not handled via mineflayer API. Also you can use almost any mineflayer plugin.
|
||||
|
||||
## Running Main App + Playground
|
||||
|
||||
To start the main web app and playground, run `pnpm run-all`. Note is doesn't start storybook and tests.
|
||||
|
||||
## Cypress Tests (E2E)
|
||||
|
||||
Cypress tests are located in `cypress` folder. To run them, run `pnpm test-mc-server` and then `pnpm test:cypress` when the `pnpm prod-start` is running (or change the port to 3000 to test with the dev server). Usually you don't need to run these until you get issues on the CI.
|
||||
|
||||
## Unit Tests
|
||||
|
||||
There are not many unit tests for now (which we are trying to improve).
|
||||
Location of unit tests: `**/*.test.ts` files in `src` folder and `prismarine-viewer` folder.
|
||||
Start them with `pnpm test-unit`.
|
||||
|
||||
## Making protocol-related changes
|
||||
|
||||
You can get a description of packets for the latest protocol version from <https://wiki.vg/Protocol> and for previous protocol versions from <https://wiki.vg/Protocol_version_numbers> (look for *Page* links that have *Protocol* in URL).
|
||||
|
|
@ -75,6 +91,84 @@ Also there are [src/generatedClientPackets.ts](src/generatedClientPackets.ts) an
|
|||
- Use `start-prod` script to start the project in production mode after running the `build` script to build the project.
|
||||
- If CI is failing on the next branch for some reason, feel free to use the latest commit for release branch. We will update the base branch asap. Please, always make sure to allow maintainers do changes when opening PRs.
|
||||
|
||||
## Tasks Categories
|
||||
|
||||
(most important for now are on top).
|
||||
|
||||
## 1. Client-side Logic (most important right now)
|
||||
|
||||
Everything related to the client side packets. Investigate issues when something goes wrong with some server. It's much easier to work on these types of tasks when you have experience in Java with Minecraft, a deep understanding of the original client, and know how to debug it (which is not hard actually). Right now the client is easily detectable by anti-cheat plugins, and the main goal is to fix it (mostly because of wrong physics implementation).
|
||||
|
||||
Priority tasks:
|
||||
|
||||
- Rewrite or fix the physics logic (Botcraft or Grim can be used as a reference as well)
|
||||
- Implement basic minecart / boat / horse riding
|
||||
- Fix auto jump module (false triggers, performance issues)
|
||||
- Investigate connection issues to some servers
|
||||
- Setup a platform for automatic cron testing against the latest version of the anti-cheat plugins
|
||||
- ...
|
||||
|
||||
Goals:
|
||||
|
||||
- Make more servers playable. Right now on hypixel-like servers (servers with minigames), only tnt run (and probably ) is fully playable.
|
||||
|
||||
Notes:
|
||||
|
||||
- You can see the incoming/outgoing packets in the console (F12 in Chrome) by enabling `options.debugLogNotFrequentPackets = true`. However, if you need a FULL log of all packets, you can start recording the packets by going into `Settings` > `Advanced` > `Enable Packets Replay` and then you can download the file and use it to replay the packets.
|
||||
- You can use mcraft-e2e studio to send the same packets over and over again (which is useful for testing) or use the packets replayer (which is useful for debugging).
|
||||
|
||||
## 2. Three.js Renderer
|
||||
|
||||
Example tasks:
|
||||
|
||||
- Improve / fix entity rendering
|
||||
- Better update entities on specific packets
|
||||
- Investigate performance issues under different conditions (instructions provided)
|
||||
- Work on the playground code
|
||||
|
||||
Goals:
|
||||
|
||||
- Fix a lot of entity rendering issues (including position updates)
|
||||
- Implement switching camera mode (first person, third person, etc)
|
||||
- Animated blocks
|
||||
- Armor rendering
|
||||
- ...
|
||||
|
||||
Note:
|
||||
|
||||
- It's useful to know how to use helpers & additional cameras (e.g. setScissor)
|
||||
|
||||
## 3. Server-side Logic
|
||||
|
||||
Flying squid fork (space-squid).
|
||||
Example tasks:
|
||||
|
||||
- Add missing commands (e.g. /scoreboard)
|
||||
- Basic physics (player fall damage, falling blocks & entities)
|
||||
- Basic entities AI (spawning, attacking)
|
||||
- Pvp
|
||||
- Emit more packets on some specific events (e.g. when a player uses an item)
|
||||
- Make more maps playable (e.g. fix when something is not implemented in both server and client and blocking map interaction)
|
||||
- ...
|
||||
|
||||
Long Term Goals:
|
||||
|
||||
- Make most adventure maps playable
|
||||
- Make a way to complete the game from the scratch (crafting, different dimensions, terrain generation, etc)
|
||||
- Make bedwars playable!
|
||||
Most of the tasks are straightforward to implement, just be sure to use a debugger ;). If you feel you are stuck, ask for help on Discord. Absolutely any tests / refactor suggestions are welcome!
|
||||
|
||||
## 4. Frontend
|
||||
|
||||
New React components, improve UI (including mobile support).
|
||||
|
||||
## Workflow
|
||||
|
||||
1. Locate the problem on the public test server & make an easily reproducible environment (you can also use local packets replay server or your custom server setup). Dm me for details on public test server / replay server
|
||||
2. Debug the code, find an issue in the code, isolate the problem
|
||||
3. Develop, try to fix and test. Finally we should find a way to fix it. It's ideal to have an automatic test but it's not necessary for now
|
||||
3. Repeat step 1 to make sure the task is done and the problem is fixed (or the feature is implemented)
|
||||
|
||||
### Would be useful to have
|
||||
|
||||
- cleanup folder & modules structure, cleanup playground code
|
||||
|
|
|
|||
|
|
@ -13,7 +13,8 @@ function InnerSearch () {
|
|||
margin: 'auto',
|
||||
zIndex: 11,
|
||||
width: 'min-content',
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
<Input
|
||||
autoFocus={currentTouch === false}
|
||||
width={50}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ import 'core-js/features/array/at'
|
|||
import 'core-js/features/promise/with-resolvers'
|
||||
|
||||
import './scaleInterface'
|
||||
import itemsPng from 'prismarine-viewer/public/textures/items.png'
|
||||
import { initWithRenderer } from './topRightStats'
|
||||
import PrismarineBlock from 'prismarine-block'
|
||||
import PrismarineItem from 'prismarine-item'
|
||||
|
|
|
|||
|
|
@ -23,13 +23,23 @@ export const guiOptionsScheme: {
|
|||
const [frameLimitMax, setFrameLimitMax] = useState(null as number | null)
|
||||
|
||||
return <div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||
<Slider style={{ width: 130 }} label='Frame Limit' disabledReason={frameLimitMax ? undefined : 'press lock button first'} unit={frameLimitValue ? 'fps' : ''} valueDisplay={frameLimitValue || 'VSync'} value={frameLimitValue || frameLimitMax! + 1} min={20} max={frameLimitMax! + 1} updateValue={(newVal) => {
|
||||
options.frameLimit = newVal > frameLimitMax! ? false : newVal
|
||||
}} />
|
||||
<Button style={{ width: 20 }} icon='pixelarticons:lock-open' onClick={async () => {
|
||||
const rate = await getScreenRefreshRate()
|
||||
setFrameLimitMax(rate)
|
||||
}} />
|
||||
<Slider
|
||||
style={{ width: 130 }}
|
||||
label='Frame Limit'
|
||||
disabledReason={frameLimitMax ? undefined : 'press lock button first'}
|
||||
unit={frameLimitValue ? 'fps' : ''}
|
||||
valueDisplay={frameLimitValue || 'VSync'}
|
||||
value={frameLimitValue || frameLimitMax! + 1} min={20}
|
||||
max={frameLimitMax! + 1} updateValue={(newVal) => {
|
||||
options.frameLimit = newVal > frameLimitMax! ? false : newVal
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
style={{ width: 20 }} icon='pixelarticons:lock-open' onClick={async () => {
|
||||
const rate = await getScreenRefreshRate()
|
||||
setFrameLimitMax(rate)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
},
|
||||
|
|
@ -94,7 +104,8 @@ export const guiOptionsScheme: {
|
|||
unit: '',
|
||||
max: sp ? 16 : 12,
|
||||
min: 1
|
||||
}} />
|
||||
}}
|
||||
/>
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -122,39 +133,41 @@ export const guiOptionsScheme: {
|
|||
const { resourcePackInstalled } = useSnapshot(resourcePackState)
|
||||
const { usingServerResourcePack } = useSnapshot(loadedGameState)
|
||||
const { enabledResourcepack } = useSnapshot(options)
|
||||
return <Button label={`Resource Pack: ${usingServerResourcePack ? 'SERVER ON' : resourcePackInstalled ? enabledResourcepack ? 'ON' : 'OFF' : 'NO'}`} inScreen onClick={async () => {
|
||||
if (resourcePackState.resourcePackInstalled) {
|
||||
const names = Object.keys(await getResourcePackNames())
|
||||
const name = names[0]
|
||||
const choices = [
|
||||
options.enabledResourcepack ? 'Disable' : 'Enable',
|
||||
'Uninstall',
|
||||
]
|
||||
const choice = await showOptionsModal(`Resource Pack ${name} action`, choices)
|
||||
if (!choice) return
|
||||
if (choice === 'Disable') {
|
||||
options.enabledResourcepack = null
|
||||
return
|
||||
}
|
||||
if (choice === 'Enable') {
|
||||
options.enabledResourcepack = name
|
||||
await completeTexturePackInstall(name, name)
|
||||
return
|
||||
}
|
||||
if (choice === 'Uninstall') {
|
||||
return <Button
|
||||
label={`Resource Pack: ${usingServerResourcePack ? 'SERVER ON' : resourcePackInstalled ? enabledResourcepack ? 'ON' : 'OFF' : 'NO'}`} inScreen onClick={async () => {
|
||||
if (resourcePackState.resourcePackInstalled) {
|
||||
const names = Object.keys(await getResourcePackNames())
|
||||
const name = names[0]
|
||||
const choices = [
|
||||
options.enabledResourcepack ? 'Disable' : 'Enable',
|
||||
'Uninstall',
|
||||
]
|
||||
const choice = await showOptionsModal(`Resource Pack ${name} action`, choices)
|
||||
if (!choice) return
|
||||
if (choice === 'Disable') {
|
||||
options.enabledResourcepack = null
|
||||
return
|
||||
}
|
||||
if (choice === 'Enable') {
|
||||
options.enabledResourcepack = name
|
||||
await completeTexturePackInstall(name, name)
|
||||
return
|
||||
}
|
||||
if (choice === 'Uninstall') {
|
||||
// todo make hidable
|
||||
setLoadingScreenStatus('Uninstalling texturepack')
|
||||
await uninstallTexturePack()
|
||||
setLoadingScreenStatus(undefined)
|
||||
}
|
||||
} else {
|
||||
setLoadingScreenStatus('Uninstalling texturepack')
|
||||
await uninstallTexturePack()
|
||||
setLoadingScreenStatus(undefined)
|
||||
}
|
||||
} else {
|
||||
// if (!fsState.inMemorySave && isGameActive(false)) {
|
||||
// alert('Unable to install resource pack in loaded save for now')
|
||||
// return
|
||||
// }
|
||||
openFilePicker('resourcepack')
|
||||
}
|
||||
}} />
|
||||
openFilePicker('resourcepack')
|
||||
}
|
||||
}}
|
||||
/>
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -224,7 +237,8 @@ export const guiOptionsScheme: {
|
|||
onClick={() => {
|
||||
showModal({ reactType: 'keybindings' })
|
||||
}}
|
||||
>Keybindings</Button>
|
||||
>Keybindings
|
||||
</Button>
|
||||
},
|
||||
mouseSensX: {},
|
||||
mouseSensY: {
|
||||
|
|
@ -325,9 +339,12 @@ export const guiOptionsScheme: {
|
|||
advanced: [
|
||||
{
|
||||
custom () {
|
||||
return <Button inScreen onClick={() => {
|
||||
if (confirm('Are you sure you want to reset all settings?')) resetLocalStorageWithoutWorld()
|
||||
}}>Reset all settings</Button>
|
||||
return <Button
|
||||
inScreen
|
||||
onClick={() => {
|
||||
if (confirm('Are you sure you want to reset all settings?')) resetLocalStorageWithoutWorld()
|
||||
}}
|
||||
>Reset all settings</Button>
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -338,17 +355,24 @@ export const guiOptionsScheme: {
|
|||
{
|
||||
custom () {
|
||||
const { active } = useSnapshot(packetsReplaceSessionState)
|
||||
return <Button inScreen onClick={() => {
|
||||
packetsReplaceSessionState.active = !active
|
||||
}}>{active ? 'Disable' : 'Enable'} Packets Replay</Button>
|
||||
return <Button
|
||||
inScreen
|
||||
onClick={() => {
|
||||
packetsReplaceSessionState.active = !active
|
||||
}}
|
||||
>{active ? 'Disable' : 'Enable'} Packets Replay</Button>
|
||||
},
|
||||
},
|
||||
{
|
||||
custom () {
|
||||
const { active } = useSnapshot(packetsReplaceSessionState)
|
||||
return <Button disabled={!active} inScreen onClick={() => {
|
||||
void downloadPacketsReplay()
|
||||
}}>Download Packets Replay</Button>
|
||||
return <Button
|
||||
disabled={!active}
|
||||
inScreen
|
||||
onClick={() => {
|
||||
void downloadPacketsReplay()
|
||||
}}
|
||||
>Download Packets Replay</Button>
|
||||
},
|
||||
}
|
||||
],
|
||||
|
|
|
|||
|
|
@ -83,7 +83,8 @@ export default ({ onBack, onConfirm, title = 'Add a Server', initialData, parseQ
|
|||
display: 'grid',
|
||||
gap: 3,
|
||||
gridTemplateColumns: smallWidth ? '1fr' : '1fr 1fr'
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{!lockConnect && <>
|
||||
<div style={{ gridColumn: smallWidth ? '' : 'span 2', display: 'flex', justifyContent: 'center' }}>
|
||||
<InputWithLabel label="Server Name" value={serverName} onChange={({ target: { value } }) => setServerName(value)} placeholder='Defaults to IP' />
|
||||
|
|
@ -98,7 +99,8 @@ export default ({ onBack, onConfirm, title = 'Add a Server', initialData, parseQ
|
|||
<label style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
<span style={{ fontSize: 12, marginBottom: 1, color: 'lightgray' }}>Account Override</span>
|
||||
<select
|
||||
onChange={({ target: { value } }) => setAccountIndex(Number(value))}
|
||||
|
|
@ -146,7 +148,8 @@ const InputWithLabel = ({ label, span, ...props }: React.ComponentProps<typeof I
|
|||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gridRow: span ? 'span 2 / span 2' : undefined,
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
<label style={{ fontSize: 12, marginBottom: 1, color: 'lightgray' }}>{label}</label>
|
||||
<Input rootStyles={{ width: ELEMENTS_WIDTH }} {...props} />
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -34,7 +34,8 @@ export default ({ status, isError, hideDots = false, lastStatus = '', backAction
|
|||
<span style={{
|
||||
userSelect: isError ? 'text' : undefined,
|
||||
wordBreak: 'break-word',
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{status}
|
||||
</span>
|
||||
{isError || hideDots ? '' : loadingDots}
|
||||
|
|
@ -48,7 +49,7 @@ export default ({ status, isError, hideDots = false, lastStatus = '', backAction
|
|||
<>
|
||||
{backAction && <Button label="Back" onClick={backAction} />}
|
||||
{actionsSlot}
|
||||
<Button onClick={() => window.location.reload()} label="Reset App (recommended)"></Button>
|
||||
<Button onClick={() => window.location.reload()} label="Reset App (recommended)" />
|
||||
</>
|
||||
)}
|
||||
</Screen>
|
||||
|
|
|
|||
|
|
@ -40,7 +40,8 @@ export default ({
|
|||
Array.from({ length: 10 }, () => 0)
|
||||
.map((num, index) => <div
|
||||
key={`armor-${index}`}
|
||||
className='armor'></div>)
|
||||
className='armor'
|
||||
/>)
|
||||
}
|
||||
</div>
|
||||
</SharedHudVars>
|
||||
|
|
|
|||
|
|
@ -14,7 +14,8 @@ export default () => {
|
|||
right: 0,
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
<Button onClick={() => {
|
||||
void bot.wake()
|
||||
hideCurrentModal()
|
||||
|
|
|
|||
|
|
@ -236,7 +236,8 @@ const Book: React.FC<BookProps> = ({ textPages, editable, onSign, onEdit, onClos
|
|||
? styles.titleAnimationReverse
|
||||
: ''
|
||||
}`}
|
||||
alt="Title Icon" />
|
||||
alt="Title Icon"
|
||||
/>
|
||||
<div className={`${styles.inside}`}>
|
||||
{renderPage(currentPage * (isSinglePage ? 1 : 2))}
|
||||
{!isSinglePage && (currentPage * 2 + 1) < pages.length && renderPage(currentPage * 2 + 1)}
|
||||
|
|
@ -274,17 +275,19 @@ const Book: React.FC<BookProps> = ({ textPages, editable, onSign, onEdit, onClos
|
|||
: animateTitleIcon === 2
|
||||
? styles.titleContentAnimationReverse
|
||||
: ''
|
||||
}`}>
|
||||
}`}
|
||||
>
|
||||
{editable ? (
|
||||
<div className={`${styles.titleContent}`} >
|
||||
<MessageFormattedString message="Enter Book Title: " />
|
||||
<form onSubmit={(e) => {
|
||||
e.preventDefault()
|
||||
handleSign()
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
<input
|
||||
ref={inputRef}
|
||||
className={''}
|
||||
className=""
|
||||
/>
|
||||
{/* for some reason this is needed to make Enter work on android chrome */}
|
||||
<button type='submit' style={{ visibility: 'hidden', height: 0, width: 0 }} />
|
||||
|
|
|
|||
|
|
@ -52,11 +52,10 @@ export default ({ bar }: { bar: BossBarType }) => {
|
|||
<div className="bossbar-container">
|
||||
<div className="bossbar-title"><MessageFormattedString message={title} /></div>
|
||||
<div className="bossbar" style={bossBarStyles}>
|
||||
<div className="fill" style={fillStyles}></div>
|
||||
<div className="fill" style={div1Styles}></div>
|
||||
<div className="fill" style={div2Styles}></div>
|
||||
<div className="fill" style={fillStyles} />
|
||||
<div className="fill" style={div1Styles} />
|
||||
<div className="fill" style={div2Styles} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ export default ({
|
|||
.map((num, index) => <div
|
||||
key={`breath-${index}`}
|
||||
className='breath'
|
||||
></div>)
|
||||
/>)
|
||||
}
|
||||
</div>
|
||||
</SharedHudVars>
|
||||
|
|
|
|||
|
|
@ -52,20 +52,23 @@ export default ({ initialTooltip, ...args }: Props) => {
|
|||
|
||||
return <>
|
||||
<Button {...args} rootRef={refs.setReference} />
|
||||
<div ref={refs.setFloating} style={{
|
||||
...floatingStyles,
|
||||
background: 'rgba(0, 0, 0, 0.3)',
|
||||
fontSize: 8,
|
||||
pointerEvents: 'none',
|
||||
userSelect: 'text',
|
||||
padding: '2px 4px',
|
||||
opacity: showTooltips ? 1 : 0,
|
||||
transition: 'opacity 0.3s ease-in-out',
|
||||
textShadow: '1px 1px 2px BLACK',
|
||||
zIndex: 11
|
||||
}}>
|
||||
<div
|
||||
ref={refs.setFloating}
|
||||
style={{
|
||||
...floatingStyles,
|
||||
background: 'rgba(0, 0, 0, 0.3)',
|
||||
fontSize: 8,
|
||||
pointerEvents: 'none',
|
||||
userSelect: 'text',
|
||||
padding: '2px 4px',
|
||||
opacity: showTooltips ? 1 : 0,
|
||||
transition: 'opacity 0.3s ease-in-out',
|
||||
textShadow: '1px 1px 2px BLACK',
|
||||
zIndex: 11
|
||||
}}
|
||||
>
|
||||
{initialTooltip.content}
|
||||
<FloatingArrow ref={arrowRef} context={context} style={{ opacity: 0.7 }}></FloatingArrow>
|
||||
<FloatingArrow ref={arrowRef} context={context} style={{ opacity: 0.7 }} />
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -73,17 +73,20 @@ const meta: Meta<typeof Chat> = {
|
|||
|
||||
return <div style={{
|
||||
marginTop: args.usingTouch ? 100 : 0
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
<div style={{ fontSize: 6, userSelect: 'auto', color: 'gray' }}>Hint: you can capture needed message with <code>bot.on('message', console.log)</code>, copy object, and assign it here to <code>window.spamMessage</code> variable (but ensure the correct frame window is selected in devtools)</div>
|
||||
<Chat {...args} opened={open} messages={messages} onClose={() => setOpen(false)} fetchCompletionItems={async (triggerType, value) => {
|
||||
console.log('fetchCompletionItems')
|
||||
await new Promise(resolve => {
|
||||
setTimeout(resolve, 0)
|
||||
})
|
||||
let items = ['test', ...Array.from({ length: 50 }).map((_, i) => `minecraft:hello${i}`)]
|
||||
if (value === '/') items = items.map(item => `/${item}`)
|
||||
return items
|
||||
}} />
|
||||
<Chat
|
||||
{...args} opened={open} messages={messages} onClose={() => setOpen(false)} fetchCompletionItems={async (triggerType, value) => {
|
||||
console.log('fetchCompletionItems')
|
||||
await new Promise(resolve => {
|
||||
setTimeout(resolve, 0)
|
||||
})
|
||||
let items = ['test', ...Array.from({ length: 50 }).map((_, i) => `minecraft:hello${i}`)]
|
||||
if (value === '/') items = items.map(item => `/${item}`)
|
||||
return items
|
||||
}}
|
||||
/>
|
||||
<Button onClick={() => setOpen(s => !s)}>Open: {open ? 'on' : 'off'}</Button>
|
||||
<Button onClick={() => fadeMessages()}>Fade</Button>
|
||||
<Button onClick={() => setAutoSpam(s => !s)}>Auto Spam: {autoSpam ? 'on' : 'off'}</Button>
|
||||
|
|
|
|||
|
|
@ -212,9 +212,11 @@ export default ({
|
|||
|
||||
return (
|
||||
<>
|
||||
<div className={`chat-wrapper chat-messages-wrapper ${usingTouch ? 'display-mobile' : ''}`} style={{
|
||||
userSelect: opened && allowSelection ? 'text' : undefined,
|
||||
}}>
|
||||
<div
|
||||
className={`chat-wrapper chat-messages-wrapper ${usingTouch ? 'display-mobile' : ''}`} style={{
|
||||
userSelect: opened && allowSelection ? 'text' : undefined,
|
||||
}}
|
||||
>
|
||||
{opacity && <div ref={chatMessages} className={`chat ${opened ? 'opened' : ''}`} id="chat-messages" style={{ opacity }}>
|
||||
{messages.map((m) => (
|
||||
<MessageLine key={reactKeyForMessage(m)} message={m} />
|
||||
|
|
@ -246,7 +248,8 @@ export default ({
|
|||
onClose?.()
|
||||
}
|
||||
}
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{isIos && <input
|
||||
value=''
|
||||
type="text"
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
import type { Meta, StoryObj } from '@storybook/react'
|
||||
import Button from './Button'
|
||||
|
||||
const defaultIcon = <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M3 21H5H19H21V3H19H5H3V21ZM19 5V19H5V5H19ZM11 17H13V11H15V9H13V7H11V9H9V11H11V17ZM9 13V11H7V13H9ZM17 13H15V11H17V13Z" fill="currentColor"></path></svg>
|
||||
const defaultIcon = <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M3 21H5H19H21V3H19H5H3V21ZM19 5V19H5V5H19ZM11 17H13V11H15V9H13V7H11V9H9V11H11V17ZM9 13V11H7V13H9ZM17 13H15V11H17V13Z" fill="currentColor" /></svg>
|
||||
|
||||
const Button2 = ({ title, icon }) => {
|
||||
//@ts-expect-error
|
||||
|
|
@ -23,7 +23,8 @@ const Comp = () => {
|
|||
display: 'grid',
|
||||
gridTemplateColumns: 'repeat(4, 1fr)',
|
||||
gap: 10
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
<Button2 title="/give" icon={defaultIcon} />
|
||||
<Button2 title="/tell" icon={<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> <path d="M4 2h18v16H6v2H4v-2h2v-2h14V4H4v18H2V2h2zm5 7H7v2h2V9zm2 0h2v2h-2V9zm6 0h-2v2h2V9z" fill="currentColor" /> </svg>} />
|
||||
<Button2 title="/setblock" icon={<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> <path d="M2 2h20v20H2V2zm2 2v4h4V4H4zm6 0v4h4V4h-4zm6 0v4h4V4h-4zm4 6h-4v4h4v-4zm0 6h-4v4h4v-4zm-6 4v-4h-4v4h4zm-6 0v-4H4v4h4zm-4-6h4v-4H4v4zm6-4v4h4v-4h-4z" fill="currentColor" /> </svg>} />
|
||||
|
|
|
|||
|
|
@ -29,10 +29,12 @@ export default ({ cancelClick, createClick, customizeClick, versions, defaultVer
|
|||
}, [])
|
||||
|
||||
return <Screen title="Create world" backdrop="dirt">
|
||||
<form style={{ display: 'flex' }} onSubmit={(e) => {
|
||||
e.preventDefault()
|
||||
createClick()
|
||||
}}>
|
||||
<form
|
||||
style={{ display: 'flex' }} onSubmit={(e) => {
|
||||
e.preventDefault()
|
||||
createClick()
|
||||
}}
|
||||
>
|
||||
<Input
|
||||
autoFocus
|
||||
value={title}
|
||||
|
|
@ -41,12 +43,14 @@ export default ({ cancelClick, createClick, customizeClick, versions, defaultVer
|
|||
}}
|
||||
placeholder='World name'
|
||||
/>
|
||||
<select value={version} style={{
|
||||
background: 'gray',
|
||||
color: 'white'
|
||||
}} onChange={({ target: { value } }) => {
|
||||
creatingWorldState.version = value
|
||||
}}>
|
||||
<select
|
||||
value={version} style={{
|
||||
background: 'gray',
|
||||
color: 'white'
|
||||
}} onChange={({ target: { value } }) => {
|
||||
creatingWorldState.version = value
|
||||
}}
|
||||
>
|
||||
{versions.map(({ version, label }) => {
|
||||
return <option key={version} value={version}>{label}</option>
|
||||
})}
|
||||
|
|
@ -56,14 +60,17 @@ export default ({ cancelClick, createClick, customizeClick, versions, defaultVer
|
|||
<Button onClick={() => {
|
||||
const index = worldTypes.indexOf(type)
|
||||
creatingWorldState.type = worldTypes[index === worldTypes.length - 1 ? 0 : index + 1]
|
||||
}}>World Type: {type}</Button>
|
||||
}}
|
||||
>World Type: {type}
|
||||
</Button>
|
||||
{/* <Button onClick={() => customizeClick()} disabled>
|
||||
Customize
|
||||
</Button> */}
|
||||
<Button onClick={() => {
|
||||
const index = gameModes.indexOf(gameMode)
|
||||
creatingWorldState.gameMode = gameModes[index === gameModes.length - 1 ? 0 : index + 1]
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
Gamemode: {gameMode}
|
||||
</Button>
|
||||
</div>
|
||||
|
|
@ -72,7 +79,9 @@ export default ({ cancelClick, createClick, customizeClick, versions, defaultVer
|
|||
<div style={{ display: 'flex' }}>
|
||||
<Button onClick={() => {
|
||||
cancelClick()
|
||||
}}>Cancel</Button>
|
||||
}}
|
||||
>Cancel
|
||||
</Button>
|
||||
<Button disabled={!title} onClick={createClick}>Create</Button>
|
||||
</div>
|
||||
<div className='muted' style={{ fontSize: 9 }}>Note: store important saves in folders on the drive!</div>
|
||||
|
|
@ -85,9 +94,7 @@ export const WorldCustomize = ({ backClick }) => {
|
|||
|
||||
return <Screen title='Customize world' backdrop='dirt'>
|
||||
<div className={styles.world_layers_container}>
|
||||
<div className="world_layer">
|
||||
|
||||
</div>
|
||||
<div className="world_layer" />
|
||||
</div>
|
||||
<Button onClick={backClick}>Back</Button>
|
||||
</Screen>
|
||||
|
|
|
|||
|
|
@ -60,11 +60,13 @@ export default () => {
|
|||
|
||||
return <SharedHudVars>
|
||||
<div className='crosshair' />
|
||||
{displayIndicator && <div className='crosshair-indicator' style={{
|
||||
{displayIndicator && <div
|
||||
className='crosshair-indicator' style={{
|
||||
//@ts-expect-error
|
||||
'--crosshair-indicator-size': `${indicatorSize}px`,
|
||||
borderLeft: `solid ${indicatorSize * indicatorProgress}px white`,
|
||||
backgroundColor: alternativeIndicator ? 'dodgerblue' : undefined,
|
||||
}} />}
|
||||
'--crosshair-indicator-size': `${indicatorSize}px`,
|
||||
borderLeft: `solid ${indicatorSize * indicatorProgress}px white`,
|
||||
backgroundColor: alternativeIndicator ? 'dodgerblue' : undefined,
|
||||
}}
|
||||
/>}
|
||||
</SharedHudVars>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,12 +18,16 @@ export default ({ dieReasonMessage, respawnCallback, disconnectCallback }: Props
|
|||
<MessageFormatted parts={dieReasonMessage} />
|
||||
</h5>
|
||||
<div className='deathScreen-buttons-grouped'>
|
||||
<Button label="Respawn" onClick={() => {
|
||||
respawnCallback()
|
||||
}} />
|
||||
<Button label="Disconnnect" onClick={() => {
|
||||
disconnectCallback()
|
||||
}} />
|
||||
<Button
|
||||
label="Respawn" onClick={() => {
|
||||
respawnCallback()
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
label="Disconnnect" onClick={() => {
|
||||
disconnectCallback()
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@ export default () => {
|
|||
<p>Prismarine Web Client ({bot.version})</p>
|
||||
<p>E: {entitiesCount}</p>
|
||||
<p>{dimension}</p>
|
||||
<div className={styles.empty}></div>
|
||||
<div className={styles.empty} />
|
||||
<p>XYZ: {pos.x.toFixed(3)} / {pos.y.toFixed(3)} / {pos.z.toFixed(3)}</p>
|
||||
<p>Chunk: {Math.floor(pos.x % 16)} ~ {Math.floor(pos.z % 16)} in {Math.floor(pos.x / 16)} ~ {Math.floor(pos.z / 16)}</p>
|
||||
<p>Packets: {packetsString}</p>
|
||||
|
|
@ -140,13 +140,13 @@ export default () => {
|
|||
|
||||
<p>Biome: minecraft:{loadedData.biomesArray[biomeId]?.name ?? 'unknown biome'}</p>
|
||||
<p>Day: {day}</p>
|
||||
<div className={styles.empty}></div>
|
||||
<div className={styles.empty} />
|
||||
{Object.entries(customEntries.current).map(([name, value]) => <p key={name}>{name}: {value}</p>)}
|
||||
</div>
|
||||
|
||||
<div className={styles['debug-right-side']}>
|
||||
<p>Renderer: {rendererDevice} powered by three.js r{THREE.REVISION}</p>
|
||||
<div className={styles.empty}></div>
|
||||
<div className={styles.empty} />
|
||||
{cursorBlock ? (<>
|
||||
<p>{cursorBlock.name}</p>
|
||||
{
|
||||
|
|
@ -154,7 +154,7 @@ export default () => {
|
|||
return <p key={name}>
|
||||
{name}: {
|
||||
typeof value === 'boolean' ? (
|
||||
<span style={{ color: value ? 'lightgreen' : 'red' }}>{value}</span>
|
||||
<span style={{ color: value ? 'lightgreen' : 'red' }}>{String(value)}</span>
|
||||
) : value
|
||||
}
|
||||
</p>
|
||||
|
|
|
|||
|
|
@ -67,7 +67,8 @@ export const DropdownButton = ({ text, links }: { text: string, links: DropdownB
|
|||
key={el.text}
|
||||
style={{ width: '98px', fontSize: '7px' }}
|
||||
onClick={el.clickHandler}
|
||||
>{el.text}</Button>
|
||||
>{el.text}
|
||||
</Button>
|
||||
})}
|
||||
</div>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -69,7 +69,8 @@ export default ({
|
|||
Array.from({ length: 10 }, () => 0)
|
||||
.map((num, index) => <div
|
||||
key={`food-${index}`}
|
||||
className='food'></div>)
|
||||
className='food'
|
||||
/>)
|
||||
}
|
||||
</div>
|
||||
</SharedHudVars>
|
||||
|
|
|
|||
|
|
@ -1,18 +1,20 @@
|
|||
import './GoogleButton.css'
|
||||
|
||||
export default ({ onClick }) => {
|
||||
return <button className="gsi-material-button" onClick={onClick} style={{
|
||||
transform: 'scale(0.55) translate(-20%)',
|
||||
}}>
|
||||
return <button
|
||||
className="gsi-material-button" onClick={onClick} style={{
|
||||
transform: 'scale(0.55) translate(-20%)',
|
||||
}}
|
||||
>
|
||||
<div className="gsi-material-button-state" />
|
||||
<div className="gsi-material-button-content-wrapper">
|
||||
<div className="gsi-material-button-icon">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48">
|
||||
<path fill="#EA4335" d="M24 9.5c3.54 0 6.71 1.22 9.21 3.6l6.85-6.85C35.9 2.38 30.47 0 24 0 14.62 0 6.51 5.38 2.56 13.22l7.98 6.19C12.43 13.72 17.74 9.5 24 9.5z"></path>
|
||||
<path fill="#4285F4" d="M46.98 24.55c0-1.57-.15-3.09-.38-4.55H24v9.02h12.94c-.58 2.96-2.26 5.48-4.78 7.18l7.73 6c4.51-4.18 7.09-10.36 7.09-17.65z"></path>
|
||||
<path fill="#FBBC05" d="M10.53 28.59c-.48-1.45-.76-2.99-.76-4.59s.27-3.14.76-4.59l-7.98-6.19C.92 16.46 0 20.12 0 24c0 3.88.92 7.54 2.56 10.78l7.97-6.19z"></path>
|
||||
<path fill="#34A853" d="M24 48c6.48 0 11.93-2.13 15.89-5.81l-7.73-6c-2.15 1.45-4.92 2.3-8.16 2.3-6.26 0-11.57-4.22-13.47-9.91l-7.98 6.19C6.51 42.62 14.62 48 24 48z"></path>
|
||||
<path fill="none" d="M0 0h48v48H0z"></path>
|
||||
<path fill="#EA4335" d="M24 9.5c3.54 0 6.71 1.22 9.21 3.6l6.85-6.85C35.9 2.38 30.47 0 24 0 14.62 0 6.51 5.38 2.56 13.22l7.98 6.19C12.43 13.72 17.74 9.5 24 9.5z" />
|
||||
<path fill="#4285F4" d="M46.98 24.55c0-1.57-.15-3.09-.38-4.55H24v9.02h12.94c-.58 2.96-2.26 5.48-4.78 7.18l7.73 6c4.51-4.18 7.09-10.36 7.09-17.65z" />
|
||||
<path fill="#FBBC05" d="M10.53 28.59c-.48-1.45-.76-2.99-.76-4.59s.27-3.14.76-4.59l-7.98-6.19C.92 16.46 0 20.12 0 24c0 3.88.92 7.54 2.56 10.78l7.97-6.19z" />
|
||||
<path fill="#34A853" d="M24 48c6.48 0 11.93-2.13 15.89-5.81l-7.73-6c-2.15 1.45-4.92 2.3-8.16 2.3-6.26 0-11.57-4.22-13.47-9.91l-7.98 6.19C6.51 42.62 14.62 48 24 48z" />
|
||||
<path fill="none" d="M0 0h48v48H0z" />
|
||||
</svg>
|
||||
</div>
|
||||
<span className="gsi-material-button-contents">Continue with Google</span>
|
||||
|
|
|
|||
|
|
@ -98,7 +98,8 @@ export default ({
|
|||
Array.from({ length: 10 }, () => 0)
|
||||
.map((num, index) => <div
|
||||
key={`heart-${index}`}
|
||||
className='heart'></div>)
|
||||
className='heart'
|
||||
/>)
|
||||
}
|
||||
</div>
|
||||
</SharedHudVars>
|
||||
|
|
|
|||
|
|
@ -45,11 +45,14 @@ export default () => {
|
|||
bottom: 20,
|
||||
left: 8,
|
||||
pointerEvents: 'none',
|
||||
}}>
|
||||
<img src={dataUrl} style={{
|
||||
width: 92,
|
||||
height: 92,
|
||||
imageRendering: 'pixelated',
|
||||
}} />
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src={dataUrl} style={{
|
||||
width: 92,
|
||||
height: 92,
|
||||
imageRendering: 'pixelated',
|
||||
}}
|
||||
/>
|
||||
</div> : null
|
||||
}
|
||||
|
|
|
|||
|
|
@ -205,16 +205,18 @@ export default () => {
|
|||
return <SharedHudVars>
|
||||
<ItemName itemKey={itemKey} />
|
||||
<Portal>
|
||||
<div className='hotbar' ref={container} style={{
|
||||
position: 'fixed',
|
||||
left: 0,
|
||||
right: 0,
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
zIndex: hasModals ? 1 : 8,
|
||||
pointerEvents: 'none',
|
||||
bottom: 'var(--hud-bottom-raw)'
|
||||
}} />
|
||||
<div
|
||||
className='hotbar' ref={container} style={{
|
||||
position: 'fixed',
|
||||
left: 0,
|
||||
right: 0,
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
zIndex: hasModals ? 1 : 8,
|
||||
pointerEvents: 'none',
|
||||
bottom: 'var(--hud-bottom-raw)'
|
||||
}}
|
||||
/>
|
||||
</Portal>
|
||||
</SharedHudVars>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -87,10 +87,12 @@ export default ({ indicators, effects }: { indicators: typeof defaultIndicatorsS
|
|||
return <div className='effectsScreen-container'>
|
||||
<div className='indicators-container'>
|
||||
{
|
||||
indicatorsMapped.map((indicator) => <div key={indicator.icon} style={{
|
||||
opacity: indicator.state ? 1 : 0,
|
||||
transition: 'opacity 0.1s',
|
||||
}}>
|
||||
indicatorsMapped.map((indicator) => <div
|
||||
key={indicator.icon} style={{
|
||||
opacity: indicator.state ? 1 : 0,
|
||||
transition: 'opacity 0.1s',
|
||||
}}
|
||||
>
|
||||
<PixelartIcon iconName={indicator.icon} />
|
||||
</div>)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,9 @@ export default ({ autoFocus, rootStyles, inputRef, ...inputProps }: Props) => {
|
|||
}, [])
|
||||
|
||||
return <div className={styles.container} style={rootStyles}>
|
||||
<input ref={ref} className={styles.input} autoComplete='off' autoCapitalize='off' autoCorrect='off' autoSave='off' spellCheck='false' {...inputProps} />
|
||||
<input
|
||||
ref={ref} className={styles.input} autoComplete='off' autoCapitalize='off' autoCorrect='off' autoSave='off' spellCheck='false'
|
||||
{...inputProps}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ export default ({
|
|||
})}
|
||||
<Button
|
||||
onClick={() => addNewCommand(group)}
|
||||
icon={'pixelarticons:add-box'}
|
||||
icon="pixelarticons:add-box"
|
||||
style={{
|
||||
alignSelf: 'center'
|
||||
}}
|
||||
|
|
@ -103,9 +103,12 @@ const CustomCommandContainer = ({
|
|||
if (!config) return null
|
||||
|
||||
return config.type === 'select'
|
||||
? <select key={indexInput} onChange={(e) => {
|
||||
setInputValue(commandKey, indexInput, e.target.value)
|
||||
}}>{config.options.map((option) => <option key={option} value={option}>{option}</option>)}</select>
|
||||
? <select
|
||||
key={indexInput} onChange={(e) => {
|
||||
setInputValue(commandKey, indexInput, e.target.value)
|
||||
}}
|
||||
>{config.options.map((option) => <option key={option} value={option}>{option}</option>)}
|
||||
</select>
|
||||
: <Input key={indexInput} rootStyles={{ width: '99%' }} placeholder={config.placeholder} value={inputs[indexInput] ?? ''} onChange={(e) => setInputValue(commandKey, indexInput, e.target.value)} />
|
||||
})}
|
||||
<div className={styles.actionBinds}>
|
||||
|
|
@ -116,21 +119,22 @@ const CustomCommandContainer = ({
|
|||
resetBinding('custom', commandKey, 'keyboard')
|
||||
}}
|
||||
className={styles['undo-keyboard']}
|
||||
icon={'pixelarticons:undo'}
|
||||
icon="pixelarticons:undo"
|
||||
/>
|
||||
: null}
|
||||
: null
|
||||
}
|
||||
|
||||
{[0, 1].map((key, index) => <ButtonWithMatchesAlert
|
||||
key={`custom-keyboard-${group}-${commandKey}-${index}`}
|
||||
group={'custom'}
|
||||
group="custom"
|
||||
action={commandKey}
|
||||
index={index}
|
||||
inputType={'keyboard'}
|
||||
inputType="keyboard"
|
||||
keys={keys}
|
||||
gamepad={gamepad}
|
||||
/>)}
|
||||
|
||||
<div style={{ marginRight: 'auto' }} ></div>
|
||||
<div style={{ marginRight: 'auto' }} />
|
||||
|
||||
{
|
||||
userConfig?.['custom']?.[commandKey]?.gamepad ? <Button
|
||||
|
|
@ -139,14 +143,15 @@ const CustomCommandContainer = ({
|
|||
resetBinding('custom', commandKey, 'gamepad')
|
||||
}}
|
||||
className={styles['undo-keyboard']}
|
||||
icon={'pixelarticons:undo'}
|
||||
icon="pixelarticons:undo"
|
||||
/>
|
||||
: null}
|
||||
: null
|
||||
}
|
||||
<ButtonWithMatchesAlert
|
||||
group={'custom'}
|
||||
group="custom"
|
||||
action={commandKey}
|
||||
index={0}
|
||||
inputType={'gamepad'}
|
||||
inputType="gamepad"
|
||||
keys={keys}
|
||||
gamepad={gamepad}
|
||||
/>
|
||||
|
|
@ -157,9 +162,8 @@ const CustomCommandContainer = ({
|
|||
return newConfig
|
||||
})
|
||||
}}
|
||||
|
||||
style={{ color: 'red' }}
|
||||
icon={'pixelarticons:delete'}
|
||||
icon="pixelarticons:delete"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -178,16 +178,19 @@ export default ({
|
|||
setUserConfig,
|
||||
handleClick,
|
||||
bindsMap: bindsMap.current
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
<Screen title="Keybindings" backdrop>
|
||||
{awaitingInputType && <AwaitingInputOverlay isGamepad={awaitingInputType === 'gamepad'} />}
|
||||
<div className={styles.container}
|
||||
<div
|
||||
className={styles.container}
|
||||
ref={containerRef}
|
||||
>
|
||||
<Button
|
||||
onClick={() => { hideModal() }}
|
||||
style={{ alignSelf: 'center' }}
|
||||
>Back</Button>
|
||||
>Back
|
||||
</Button>
|
||||
|
||||
{Object.entries(commands).map(([group, actions], index) => {
|
||||
if (group === 'custom') return null
|
||||
|
|
@ -198,7 +201,8 @@ export default ({
|
|||
color: 'rgba(255, 255, 255, 0.7)',
|
||||
fontSize: '6px',
|
||||
textAlign: 'center'
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
Note: Left, right and middle click keybindings are hardcoded and cannot be changed currently.
|
||||
</div>
|
||||
) : null}
|
||||
|
|
@ -221,7 +225,7 @@ export default ({
|
|||
group={group}
|
||||
action={action}
|
||||
index={index}
|
||||
inputType={'keyboard'}
|
||||
inputType="keyboard"
|
||||
keys={keys}
|
||||
gamepad={gamepad}
|
||||
/>)}
|
||||
|
|
@ -244,7 +248,7 @@ export default ({
|
|||
group={group}
|
||||
action={action}
|
||||
index={0}
|
||||
inputType={'gamepad'}
|
||||
inputType="gamepad"
|
||||
keys={keys}
|
||||
gamepad={gamepad}
|
||||
/>
|
||||
|
|
@ -306,7 +310,7 @@ export const ButtonWithMatchesAlert = ({
|
|||
//@ts-format-ignore-region
|
||||
<div id={`bind-warning-${group}-${action}-${inputType}-${index}`} className={styles['matched-bind-warning']}>
|
||||
<PixelartIcon
|
||||
iconName={'alert'}
|
||||
iconName="alert"
|
||||
width={5}
|
||||
styles={{
|
||||
display: 'flex',
|
||||
|
|
@ -316,13 +320,12 @@ export const ButtonWithMatchesAlert = ({
|
|||
}}
|
||||
/>
|
||||
<div>
|
||||
This bind is already in use. <span></span>
|
||||
This bind is already in use. <span />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
//@ts-format-ignore-endregion
|
||||
: null
|
||||
}
|
||||
: null}
|
||||
</div>
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ export default ({
|
|||
<div className={styles.root}>
|
||||
<div className={styles['game-title']}>
|
||||
<div className={styles.minecraft}>
|
||||
<div className={styles.edition}></div>
|
||||
<div className={styles.edition} />
|
||||
<span className={styles.splash}>Prismarine is a beautiful block</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -108,10 +108,13 @@ export default ({
|
|||
Prismarine Web Client {versionStatus}
|
||||
</span>
|
||||
<span className={styles['product-description']}>
|
||||
<a style={{
|
||||
color: 'lightgray',
|
||||
fontSize: 9,
|
||||
}} href='https://privacy.mcraft.fun'>Privacy Policy</a>
|
||||
<a
|
||||
style={{
|
||||
color: 'lightgray',
|
||||
fontSize: 9,
|
||||
}} href='https://privacy.mcraft.fun'
|
||||
>Privacy Policy
|
||||
</a>
|
||||
<span>A Minecraft client in the browser!</span>
|
||||
</span>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -35,24 +35,34 @@ export default () => {
|
|||
|
||||
// ios note: just don't use <button>
|
||||
return <div ref={elRef} className={styles['mobile-top-btns']} id="mobile-top">
|
||||
<div className={styles['debug-btn']} onPointerDown={(e) => {
|
||||
window.dispatchEvent(new MouseEvent('mousedown', { button: 1 }))
|
||||
}}>S</div>
|
||||
<div className={styles['debug-btn']} onPointerDown={(e) => {
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', { code: 'F3' }))
|
||||
document.dispatchEvent(new KeyboardEvent('keyup', { code: 'F3' }))
|
||||
}} { ...longPressEvent }>F3</div>
|
||||
<div className={styles['chat-btn']} onPointerDown={(e) => {
|
||||
e.stopPropagation()
|
||||
if (activeModalStack.at(-1)?.reactType === 'chat') {
|
||||
hideCurrentModal()
|
||||
} else {
|
||||
showModal({ reactType: 'chat' })
|
||||
}
|
||||
}}></div>
|
||||
<div className={styles['pause-btn']} onPointerDown={(e) => {
|
||||
e.stopPropagation()
|
||||
showModal({ reactType: 'pause-screen' })
|
||||
}}></div>
|
||||
<div
|
||||
className={styles['debug-btn']} onPointerDown={(e) => {
|
||||
window.dispatchEvent(new MouseEvent('mousedown', { button: 1 }))
|
||||
}}
|
||||
>S
|
||||
</div>
|
||||
<div
|
||||
className={styles['debug-btn']} onPointerDown={(e) => {
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', { code: 'F3' }))
|
||||
document.dispatchEvent(new KeyboardEvent('keyup', { code: 'F3' }))
|
||||
}} {...longPressEvent}
|
||||
>F3
|
||||
</div>
|
||||
<div
|
||||
className={styles['chat-btn']} onPointerDown={(e) => {
|
||||
e.stopPropagation()
|
||||
if (activeModalStack.at(-1)?.reactType === 'chat') {
|
||||
hideCurrentModal()
|
||||
} else {
|
||||
showModal({ reactType: 'chat' })
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
className={styles['pause-btn']} onPointerDown={(e) => {
|
||||
e.stopPropagation()
|
||||
showModal({ reactType: 'pause-screen' })
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ const AddElem = ({ elem }) => {
|
|||
}
|
||||
}, [])
|
||||
|
||||
return <div ref={ref}></div>
|
||||
return <div ref={ref} />
|
||||
}
|
||||
|
||||
// for (const key of Object.keys(viewer.world.sectionObjects)) {
|
||||
|
|
|
|||
|
|
@ -33,8 +33,11 @@ export default () => {
|
|||
}}
|
||||
backdrop={false}
|
||||
>
|
||||
<Button style={{ marginTop: 30 }} onClick={() => {
|
||||
hideCurrentModal()
|
||||
}}>Back</Button>
|
||||
<Button
|
||||
style={{ marginTop: 30 }} onClick={() => {
|
||||
hideCurrentModal()
|
||||
}}
|
||||
>Back
|
||||
</Button>
|
||||
</Screen>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,29 +32,32 @@ export default ({ type = 'message', message, subMessage = '', open, icon = '', a
|
|||
{state => {
|
||||
const addStyles = { ...basicStyle, ...stateStyles[state] }
|
||||
|
||||
return <div className={`app-notification ${isError ? 'error-notification' : ''}`} onClick={action} style={{
|
||||
position: 'fixed',
|
||||
top: 0,
|
||||
right: 0,
|
||||
width: '180px',
|
||||
whiteSpace: 'nowrap',
|
||||
fontSize: '9px',
|
||||
display: 'flex',
|
||||
gap: 4,
|
||||
alignItems: 'center',
|
||||
padding: '3px 5px',
|
||||
background: isError ? 'rgba(255, 0, 0, 0.7)' : 'rgba(0, 0, 0, 0.7)',
|
||||
borderRadius: '0 0 0 5px',
|
||||
pointerEvents: action ? '' : 'none',
|
||||
zIndex: 1200, // even above stats
|
||||
...addStyles
|
||||
}}>
|
||||
return <div
|
||||
className={`app-notification ${isError ? 'error-notification' : ''}`} onClick={action} style={{
|
||||
position: 'fixed',
|
||||
top: 0,
|
||||
right: 0,
|
||||
width: '180px',
|
||||
whiteSpace: 'nowrap',
|
||||
fontSize: '9px',
|
||||
display: 'flex',
|
||||
gap: 4,
|
||||
alignItems: 'center',
|
||||
padding: '3px 5px',
|
||||
background: isError ? 'rgba(255, 0, 0, 0.7)' : 'rgba(0, 0, 0, 0.7)',
|
||||
borderRadius: '0 0 0 5px',
|
||||
pointerEvents: action ? '' : 'none',
|
||||
zIndex: 1200, // even above stats
|
||||
...addStyles
|
||||
}}
|
||||
>
|
||||
<PixelartIcon iconName={icon} styles={{ fontSize: 12 }} />
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: 2,
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
{message}
|
||||
</div>
|
||||
|
|
@ -62,7 +65,9 @@ export default ({ type = 'message', message, subMessage = '', open, icon = '', a
|
|||
fontSize: '7px',
|
||||
whiteSpace: 'nowrap',
|
||||
color: 'lightgray',
|
||||
}}>{subMessage}</div>
|
||||
}}
|
||||
>{subMessage}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}}
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ export default () => {
|
|||
if (!isModalActive) return null
|
||||
return <Screen title='Game Menu'>
|
||||
<Button
|
||||
icon={'pixelarticons:folder'}
|
||||
icon="pixelarticons:folder"
|
||||
style={{ position: 'fixed', top: '5px', left: 'calc(env(safe-area-inset-left) + 5px)' }}
|
||||
onClick={async () => openWorldActions()}
|
||||
/>
|
||||
|
|
@ -126,14 +126,14 @@ export default () => {
|
|||
{(navigator.share as typeof navigator.share | undefined) ? (
|
||||
<Button
|
||||
className="button"
|
||||
icon={'pixelarticons:arrow-up'}
|
||||
icon="pixelarticons:arrow-up"
|
||||
style={{ width: '20px' }}
|
||||
onClick={async () => clickWebShareButton()}
|
||||
/>
|
||||
) : null}
|
||||
<Button
|
||||
className="button"
|
||||
icon={'pixelarticons:dice'}
|
||||
icon="pixelarticons:dice"
|
||||
style={{ width: '20px' }}
|
||||
onClick={async () => clickJoinLinkButton(true)}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -11,9 +11,11 @@ export default ({
|
|||
if (width !== undefined) styles = { width, height: width, ...styles }
|
||||
iconName = iconName.replace('pixelarticons:', '')
|
||||
|
||||
return <div style={{
|
||||
...styles
|
||||
}} onClick={onClick} className={`${`pixelart-icons-font-${iconName}`} ${className ?? ''}`} />
|
||||
return <div
|
||||
style={{
|
||||
...styles
|
||||
}} onClick={onClick} className={`${`pixelart-icons-font-${iconName}`} ${className ?? ''}`}
|
||||
/>
|
||||
}
|
||||
|
||||
export const pixelartIcons = new Proxy({} as PixelartIconsGenerated, {
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ interface Props {
|
|||
export default ({ title, children, backdrop = true, style, className }: Props) => {
|
||||
return (
|
||||
<>
|
||||
{backdrop === 'dirt' ? <div className='dirt-bg'></div> : backdrop ? <div className="backdrop"></div> : null}
|
||||
{backdrop === 'dirt' ? <div className='dirt-bg' /> : backdrop ? <div className="backdrop" /> : null}
|
||||
<div className={`fullscreen ${className}`} style={{ overflow: 'auto', ...style }}>
|
||||
<div className="screen-content">
|
||||
<div className="screen-title">{title}</div>
|
||||
|
|
|
|||
|
|
@ -33,13 +33,19 @@ export default () => {
|
|||
if (!isModalActive) return
|
||||
|
||||
return <Screen title={title} backdrop>
|
||||
{options.map(option => <Button key={option} onClick={() => {
|
||||
hideCurrentModal()
|
||||
resolve(option)
|
||||
}}>{option}</Button>)}
|
||||
{showCancel && <Button style={{ marginTop: 30 }} onClick={() => {
|
||||
hideCurrentModal()
|
||||
resolve(undefined)
|
||||
}}>Cancel</Button>}
|
||||
{options.map(option => <Button
|
||||
key={option} onClick={() => {
|
||||
hideCurrentModal()
|
||||
resolve(option)
|
||||
}}
|
||||
>{option}
|
||||
</Button>)}
|
||||
{showCancel && <Button
|
||||
style={{ marginTop: 30 }} onClick={() => {
|
||||
hideCurrentModal()
|
||||
resolve(undefined)
|
||||
}}
|
||||
>Cancel
|
||||
</Button>}
|
||||
</Screen>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ const meta: Meta<typeof ServersList> = {
|
|||
accounts={['testting']}
|
||||
onConfirm={(info) => {
|
||||
console.log('add server', info)
|
||||
}} /> :
|
||||
}}
|
||||
/> :
|
||||
<ServersList
|
||||
worldData={[{
|
||||
name: 'test',
|
||||
|
|
|
|||
|
|
@ -56,45 +56,50 @@ export default ({ initialProxies, updateProxies: updateProxiesProp, joinServer,
|
|||
const [serverIp, setServerIp] = React.useState('')
|
||||
const [save, setSave] = React.useState(true)
|
||||
|
||||
return <Singleplayer {...props}
|
||||
firstRowChildrenOverride={<form style={{ width: '100%', display: 'flex', justifyContent: 'center' }} onSubmit={(e) => {
|
||||
e.preventDefault()
|
||||
let ip = serverIp
|
||||
let version
|
||||
let msAuth = false
|
||||
const parts = ip.split(':')
|
||||
if (parts.at(-1) === 'ms') {
|
||||
msAuth = true
|
||||
parts.pop()
|
||||
}
|
||||
if (parts.length > 1 && parts.at(-1)!.includes('.')) {
|
||||
version = parts.at(-1)!
|
||||
ip = parts.slice(0, -1).join(':')
|
||||
}
|
||||
joinServer({
|
||||
ip,
|
||||
versionOverride: version,
|
||||
authenticatedAccountOverride: msAuth ? true : undefined, // todo popup selector
|
||||
}, {
|
||||
shouldSave: save,
|
||||
})
|
||||
}}
|
||||
return <Singleplayer
|
||||
{...props}
|
||||
firstRowChildrenOverride={<form
|
||||
style={{ width: '100%', display: 'flex', justifyContent: 'center' }} onSubmit={(e) => {
|
||||
e.preventDefault()
|
||||
let ip = serverIp
|
||||
let version
|
||||
let msAuth = false
|
||||
const parts = ip.split(':')
|
||||
if (parts.at(-1) === 'ms') {
|
||||
msAuth = true
|
||||
parts.pop()
|
||||
}
|
||||
if (parts.length > 1 && parts.at(-1)!.includes('.')) {
|
||||
version = parts.at(-1)!
|
||||
ip = parts.slice(0, -1).join(':')
|
||||
}
|
||||
joinServer({
|
||||
ip,
|
||||
versionOverride: version,
|
||||
authenticatedAccountOverride: msAuth ? true : undefined, // todo popup selector
|
||||
}, {
|
||||
shouldSave: save,
|
||||
})
|
||||
}}
|
||||
>
|
||||
<div style={{ display: 'flex', gap: 5, alignItems: 'center' }}>
|
||||
{/* todo history */}
|
||||
<Input required placeholder='Quick Connect IP (:version)' value={serverIp} onChange={({ target: { value } }) => setServerIp(value)} />
|
||||
<label style={{ fontSize: 10, display: 'flex', alignItems: 'center', gap: 5, height: '100%', marginTop: '-1px' }}>
|
||||
<input type='checkbox' checked={save}
|
||||
<input
|
||||
type='checkbox' checked={save}
|
||||
style={{ borderRadius: 0 }}
|
||||
onChange={({ target: { checked } }) => setSave(checked)}
|
||||
/> Save</label>
|
||||
/> Save
|
||||
</label>
|
||||
<Button style={{ width: 90 }} type='submit'>Join Server</Button>
|
||||
</div>
|
||||
</form>}
|
||||
searchRowChildrenOverride={
|
||||
<div style={{
|
||||
// marginTop: 12,
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
<div style={{ display: 'flex', gap: 3, alignItems: 'center' }}>
|
||||
<span style={{ color: 'lightgray', fontSize: 14 }}>Proxy:</span>
|
||||
<div {...autocomplete.getRootProps()} style={{ position: 'relative', width: 130 }}>
|
||||
|
|
@ -104,11 +109,13 @@ export default ({ initialProxies, updateProxies: updateProxiesProp, joinServer,
|
|||
status='unknown'
|
||||
ip=''
|
||||
/>
|
||||
{autocomplete.groupedOptions && <ul {...autocomplete.getListboxProps()} style={{
|
||||
position: 'absolute',
|
||||
zIndex: 1,
|
||||
{autocomplete.groupedOptions && <ul
|
||||
{...autocomplete.getListboxProps()} style={{
|
||||
position: 'absolute',
|
||||
zIndex: 1,
|
||||
// marginTop: 10,
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{autocomplete.groupedOptions.map((proxy, index) => {
|
||||
const { itemRef, ...optionProps } = autocomplete.getOptionProps({ option: proxy, index })
|
||||
return <ProxyRender {...optionProps as any} ip={proxy} disabled />
|
||||
|
|
@ -144,9 +151,11 @@ const ProxyRender = ({ status, ip, inputRef, value, setValue, ...props }: {
|
|||
success: 'cellular-signal-3',
|
||||
}
|
||||
|
||||
return <div style={{
|
||||
position: 'relative',
|
||||
}} {...props}>
|
||||
return <div
|
||||
style={{
|
||||
position: 'relative',
|
||||
}} {...props}
|
||||
>
|
||||
<Input
|
||||
inputRef={inputRef}
|
||||
style={{
|
||||
|
|
@ -165,7 +174,8 @@ const ProxyRender = ({ status, ip, inputRef, value, setValue, ...props }: {
|
|||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: 2
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
<PixelartIcon iconName={iconPerStatus.unknown} />
|
||||
<div style={{
|
||||
fontSize: 10,
|
||||
|
|
@ -174,7 +184,8 @@ const ProxyRender = ({ status, ip, inputRef, value, setValue, ...props }: {
|
|||
width: '100%',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{ip.replace(/^https?:\/\//, '')}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -5,9 +5,11 @@ import SignEditor from './SignEditor'
|
|||
const meta: Meta<typeof SignEditor> = {
|
||||
component: SignEditor,
|
||||
render (args) {
|
||||
return <SignEditor {...args} handleClick={(result) => {
|
||||
console.log('handleClick', result)
|
||||
}} />
|
||||
return <SignEditor
|
||||
{...args} handleClick={(result) => {
|
||||
console.log('handleClick', result)
|
||||
}}
|
||||
/>
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -48,11 +48,12 @@ export default ({ handleInput, isWysiwyg, handleClick }: Props) => {
|
|||
const nextElem = elements[focusedElemIndex + dir]
|
||||
nextElem?.focus()
|
||||
}
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
<div className='signs-editor-inner-container'>
|
||||
<img className='signs-editor-bg-image' src={imageSource} alt='' />
|
||||
{isWysiwyg ? (
|
||||
<p ref={prosemirrorContainer} className='wysiwyg-editor'></p>
|
||||
<p ref={prosemirrorContainer} className='wysiwyg-editor' />
|
||||
) : [1, 2, 3, 4].map((value, index) => {
|
||||
return <input
|
||||
className='sign-editor'
|
||||
|
|
@ -61,23 +62,25 @@ export default ({ handleInput, isWysiwyg, handleClick }: Props) => {
|
|||
maxLength={15} // overriden by handleInput
|
||||
onChange={(e) => {
|
||||
handleInput(e.currentTarget)
|
||||
}} />
|
||||
})
|
||||
}
|
||||
<Button onClick={async () => {
|
||||
if (handleClick) {
|
||||
if (isWysiwyg) {
|
||||
const text = markdownToFormattedText(editorView.current!.content)
|
||||
handleClick({ dataText: text })
|
||||
} else {
|
||||
const text = [] as string[]
|
||||
for (const input of document.getElementsByClassName('sign-editor')) {
|
||||
text.push((input as HTMLInputElement).value)
|
||||
}}
|
||||
/>
|
||||
})}
|
||||
<Button
|
||||
onClick={async () => {
|
||||
if (handleClick) {
|
||||
if (isWysiwyg) {
|
||||
const text = markdownToFormattedText(editorView.current!.content)
|
||||
handleClick({ dataText: text })
|
||||
} else {
|
||||
const text = [] as string[]
|
||||
for (const input of document.getElementsByClassName('sign-editor')) {
|
||||
text.push((input as HTMLInputElement).value)
|
||||
}
|
||||
handleClick({ plainText: text })
|
||||
}
|
||||
handleClick({ plainText: text })
|
||||
}
|
||||
}
|
||||
}} className='sign-editor-button' label={'Done'} />
|
||||
}} className='sign-editor-button' label="Done"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,8 +4,7 @@ import SignInMessage from './SignInMessage'
|
|||
const meta: Meta<{ open }> = {
|
||||
component: SignInMessage as any,
|
||||
render ({ open }) {
|
||||
return <SignInMessage
|
||||
/>
|
||||
return <SignInMessage />
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -36,7 +36,8 @@ export default ({
|
|||
height: 213,
|
||||
color: 'black',
|
||||
// borderRadius: 8,
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
<div style={{
|
||||
// fontFamily: 'monospace',
|
||||
fontSize: 18,
|
||||
|
|
@ -46,18 +47,22 @@ export default ({
|
|||
textAlign: 'center',
|
||||
marginTop: -5,
|
||||
userSelect: 'all',
|
||||
}}>{code}</div>
|
||||
}}
|
||||
>{code}
|
||||
</div>
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
fontSize: 12,
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
Waiting... <PixelartIcon iconName='clock' /> {timeLeft}
|
||||
</div>
|
||||
<div style={{
|
||||
fontSize: 12,
|
||||
marginTop: 10,
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
To join a Minecraft server {connectingServer} using your Microsoft account, you need to visit{' '}
|
||||
<a
|
||||
href={directLink}
|
||||
|
|
@ -67,7 +72,8 @@ export default ({
|
|||
fontWeight: 600,
|
||||
}}
|
||||
target='_blank'
|
||||
>Direct Link</a>
|
||||
>Direct Link
|
||||
</a>
|
||||
{' '} or {' '}
|
||||
<a
|
||||
href={loginLink}
|
||||
|
|
@ -77,7 +83,8 @@ export default ({
|
|||
fontWeight: 600,
|
||||
}}
|
||||
target='_blank'
|
||||
>{loginLink}</a>
|
||||
>{loginLink}
|
||||
</a>
|
||||
{' '}
|
||||
and enter the code above.
|
||||
</div>
|
||||
|
|
@ -85,7 +92,8 @@ export default ({
|
|||
fontSize: 12,
|
||||
marginTop: 5,
|
||||
color: 'gray'
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
<PixelartIcon iconName='alert' /> Join only <b>vanilla servers</b>! This client is detectable and may result in a ban by anti-cheat plugins.
|
||||
</div>}
|
||||
{setSaveToken && <label style={{
|
||||
|
|
@ -94,7 +102,8 @@ export default ({
|
|||
alignItems: 'center',
|
||||
gap: 5,
|
||||
marginTop: 4,
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
<input type='checkbox' defaultChecked={defaultSaveToken} onChange={e => setSaveToken(e.target.checked)} />{' '}
|
||||
Save account token in this browser
|
||||
</label>}
|
||||
|
|
@ -104,6 +113,7 @@ export default ({
|
|||
marginTop: -5,
|
||||
}}
|
||||
onClick={onCancel}
|
||||
>Cancel</Button>
|
||||
>Cancel
|
||||
</Button>
|
||||
</Screen>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,12 +45,14 @@ const World = ({ name, isFocused, title, lastPlayed, size, detail = '', onFocus,
|
|||
return filesize(size)
|
||||
}, [size])
|
||||
|
||||
return <div className={classNames(styles.world_root, isFocused ? styles.world_focused : undefined)} tabIndex={0} onFocus={() => onFocus?.(name)} onKeyDown={(e) => {
|
||||
if (e.code === 'Enter' || e.code === 'Space') {
|
||||
e.preventDefault()
|
||||
onInteraction?.(e.code === 'Enter' ? 'enter' : 'space')
|
||||
}
|
||||
}} onDoubleClick={() => onInteraction?.('enter')}>
|
||||
return <div
|
||||
className={classNames(styles.world_root, isFocused ? styles.world_focused : undefined)} tabIndex={0} onFocus={() => onFocus?.(name)} onKeyDown={(e) => {
|
||||
if (e.code === 'Enter' || e.code === 'Space') {
|
||||
e.preventDefault()
|
||||
onInteraction?.(e.code === 'Enter' ? 'enter' : 'space')
|
||||
}
|
||||
}} onDoubleClick={() => onInteraction?.('enter')}
|
||||
>
|
||||
<img className={`${styles.world_image} ${iconSrc ? '' : styles.image_missing}`} src={iconSrc ?? missingWorldPreview} alt='world preview' />
|
||||
<div className={styles.world_info}>
|
||||
<div className={styles.world_title}>
|
||||
|
|
@ -134,18 +136,22 @@ export default ({
|
|||
<Input autoFocus value={search} onChange={({ target: { value } }) => setSearch(value)} />
|
||||
</div>}
|
||||
<div className={classNames(styles.content, !worldData && styles.content_loading)}>
|
||||
<Tabs tabs={Object.keys(providers)} disabledTabs={disabledProviders} activeTab={activeProvider ?? ''} labels={providers} onTabChange={(tab) => {
|
||||
setActiveProvider?.(tab as any)
|
||||
}} fullSize />
|
||||
<Tabs
|
||||
tabs={Object.keys(providers)} disabledTabs={disabledProviders} activeTab={activeProvider ?? ''} labels={providers} onTabChange={(tab) => {
|
||||
setActiveProvider?.(tab as any)
|
||||
}} fullSize
|
||||
/>
|
||||
<div style={{
|
||||
marginTop: 3,
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{
|
||||
providerActions && <div style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
// overflow: 'auto',
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
<span style={{ fontSize: 9, marginRight: 3 }}>Actions: </span> {Object.entries(providerActions).map(([label, action]) => (
|
||||
typeof action === 'function' ? <Button key={label} onClick={action} style={{ width: 100 }}>{label}</Button> : <Fragment key={label}>{action}</Fragment>
|
||||
))}
|
||||
|
|
@ -154,15 +160,19 @@ export default ({
|
|||
{
|
||||
worldData
|
||||
? worldData.filter(data => data.title.toLowerCase().includes(search.toLowerCase())).map(({ name, size, detail, ...rest }) => (
|
||||
<World {...rest} size={size} name={name} onFocus={setFocusedWorld} isFocused={focusedWorld === name} key={name} onInteraction={(interaction) => {
|
||||
if (interaction === 'enter') onWorldAction('load', name)
|
||||
else if (interaction === 'space') firstButton.current?.focus()
|
||||
}} detail={detail} />
|
||||
<World
|
||||
{...rest} size={size} name={name} onFocus={setFocusedWorld} isFocused={focusedWorld === name} key={name} onInteraction={(interaction) => {
|
||||
if (interaction === 'enter') onWorldAction('load', name)
|
||||
else if (interaction === 'space') firstButton.current?.focus()
|
||||
}}
|
||||
detail={detail}
|
||||
/>
|
||||
))
|
||||
: <div style={{
|
||||
fontSize: 10,
|
||||
color: error ? 'red' : 'lightgray',
|
||||
}}>{error || 'Loading (check #dev console if loading too long)...'}</div>
|
||||
}}>{error || 'Loading (check #dev console if loading too long)...'}
|
||||
</div>
|
||||
}
|
||||
{
|
||||
warning && <div style={{
|
||||
|
|
@ -170,7 +180,8 @@ export default ({
|
|||
color: '#ffa500ba',
|
||||
marginTop: 5,
|
||||
textAlign: 'center',
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{warning} {warningAction && <a onClick={warningAction}>{warningActionLabel}</a>}
|
||||
</div>
|
||||
}
|
||||
|
|
@ -186,7 +197,7 @@ export default ({
|
|||
<Button style={{ width: 100 }} disabled={!focusedWorld} onClick={() => onWorldAction('delete', focusedWorld)}>Delete</Button>
|
||||
{serversLayout ?
|
||||
<Button style={{ width: 100 }} onClick={() => onGeneralAction('create')}>Add</Button> :
|
||||
<Button style={{ width: 100 }} /* disabled={!focusedWorld} */ onClick={() => onWorldAction('edit', focusedWorld)} disabled>Edit</Button>}
|
||||
<Button style={{ width: 100 }} onClick={() => onWorldAction('edit', focusedWorld)} disabled>Edit</Button>}
|
||||
<Button style={{ width: 100 }} onClick={() => onGeneralAction('cancel')}>Cancel</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -73,8 +73,8 @@ const Slider: React.FC<Props> = ({
|
|||
fireValueUpdate(true)
|
||||
}}
|
||||
/>
|
||||
<div className={styles.disabled} title={disabledReason}></div>
|
||||
<div className={styles['slider-thumb']} style={{ left: `calc((100% * ${ratio}) - (8px * ${ratio}))` }}></div>
|
||||
<div className={styles.disabled} title={disabledReason} />
|
||||
<div className={styles['slider-thumb']} style={{ left: `calc((100% * ${ratio}) - (8px * ${ratio}))` }} />
|
||||
<label className={styles.label}>
|
||||
{label}: {valueDisplay ?? value} {unit}
|
||||
</label>
|
||||
|
|
|
|||
|
|
@ -17,13 +17,15 @@ const SoundRow = ({ sound, children }) => {
|
|||
<span style={{ fontSize: 12, marginRight: 2, ...isMuted ? { color: '#af1c1c' } : {} }}>{sound}</span>
|
||||
{children}
|
||||
</div>
|
||||
<Button icon={isMuted ? 'pixelarticons:music' : 'pixelarticons:close'} onClick={() => {
|
||||
if (isMuted) {
|
||||
options.mutedSounds.splice(options.mutedSounds.indexOf(sound), 1)
|
||||
} else {
|
||||
options.mutedSounds.push(sound)
|
||||
}
|
||||
}}></Button>
|
||||
<Button
|
||||
icon={isMuted ? 'pixelarticons:music' : 'pixelarticons:close'} onClick={() => {
|
||||
if (isMuted) {
|
||||
options.mutedSounds.splice(options.mutedSounds.indexOf(sound), 1)
|
||||
} else {
|
||||
options.mutedSounds.push(sound)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,24 +16,30 @@ export default ({ tabs, activeTab, labels, onTabChange, fullSize, style, disable
|
|||
width: fullSize ? '100%' : undefined,
|
||||
display: fullSize ? 'flex' : undefined,
|
||||
...style,
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{tabs.map(tab => {
|
||||
const active = tab === activeTab
|
||||
return <div key={tab} style={{
|
||||
position: 'relative',
|
||||
width: fullSize ? '100%' : 150,
|
||||
}}>
|
||||
<Button disabled={active || disabledTabs?.includes(tab)} style={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
// background: active ? 'rgb(77, 77, 77)' : 'rgb(114, 114, 114)',
|
||||
color: 'white',
|
||||
cursor: 'pointer',
|
||||
fontSize: 9,
|
||||
padding: '2px 0px',
|
||||
}} onClick={() => {
|
||||
onTabChange(tab)
|
||||
}}>{labels?.[tab] ?? tab}</Button>
|
||||
return <div
|
||||
key={tab} style={{
|
||||
position: 'relative',
|
||||
width: fullSize ? '100%' : 150,
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
disabled={active || disabledTabs?.includes(tab)} style={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
// background: active ? 'rgb(77, 77, 77)' : 'rgb(114, 114, 114)',
|
||||
color: 'white',
|
||||
cursor: 'pointer',
|
||||
fontSize: 9,
|
||||
padding: '2px 0px',
|
||||
}} onClick={() => {
|
||||
onTabChange(tab)
|
||||
}}
|
||||
>{labels?.[tab] ?? tab}
|
||||
</Button>
|
||||
{active && <div style={{
|
||||
position: 'absolute',
|
||||
bottom: 0,
|
||||
|
|
@ -43,7 +49,8 @@ export default ({ tabs, activeTab, labels, onTabChange, fullSize, style, disable
|
|||
background: 'white',
|
||||
width: '50%',
|
||||
margin: 'auto',
|
||||
}} />}
|
||||
}}
|
||||
/>}
|
||||
</div>
|
||||
})}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -201,7 +201,8 @@ export default ({ touchActive, setupActive, buttonsPositions, closeButtonsSetup
|
|||
left: `${pointer.x / window.innerWidth * 100}%`,
|
||||
top: `${pointer.y / window.innerHeight * 100}%`
|
||||
} : {}
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className='movement_joystick_inner'
|
||||
style={{
|
||||
|
|
@ -232,13 +233,18 @@ export default ({ touchActive, setupActive, buttonsPositions, closeButtonsSetup
|
|||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
gap: 3
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
<Button onClick={() => {
|
||||
closeButtonsSetup()
|
||||
}}>Cancel</Button>
|
||||
}}
|
||||
>Cancel
|
||||
</Button>
|
||||
<Button onClick={() => {
|
||||
closeButtonsSetup(newButtonPositions)
|
||||
}}>Apply</Button>
|
||||
}}
|
||||
>Apply
|
||||
</Button>
|
||||
</div>}
|
||||
</div>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ export default ({ progress, level, gamemode }: { progress: number; level: number
|
|||
className={styles['xp-bar-bg']}
|
||||
style={{ display: gamemode === 'creative' || gamemode === 'spectator' ? 'none' : 'block' }}
|
||||
>
|
||||
<div className={styles['xp-bar']} style={{ width: `${182 * progress}px` }}></div>
|
||||
<div className={styles['xp-bar']} style={{ width: `${182 * progress}px` }} />
|
||||
<span className={styles['xp-label']} style={{ display: level > 0 ? 'block' : 'none' }}>{level}</span>
|
||||
</div>
|
||||
</SharedHudVars>
|
||||
|
|
|
|||
|
|
@ -160,7 +160,7 @@ const App = () => {
|
|||
<HeldMapUi />
|
||||
</InGameComponent>
|
||||
</div>
|
||||
<div></div>
|
||||
<div />
|
||||
</RobustPortal>
|
||||
<EnterFullscreenButton />
|
||||
<InGameUi />
|
||||
|
|
@ -186,18 +186,23 @@ const App = () => {
|
|||
<div className='overlay-top-scaled'>
|
||||
<GamepadUiCursor />
|
||||
</div>
|
||||
<div></div>
|
||||
<div />
|
||||
</RobustPortal>
|
||||
</ButtonAppProvider>
|
||||
</div>
|
||||
}
|
||||
|
||||
const PerComponentErrorBoundary = ({ children }) => {
|
||||
return children.map((child, i) => <ErrorBoundary key={i} renderError={(error) => {
|
||||
const componentNameClean = (child.type.name || child.type.displayName || 'Unknown').replaceAll(/__|_COMPONENT/g, '')
|
||||
showNotification(`UI component ${componentNameClean} crashed!`, 'Please report this. Use console for more.', true, undefined)
|
||||
return null
|
||||
}}>{child}</ErrorBoundary>)
|
||||
return children.map((child, i) => <ErrorBoundary
|
||||
key={i}
|
||||
renderError={(error) => {
|
||||
const componentNameClean = (child.type.name || child.type.displayName || 'Unknown').replaceAll(/__|_COMPONENT/g, '')
|
||||
showNotification(`UI component ${componentNameClean} crashed!`, 'Please report this. Use console for more.', true, undefined)
|
||||
return null
|
||||
}}
|
||||
>
|
||||
{child}
|
||||
</ErrorBoundary>)
|
||||
}
|
||||
|
||||
renderToDom(<App />, {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue