credit goes to this dude Rubik's cube in Javascript
I made an ai agent from scanning your periscope modules, so it uses the correct syntax for the periscope resources then just fed it that code and got it to convert it
here are the files but they were made by a clanker
Main.jsx
import styles, { applyStyle } from './Style.css'
const { useEffect, useRef } = React
const faces = ['left', 'right', 'top', 'bottom', 'back', 'front'] as const
const colors = ['blue', 'green', 'white', 'yellow', 'orange', 'red'] as const
function classNames(...names: string[]) {
return names.filter(Boolean).join(' ')
}
export default function RubiksCube() {
const sceneRef = useRef<HTMLDivElement | null>(null)
const pivotRef = useRef<HTMLDivElement | null>(null)
const cubeRef = useRef<HTMLDivElement | null>(null)
const guideRef = useRef<HTMLDivElement | null>(null)
useEffect(() => {
applyStyle()
const scene = sceneRef.current
const pivot = pivotRef.current
const cube = cubeRef.current
const guide = guideRef.current
if (scene == null || pivot == null || cube == null || guide == null) {
console.warn('RubiksCube failed to initialize because required DOM nodes were not found.')
return
}
const pieces = Array.from(cube.getElementsByClassName(styles.piece)) as HTMLElement[]
function mx(i: number, j: number) {
return ([2, 4, 3, 5][j % 4 | 0] + (i % 2) * (((j | 0) % 4) * 2 + 3) + 2 * (i / 2 | 0)) % 6
}
function getAxis(face: number) {
return String.fromCharCode('X'.charCodeAt(0) + face / 2)
}
function getPieceBy(face: number, index: number, corner: number) {
const pieceId = (1 << face) + (1 << mx(face, index)) + (1 << mx(face, index + 1)) * corner
return cube.querySelector(`[data-piece-id="${pieceId}"]`) as HTMLElement | null
}
function assembleCube() {
for (const piece of pieces) {
piece.removeAttribute('data-piece-id')
Array.from(piece.getElementsByClassName(styles.sticker)).forEach((sticker) => sticker.remove())
}
for (let i = 0; i < pieces.length; i++) {
let id = 0
function moveTo(face: number) {
id += 1 << face
const sticker = document.createElement('div')
sticker.className = classNames(styles.sticker, styles[colors[face]])
pieces[i].children[face].appendChild(sticker)
return `translate${getAxis(face)}(${face % 2 * 4 - 2}em)`
}
const x = mx(i, i % 18)
pieces[i].style.transform =
`rotateX(0deg)${moveTo(i % 6)}` + (i > 5 ? moveTo(x) + (i > 17 ? moveTo(mx(x, x + 2)) : '') : '')
pieces[i].setAttribute('data-piece-id', String(id))
}
}
function swapPieces(face: number, times: number) {
for (let i = 0; i < 6 * times; i++) {
const piece1 = getPieceBy(face, i / 2, i % 2)
const piece2 = getPieceBy(face, i / 2 + 1, i % 2)
if (piece1 == null || piece2 == null) {
console.warn(`RubiksCube could not find pieces for face ${face}.`)
return
}
for (let j = 0; j < 5; j++) {
const sticker1 = piece1.children[j < 4 ? mx(face, j) : face].firstChild as HTMLElement | null
const sticker2 = piece2.children[j < 4 ? mx(face, j + 1) : face].firstChild as HTMLElement | null
const className = sticker1?.className ?? ''
if (className && sticker1 != null && sticker2 != null) {
sticker1.className = sticker2.className
sticker2.className = className
}
}
}
}
function animateRotation(face: number, clockwise: boolean, currentTime: number) {
const k = 0.3 * (face % 2 * 2 - 1) * (2 * Number(clockwise) - 1)
const qubes = Array(9)
.fill(pieces[face])
.map((value, index) => (index ? getPieceBy(face, index / 2, index % 2) : value))
.filter((piece): piece is HTMLElement => piece != null)
function rotatePieces() {
const passed = Date.now() - currentTime
const style = `rotate${getAxis(face)}(${k * passed * Number(passed < 300)}deg)`
qubes.forEach((piece) => {
piece.style.transform = piece.style.transform.replace(/rotate.\(\S+\)/, style)
})
if (passed >= 300) {
swapPieces(face, 3 - 2 * Number(clockwise))
return
}
requestAnimationFrame(rotatePieces)
}
rotatePieces()
}
function mousedown(mouseDownEvent: MouseEvent) {
const startXY = pivot.style.transform.match(/-?\d+\.?\d*/g)?.map(Number) ?? [-35, -135]
const target = mouseDownEvent.target as Element
const element = target.closest(`.${styles.element}`) as HTMLElement | null
const face = element == null ? -1 : Array.prototype.indexOf.call(element.parentNode?.children ?? [], element)
function mousemove(mouseMoveEvent: MouseEvent) {
if (element != null) {
const anchor = document
.elementFromPoint(mouseMoveEvent.clientX, mouseMoveEvent.clientY)
?.closest('[data-anchor]') as HTMLElement | null
const anchorIndex = anchor?.dataset.anchor
if (anchorIndex != null) {
mouseup()
const rotatedFace = mx(face, Number(anchorIndex) + 3)
const clockwise = element.parentElement?.children[rotatedFace]?.hasChildNodes() ?? false
animateRotation(mx(face, Number(anchorIndex) + 1 + 2 * Number(clockwise)), clockwise, Date.now())
}
} else {
pivot.style.transform =
`rotateX(${startXY[0] - (mouseMoveEvent.pageY - mouseDownEvent.pageY) / 2}deg)` +
`rotateY(${startXY[1] + (mouseMoveEvent.pageX - mouseDownEvent.pageX) / 2}deg)`
}
}
function mouseup() {
guide.style.display = 'none'
scene.appendChild(guide)
scene.removeEventListener('mousemove', mousemove)
document.removeEventListener('mouseup', mouseup)
scene.addEventListener('mousedown', mousedown)
}
if (element != null) {
guide.style.display = 'block'
element.appendChild(guide)
}
scene.addEventListener('mousemove', mousemove)
document.addEventListener('mouseup', mouseup)
scene.removeEventListener('mousedown', mousedown)
}
function preventDrag(event: DragEvent) {
event.preventDefault()
}
assembleCube()
scene.addEventListener('mousedown', mousedown)
document.addEventListener('dragstart', preventDrag)
return () => {
scene.removeEventListener('mousedown', mousedown)
document.removeEventListener('dragstart', preventDrag)
}
}, [])
return (
<div className={styles.cubeStage}>
<div ref={sceneRef} className={styles.scene}>
<div
ref={pivotRef}
className={classNames(styles.pivot, styles.centered)}
>
<div ref={cubeRef} className={styles.cube}>
{Array.from({ length: 26 }, (_, piece) => (
<div key={piece} className={styles.piece}>
{faces.map((face) => (
<div key={face} className={classNames(styles.element, styles[face])} />
))}
</div>
))}
</div>
<div ref={guideRef} className={styles.guide}>
{Array.from({ length: 4 }, (_, anchor) => (
<div
key={anchor}
data-anchor={anchor}
className={styles.anchor}
style={{
transform: `translateZ(3px) translateY(-33.33%) rotate(${-90 * anchor}deg) translateY(66.67%)`,
}}
/>
))}
</div>
</div>
</div>
</div>
)
}
Style.css
.cubeStage {
width: 100%;
height: 100%;
min-height: 320px;
overflow: hidden;
color: #2f2f31;
background: radial-gradient(circle, white, rgba(0, 0, 0, 0.5));
background-blend-mode: overlay;
user-select: none;
}
.credits {
top: 90%;
width: 100%;
}
.text {
position: initial;
color: grey;
font-family: Helvetica, Arial, sans-serif;
font-size: 0.8rem;
pointer-events: none;
text-align: center;
}
.centered {
position: absolute;
inset: 0;
margin: auto;
}
.scene {
position: relative;
width: 100%;
height: 100%;
perspective: 1200px;
transform-style: preserve-3d;
}
.scene div {
position: absolute;
transform-style: inherit;
}
.pivot {
width: 0;
height: 0;
transition: 0.18s;
}
.guide {
display: none;
}
.anchor {
width: 2em;
height: 6em;
}
.cube {
margin-top: -1em;
margin-left: -1em;
font-size: 190%;
}
.piece {
width: 1.9em;
height: 1.9em;
}
.element {
width: 100%;
height: 100%;
background: #0a0a0a;
border: 0.05em solid #0a0a0a;
border-radius: 10%;
outline: 1px solid transparent;
}
.left {
transform: rotateX(0deg) rotateY(-90deg) rotateZ(180deg) translateZ(1em);
}
.right {
transform: rotateX(0deg) rotateY(90deg) rotateZ(90deg) translateZ(1em);
}
.back {
transform: rotateX(0deg) rotateY(180deg) rotateZ(-90deg) translateZ(1em);
}
.front {
transform: rotateX(0deg) rotateY(0deg) rotateZ(0deg) translateZ(1em);
}
.bottom {
transform: rotateX(-90deg) rotateY(0deg) rotateZ(-90deg) translateZ(1em);
}
.top {
transform: rotateX(90deg) rotateY(0deg) rotateZ(180deg) translateZ(1em);
}
.sticker {
position: absolute;
inset: 0;
width: 95%;
height: 95%;
margin: auto;
border-radius: 10%;
box-shadow:
inset 0.05em 0.05em 0.2rem 0 rgba(255, 255, 255, 0.25),
inset -0.05em -0.05em 0.2rem 0 rgba(0, 0, 0, 0.25);
outline: 1px solid transparent;
transform: translateZ(2px);
}
.blue {
background-color: #001ca8;
}
.green {
background-color: #006e16;
}
.white {
background-color: #ddd;
}
.yellow {
background-color: #e0ae00;
}
.orange {
background-color: #ff5000;
}
.red {
background-color: #df0500;
}