Skip to content
Snippets Groups Projects
Commit e12f58ba authored by João Magalhães's avatar João Magalhães :rocket:
Browse files

Merge branch 'joamag/web' into 'master'

Initial web version of boytacean

Closes #1

See merge request !2
parents e3f188ab be1b7517
No related branches found
No related tags found
1 merge request!2Initial web version of boytacean
Pipeline #842 passed
Showing
with 1856 additions and 4 deletions
......@@ -5,5 +5,5 @@ Cargo.lock
/.idea
/target
/res/roms
/res/roms.prop
/examples/*/target
image: hivesolutions/ubuntu_dev
variables:
NETLIFY_SITE_ID: boytacean
NETLIFY_AUTH_TOKEN: $NETLIFY_AUTH_TOKEN
CLOUDFLARE_API_TOKEN: $CLOUDFLARE_API_TOKEN
CRATES_TOKEN: $CRATES_TOKEN
NPM_TOKEN: $NPM_TOKEN
stages:
- build
- deploy
before_script:
- apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y -q pkg-config
- curl -sf -L https://static.rust-lang.org/rustup.sh | sh -s -- -y
- export PATH=$PATH:$HOME/.cargo/bin
- curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash
- export NVM_DIR="$HOME/.nvm"
- \[ -s "$NVM_DIR/nvm.sh" \] && \. "$NVM_DIR/nvm.sh"
- \[ -s "$NVM_DIR/bash_completion" \] && \. "$NVM_DIR/bash_completion"
- nvm install stable
build-rust:
stage: build
parallel:
matrix:
- RUST_VERSION: ["1.56.1", "1.60.0", "stable", "nightly"]
script:
- rustup toolchain install $RUST_VERSION
- rustup override set $RUST_VERSION
- rustc --version
- cargo build
- cargo build --release
build-wasm:
stage: build
parallel:
matrix:
- RUST_VERSION: ["1.60.0"]
script:
- rustup toolchain install $RUST_VERSION
- rustup override set $RUST_VERSION
- rustc --version
- cargo install wasm-pack
- wasm-pack build --release --target=web --out-dir=examples/web/lib -- --features wasm
- cd examples/web && npm install && npm run build
artifacts:
paths:
- examples/web/dist
- examples/web/lib
expire_in: 1 day
deploy-netlify-preview:
stage: deploy
script:
- cd examples/web/dist
- npm_config_yes=true npx --package=netlify-cli netlify deploy --dir=.
dependencies:
- build-wasm
only:
- master
deploy-netlify-prod:
stage: deploy
script:
- cd examples/web/dist
- npm_config_yes=true npx --package=netlify-cli netlify deploy --dir=. --prod
dependencies:
- build-wasm
only:
- tags
deploy-cloudfare-preview:
stage: deploy
script:
- cd examples/web/dist
- npm_config_yes=true npx wrangler pages publish . --project-name=boytacean --branch master
dependencies:
- build-wasm
only:
- master
deploy-cloudfare-prod:
stage: deploy
script:
- cd examples/web/dist
- npm_config_yes=true npx wrangler pages publish . --project-name=boytacean --branch stable
dependencies:
- build-wasm
only:
- tags
deploy-crates:
stage: deploy
script:
- cargo login $CRATES_TOKEN
- cargo publish --no-verify
dependencies:
- build-rust
only:
- tags
deploy-npm:
stage: deploy
script:
- echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > ~/.npmrc
- cd examples/web/lib && npm publish
dependencies:
- build-wasm
only:
- tags
......@@ -79,9 +79,9 @@ fn main() {
.unwrap();
let mut game_boy = GameBoy::new();
game_boy.load_boot_default();
game_boy.load_rom("../../res/roms/ld_r_r.gb");
//game_boy.load_rom("../../res/roms/opus5.gb");
game_boy.load_boot_static();
game_boy.load_rom_file("../../res/roms/firstwhite.gb");
//game_boy.load_rom_file("../../res/roms/opus5.gb");
let mut counter = 0;
......
yarn.lock
package-lock.json
/.parcel-cache
/dist
/node_modules
{
"extends": "@parcel/config-default",
"transformers": {
"*.{ts,tsx}": ["@parcel/transformer-typescript-tsc"],
"*.gb": ["@parcel/transformer-raw"]
}
}
{
"semi": true,
"trailingComma": "none",
"singleQuote": false,
"tabWidth": 4,
"endOfLine": "crlf"
}
@import url("https://fonts.googleapis.com/css2?family=VT323&display=swap");
* {
box-sizing: border-box;
-o-box-sizing: border-box;
-ms-box-sizing: border-box;
-moz-box-sizing: border-box;
-khtml-box-sizing: border-box;
-webkit-box-sizing: border-box;
}
a {
border-bottom: 2px dotted #ffffff;
color: #ffffff;
text-decoration: none;
}
a:hover {
border-bottom-style: solid;
}
html {
margin: 0px 0px 0px 0px;
padding: 0px 0px 0px 0px;
}
body {
color: #ffffff;
font-family: "VT323", "Roboto", "Open Sans", Arial, Helvetica, sans-serif;
margin: 0px 0px 0px 0px;
padding: 12px 12px 52px 12px;
}
p {
font-size: 18px;
line-height: 24px;
margin: 12px 0px 12px 0px;
}
.main {
display: flex;
}
@media only screen and (max-width: 1120px) {
.main {
flex-direction: column;
}
}
.main > .side-left {
display: flex;
flex: 1 0;
justify-content: center;
text-align: center;
}
.main > .side-left .canvas-container {
max-width: 100%;
}
.main > .side-left .canvas-container.fullscreen {
align-items: center;
background-color: #2d2d2d;
display: flex;
height: 100%;
justify-content: center;
left: 0px;
position: fixed;
top: 0px;
width: 100%;
z-index: 6;
}
.main > .side-left .canvas-container > .canvas-close {
bottom: 22px;
display: none;
position: absolute;
right: 22px;
}
.main > .side-left .canvas-container > .canvas-close > img {
height: 32px;
width: 32px;
}
.main > .side-left .canvas-container.fullscreen > .canvas-close {
display: block;
}
.main > .side-left .canvas-container > .canvas-frame {
background-color: #1b1a17;
border: 2px solid #50cb93;
font-size: 0px;
margin-top: 78px;
max-width: 320px;
padding: 8px 8px 8px 8px;
}
@media only screen and (max-width: 1120px) {
.main > .side-left .canvas-container > .canvas-frame {
margin-top: 12px;
}
}
.main > .side-left .canvas-container.fullscreen > .canvas-frame {
background-color: transparent;
border: none;
box-shadow: 0px 0px 12px rgba(0, 0, 0, 0.24);
-o-box-shadow: 0px 0px 12px rgba(0, 0, 0, 0.24);
-ms-box-shadow: 0px 0px 12px rgba(0, 0, 0, 0.24);
-moz-box-shadow: 0px 0px 12px rgba(0, 0, 0, 0.24);
-khtml-box-shadow: 0px 0px 12px rgba(0, 0, 0, 0.24);
-webkit-box-shadow: 0px 0px 12px rgba(0, 0, 0, 0.24);
margin: 0px 0px 0px 0px;
max-width: unset;
padding: 0px 0px 0px 0px;
}
.main > .side-left .canvas-container > .canvas-frame > .canvas {
width: 100%;
}
.main > .side-right {
flex: 0 1;
max-width: 100%;
min-width: 580px;
padding: 0px 24px 0px 24px;
}
@media only screen and (max-width: 1120px) {
.main > .side-right {
min-width: unset;
padding: 0px 0px 0px 0px;
}
}
.main > .side-right .logo-image {
vertical-align: middle;
width: 32px;
}
.main > .side-right .separator {
background: #ffffff;
height: 2px;
margin: 22px 0px 22px 0px;
}
.main > .side-right .diag {
font-size: 24px;
vertical-align: top;
}
.main > .side-right .diag > dt {
clear: both;
float: left;
margin-top: 12px;
}
.main > .side-right .diag > dt:first-of-type {
margin-top: 0px;
}
.main > .side-right .diag > dd {
float: right;
margin-top: 12px;
}
.main > .side-right .diag > dd:first-of-type {
margin-top: 0px;
}
.main > .side-right .diag::after {
clear: both;
content: '';
display: block;
}
.footer {
bottom: 0px;
height: 40px;
left: 0px;
line-height: 40px;
padding: 0px 0px 0px 0px;
position: fixed;
text-align: center;
width: 100%;
}
.footer-background {
bottom: 0px;
filter: blur(1.0rem);
-o-filter: blur(1.0rem);
-ms-filter: blur(1.0rem);
-moz-filter: blur(1.0rem);
-khtml-filter: blur(1.0rem);
-webkit-filter: blur(1.0rem);
height: 40px;
left: 0px;
position: fixed;
width: 100%;
}
.toast-container {
background-color: black;
height: 0px;
left: 0px;
padding: 0px 24px 0px 24px;
pointer-events: none;
position: fixed;
text-align: center;
top: 0px;
width: 100%;
z-index: 8;
}
.toast-container > .toast {
background-color: #2a9d8f;
border-radius: 4px 4px 4px 4px;
-o-border-radius: 4px 4px 4px 4px;
-ms-border-radius: 4px 4px 4px 4px;
-moz-border-radius: 4px 4px 4px 4px;
-khtml-border-radius: 4px 4px 4px 4px;
-webkit-border-radius: 4px 4px 4px 4px;
cursor: pointer;
display: inline-block;
font-size: 20px;
line-height: 22px;
opacity: 0.0;
-o-opacity: 0.0;
-ms-opacity: 0.0;
-moz-opacity: 0.0;
-khtml-opacity: 0.0;
-webkit-opacity: 0.0;
padding: 12px 18px 12px 18px;
position: relative;
top: -46px;
transition: top 0.5s cubic-bezier(0.075, 0.82, 0.165, 1), opacity 0.35s cubic-bezier(0.075, 0.82, 0.165, 1);
-o-transition: top 0.5s cubic-bezier(0.075, 0.82, 0.165, 1), opacity 0.35s cubic-bezier(0.075, 0.82, 0.165, 1);
-ms-transition: top 0.5s cubic-bezier(0.075, 0.82, 0.165, 1), opacity 0.35s cubic-bezier(0.075, 0.82, 0.165, 1);
-moz-transition: top 0.5s cubic-bezier(0.075, 0.82, 0.165, 1), opacity 0.35s cubic-bezier(0.075, 0.82, 0.165, 1);
-khtml-transition: top 0.5s cubic-bezier(0.075, 0.82, 0.165, 1), opacity 0.35s cubic-bezier(0.075, 0.82, 0.165, 1);
-webkit-transition: top 0.5s cubic-bezier(0.075, 0.82, 0.165, 1), opacity 0.35s cubic-bezier(0.075, 0.82, 0.165, 1);
width: fit-content;
}
.toast-container > .toast.error {
background-color: #e63946;
}
.toast-container > .toast.visible {
opacity: 1.0;
-o-opacity: 1.0;
-ms-opacity: 1.0;
-moz-opacity: 1.0;
-khtml-opacity: 1.0;
-webkit-opacity: 1.0;
pointer-events: all;
top: 24px;
}
.button-area {
user-select: none;
-o-user-select: none;
-ms-user-select: none;
-moz-user-select: none;
-khtml-user-select: none;
-webkit-user-select: none;
}
.button-area > * {
margin-bottom: 12px;
}
.magnify-button {
cursor: pointer;
display: inline-block;
transition: transform 0.35s cubic-bezier(0.075, 0.82, 0.165, 1);
-o-transition: transform 0.35s cubic-bezier(0.075, 0.82, 0.165, 1);
-ms-transition: transform 0.35s cubic-bezier(0.075, 0.82, 0.165, 1);
-moz-transition: transform 0.35s cubic-bezier(0.075, 0.82, 0.165, 1);
-khtml-transition: transform 0.35s cubic-bezier(0.075, 0.82, 0.165, 1);
-webkit-transition: transform 0.35s cubic-bezier(0.075, 0.82, 0.165, 1);
}
.magnify-button:hover {
transform: scale(1.3, 1.3);
-o-transform: scale(1.3, 1.3);
-ms-transform: scale(1.3, 1.3);
-moz-transform: scale(1.3, 1.3);
-khtml-transform: scale(1.3, 1.3);
-webkit-transform: scale(1.3, 1.3);
}
.magnify-button:active {
transform: scale(1.0, 1.0);
-o-transform: scale(1.0, 1.0);
-ms-transform: scale(1.0, 1.0);
-moz-transform: scale(1.0, 1.0);
-khtml-transform: scale(1.0, 1.0);
-webkit-transform: scale(1.0, 1.0);
}
.tiny-button {
border-radius: 96px 96px 96px 96px;
-o-border-radius: 96px 96px 96px 96px;
-ms-border-radius: 96px 96px 96px 96px;
-moz-border-radius: 96px 96px 96px 96px;
-khtml-border-radius: 96px 96px 96px 96px;
-webkit-border-radius: 96px 96px 96px 96px;
cursor: pointer;
display: inline-block;
padding: 0px 8px 0px 8px;
user-select: none;
-o-user-select: none;
-ms-user-select: none;
-moz-user-select: none;
-khtml-user-select: none;
-webkit-user-select: none;
}
.tiny-button.border {
border: 1px solid #ffffff;
}
.tiny-button.padded {
padding: 4px 10px 4px 10px;
}
.tiny-button.padded-large {
padding: 4px 14px 4px 14px;
}
.tiny-button.rounded {
padding: 6px 6px 6px 6px;
}
.tiny-button.enabled {
background-color: #50cb93;
}
.tiny-button.file {
position: relative;
}
.tiny-button:hover {
background-color: #50cb93;
}
.tiny-button.red:hover {
background-color: #e63946;
}
.tiny-button:active {
background-color: #2a9d8f;
}
.tiny-button.red:active {
background-color: #bf2a37;
}
.tiny-button > img {
margin-right: 6px;
margin-top: 2px;
vertical-align: top;
width: 13px;
}
.tiny-button > img.medium {
width: 20px;
}
.tiny-button > img.large {
width: 28px;
}
.tiny-button > img.very-large {
width: 38px;
}
.tiny-button.no-text > img {
margin-right: 0px;
margin-top: 0px;
}
.tiny-button.file > input[type="file"] {
cursor: pointer;
height: 100%;
left: 0px;
opacity: 0;
-o-opacity: 0;
-ms-opacity: 0;
-moz-opacity: 0;
-khtml-opacity: 0;
-webkit-opacity: 0;
position: absolute;
top: 0px;
vertical-align: top;
width: 100%;
}
.tiny-button.file > input[type="file"]::-webkit-file-upload-button {
cursor: pointer;
}
.overlay {
align-items: center;
background-color: rgba(80, 203, 147, 0.95);
display: flex;
font-size: 48px;
height: 100%;
justify-content: center;
left: 0px;
opacity: 0.0;
-o-opacity: 0.0;
-ms-opacity: 0.0;
-moz-opacity: 0.0;
-khtml-opacity: 0.0;
-webkit-opacity: 0.0;
pointer-events: none;
position: fixed;
text-align: center;
top: 0px;
transition: opacity 0.35s cubic-bezier(0.075, 0.82, 0.165, 1);
-o-transition: opacity 0.35s cubic-bezier(0.075, 0.82, 0.165, 1);
-ms-transition: opacity 0.35s cubic-bezier(0.075, 0.82, 0.165, 1);
-moz-transition: opacity 0.35s cubic-bezier(0.075, 0.82, 0.165, 1);
-khtml-transition: opacity 0.35s cubic-bezier(0.075, 0.82, 0.165, 1);
-webkit-transition: opacity 0.35s cubic-bezier(0.075, 0.82, 0.165, 1);
width: 100%;
z-index: 10;
}
.overlay.visible {
opacity: 1.0;
-o-opacity: 1.0;
-ms-opacity: 1.0;
-moz-opacity: 1.0;
-khtml-opacity: 1.0;
-webkit-opacity: 1.0;
}
.overlay .overlay-image {
margin-top: 16px;
}
.overlay .overlay-image > img {
width: 64px;
}
.modal-container {
align-items: center;
background-color: rgba(20, 20, 20, 0.95);
display: flex;
height: 100%;
justify-content: center;
left: 0px;
opacity: 0;
-o-opacity: 0;
-ms-opacity: 0;
-moz-opacity: 0;
-khtml-opacity: 0;
-webkit-opacity: 0;
padding: 0px 12px 0px 12px;
pointer-events: none;
position: fixed;
text-align: center;
top: 0px;
transition: opacity 0.35s cubic-bezier(0.075, 0.82, 0.165, 1);
-o-transition: opacity 0.35s cubic-bezier(0.075, 0.82, 0.165, 1);
-ms-transition: opacity 0.35s cubic-bezier(0.075, 0.82, 0.165, 1);
-moz-transition: opacity 0.35s cubic-bezier(0.075, 0.82, 0.165, 1);
-khtml-transition: opacity 0.35s cubic-bezier(0.075, 0.82, 0.165, 1);
-webkit-transition: opacity 0.35s cubic-bezier(0.075, 0.82, 0.165, 1);
width: 100%;
z-index: 10;
}
.modal-container.visible {
opacity: 1.0;
-o-opacity: 1.0;
-ms-opacity: 1.0;
-moz-opacity: 1.0;
-khtml-opacity: 1.0;
-webkit-opacity: 1.0;
transition: opacity 0.5s cubic-bezier(0.075, 0.82, 0.165, 1);
-o-transition: opacity 0.5s cubic-bezier(0.075, 0.82, 0.165, 1);
-ms-transition: opacity 0.5s cubic-bezier(0.075, 0.82, 0.165, 1);
-moz-transition: opacity 0.5s cubic-bezier(0.075, 0.82, 0.165, 1);
-khtml-transition: opacity 0.5s cubic-bezier(0.075, 0.82, 0.165, 1);
-webkit-transition: opacity 0.5s cubic-bezier(0.075, 0.82, 0.165, 1);
}
.modal-container > .modal {
background-color: #264653;
border-radius: 6px 6px 6px 6px;
-o-border-radius: 6px 6px 6px 6px;
-ms-border-radius: 6px 6px 6px 6px;
-moz-border-radius: 6px 6px 6px 6px;
-khtml-border-radius: 6px 6px 6px 6px;
-webkit-border-radius: 6px 6px 6px 6px;
box-shadow: 0px 3px 8px rgba(0, 0, 0, 0.24);
-o-box-shadow: 0px 3px 8px rgba(0, 0, 0, 0.24);
-ms-box-shadow: 0px 3px 8px rgba(0, 0, 0, 0.24);
-moz-box-shadow: 0px 3px 8px rgba(0, 0, 0, 0.24);
-khtml-box-shadow: 0px 3px 8px rgba(0, 0, 0, 0.24);
-webkit-box-shadow: 0px 3px 8px rgba(0, 0, 0, 0.24);
max-width: 100%;
padding: 24px 24px 24px 24px;
text-align: left;
transform: scale(0.96);
-o-transform: scale(0.96);
-ms-transform: scale(0.96);
-moz-transform: scale(0.96);
-khtml-transform: scale(0.96);
-webkit-transform: scale(0.96);
transition: transform 0.35s cubic-bezier(0.075, 0.82, 0.165, 1);
-o-transition: transform 0.35s cubic-bezier(0.075, 0.82, 0.165, 1);
-ms-transition: transform 0.35s cubic-bezier(0.075, 0.82, 0.165, 1);
-moz-transition: transform 0.35s cubic-bezier(0.075, 0.82, 0.165, 1);
-khtml-transition: transform 0.35s cubic-bezier(0.075, 0.82, 0.165, 1);
-webkit-transition: transform 0.35s cubic-bezier(0.075, 0.82, 0.165, 1);
width: 480px;
}
.modal-container.visible > .modal {
pointer-events: all;
transform: scale(1);
-o-transform: scale(1);
-ms-transform: scale(1);
-moz-transform: scale(1);
-khtml-transform: scale(1);
-webkit-transform: scale(1);
transition: transform 0.5s cubic-bezier(0.075, 0.82, 0.165, 1);
-o-transition: transform 0.5s cubic-bezier(0.075, 0.82, 0.165, 1);
-ms-transition: transform 0.5s cubic-bezier(0.075, 0.82, 0.165, 1);
-moz-transition: transform 0.5s cubic-bezier(0.075, 0.82, 0.165, 1);
-khtml-transition: transform 0.5s cubic-bezier(0.075, 0.82, 0.165, 1);
-webkit-transition: transform 0.5s cubic-bezier(0.075, 0.82, 0.165, 1);
}
.modal-container > .modal .modal-top-buttons {
float: right;
margin-right: -10px;
margin-top: -10px;
}
.modal-container > .modal .modal-title {
font-size: 32px;
margin-top: 0px;
text-align: left;
}
.modal-container > .modal .modal-text {
font-size: 20px;
line-height: 22px;
}
.modal-container > .modal .modal-buttons {
font-size: 22px;
margin-top: 24px;
text-align: center;
user-select: none;
-o-user-select: none;
-ms-user-select: none;
-moz-user-select: none;
-khtml-user-select: none;
-webkit-user-select: none;
}
.modal-container > .modal .modal-buttons > .tiny-button {
margin-right: 12px;
min-width: 120px;
}
.modal-container > .modal .modal-buttons > .tiny-button:last-child {
margin-right: 0px;
}
.keyboard {
font-size: 0px;
text-align: center;
touch-callout: none;
-o-touch-callout: none;
-ms-touch-callout: none;
-moz-touch-callout: none;
-khtml-touch-callout: none;
-webkit-touch-callout: none;
user-select: none;
-o-user-select: none;
-ms-user-select: none;
-moz-user-select: none;
-khtml-user-select: none;
-webkit-user-select: none;
}
.keyboard > .keyboard-line {
margin-bottom: 12px;
}
.keyboard > .keyboard-line:last-child {
margin-bottom: 0px;
}
.keyboard .key {
border: 2px solid #ffffff;
border-radius: 5px 5px 5px 5px;
-o-border-radius: 5px 5px 5px 5px;
-ms-border-radius: 5px 5px 5px 5px;
-moz-border-radius: 5px 5px 5px 5px;
-khtml-border-radius: 5px 5px 5px 5px;
-webkit-border-radius: 5px 5px 5px 5px;
cursor: pointer;
display: inline-block;
font-size: 38px;
height: 48px;
line-height: 46px;
margin-right: 14px;
text-align: center;
width: 48px;
}
.keyboard .key:last-child {
margin-right: 0px;
}
.keyboard .key:hover {
background-color: #50cb93;
}
.keyboard .key:active {
background-color: #2a9d8f;
}
<!DOCTYPE html>
<html lang="en">
<head>
<title>Boytacean</title>
<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="description" content="Game Boy emulator written in Rust 🦀." />
<meta name="viewport"
content="width=device-width, user-scalable=yes, initial-scale=1, minimum-scale=1, maximum-scale=5" />
<link rel="icon" href="res/icon.png" />
<link rel="stylesheet" href="index.css" />
</head>
<body>
<div class="main">
<div class="side-left">
<div id="canvas-container" class="canvas-container">
<span id="canvas-close" class="magnify-button canvas-close">
<img class="large" src="res/minimise.svg" alt="minimise" />
</span>
<div class="canvas-frame">
<canvas id="chip-canvas" class="canvas" width="320" height="288"></canvas>
</div>
</div>
</div>
<div class="side-right">
<h1>Boytacean <a id="version" href="https://gitlab.stage.hive.pt/joamag/boytacean/-/blob/master/CHANGELOG.md" target="_blank"></a> <img class="logo-image" src="res/thunder.png" alt="thunder" />
</h1>
<div class="separator"></div>
<div id="section-narrative" class="section">
<p>This is a <a href="https://en.wikipedia.org/wiki/Game_Boy" target="_blank">Game Boy</a> emulator built using
the <a href="https://www.rust-lang.org" target="_blank">Rust Programming Language</a> and is running
inside this browser with the help of <a href="https://webassembly.org/" target="_blank">WebAssembly</a>.
</p>
<p>You can check the source code of it at <a href="https://gitlab.stage.hive.pt/joamag/boytacean"
target="_blank">GitLab</a>.</p>
<p>TIP: Drag and Drop ROM files to the Browser to load the ROM.</p>
</div>
<div id="separator-narrative" class="separator"></div>
<div id="section-keyboard" class="section" style="display: none;">
<div id="keyboard" class="keyboard">
<div class="keyboard-line">
<span class="key">1</span>
<span class="key">2</span>
<span class="key">3</span>
<span class="key">4</span>
</div>
<div class="keyboard-line">
<span class="key">Q</span>
<span class="key">W</span>
<span class="key">E</span>
<span class="key">R</span>
</div>
<div class="keyboard-line">
<span class="key">A</span>
<span class="key">S</span>
<span class="key">D</span>
<span class="key">F</span>
</div>
<div class="keyboard-line">
<span class="key">Z</span>
<span class="key">X</span>
<span class="key">C</span>
<span class="key">V</span>
</div>
</div>
</div>
<div id="separator-keyboard" class="separator" style="display: none;"></div>
<div id="section-diag" class="section">
<dl class="diag">
<dt>Engine</dt>
<dd id="engine" class="tiny-button">-</dd>
<dt>ROM</dt>
<dd id="rom-name">-</dd>
<dt>ROM Size</dt>
<dd><span id="rom-size">-</span> bytes</dd>
<dt>CPU Frequency</dt>
<dd>
<span id="logic-frequency-minus" class="tiny-button">-</span>
<span id="logic-frequency">-</span> Hz
<span id="logic-frequency-plus" class="tiny-button">+</span></dd>
<dt>Framerate</dt>
<dd><span id="fps-count">-</span> fps</dd>
</dl>
</div>
<div id="separator-diag" class="separator"></div>
<div class="section">
<div class="button-area">
<span id="button-pause" class="tiny-button border padded">
<img src="res/pause.svg" alt="pause" /><span>Pause</span>
</span>
<span id="button-reset" class="tiny-button border padded">
<img src="res/reset.svg" alt="reset" /><span>Reset</span>
</span>
<span id="button-benchmark" class="tiny-button border padded">
<img src="res/bolt.svg" alt="bolt" /><span>Benchmark</span>
</span>
<span id="button-fullscreen" class="tiny-button border padded">
<img src="res/maximise.svg" alt="maximise" /><span>Fullscreen</span>
</span>
<span id="button-keyboard" class="tiny-button border padded">
<img src="res/dialpad.svg" alt="info" /><span>Keyboard</span>
</span>
<span id="button-information" class="tiny-button border padded enabled">
<img src="res/info.svg" alt="info" /><span>Information</span>
</span>
<span id="button-debug" class="tiny-button border padded">
<img src="res/bug.svg" alt="bug" /><span>Debug</span>
</span>
<span id="button-theme" class="tiny-button border padded">
<img src="res/marker.svg" alt="marker" /><span>Theme</span>
</span>
<span id="button-upload" class="tiny-button border padded file">
<img src="res/upload.svg" alt="upload" /><span>Upload ROM</span>
<input type="file" id="button-upload-file" name="button-upload-file" accept=".ch8">
</span>
</div>
</div>
</div>
</div>
<div class="toast-container">
<div id="toast" class="toast"></div>
</div>
</body>
<div id="modal-container" class="modal-container">
<div id="modal" class="modal">
<div class="modal-top-buttons">
<span id="modal-close" class="tiny-button rounded no-text">
<img class="medium" src="res/close.svg" alt="close" />
</span>
</div>
<h2 id="modal-title" class="modal-title"></h2>
<p id="modal-text" class="modal-text"></p>
<div class="modal-buttons">
<span id="modal-cancel" class="tiny-button red border padded-large">Cancel</span>
<span id="modal-confirm" class="tiny-button border padded-large">Confirm</span>
</div>
</div>
</div>
<div id="overlay" class="overlay">
<div class="overlay-container">
<div class="overlay-text">
Drag to load ROM <span id="rom-name"></span>
</div>
<div class="overlay-image">
<img src="res/sunglasses.png" alt="sunglasses" />
</div>
</div>
</div>
<div id="footer-background" class="footer-background"></div>
<div id="footer" class="footer">
Built with ❤️ by <a href="https://joao.me" target="_blank">João Magalhães</a>
</div>
<script type="module" src="index.ts"></script>
</body>
</html>
This diff is collapsed.
{
"name": "boytacean-web",
"version": "0.1.0",
"description": "The web version of Boytacean",
"repository": {
"type": "git",
"url": "git+https://gitlab.stage.hive.pt/joamag/boytacean.git"
},
"license": "Apache-2.0",
"scripts": {
"build": "parcel build index.html",
"dev": "parcel index.html",
"pretty": "prettier --config .prettierrc \"./**/*.{ts,json}\" --write",
"start": "npm run build",
"watch": "parcel watch index.html"
},
"source": "index.ts",
"devDependencies": {
"@parcel/transformer-typescript-tsc": "^2.6.1",
"parcel": "^2.6.1",
"prettier": "^2.7.1",
"typescript": "^4.5.5"
}
}
<svg width="48px" height="48px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="bikeIconTitle" stroke="#ffffff" stroke-width="2" stroke-linecap="square" stroke-linejoin="miter" color="#ffffff"> <title id="bikeIconTitle">Bike</title> <circle cx="14" cy="6" r="1"/> <path d="M12 18V14L9 12L12 9L14 11L16 12"/> <circle cx="6" cy="17" r="3"/> <circle cx="18" cy="17" r="3"/> </svg>
\ No newline at end of file
<svg role="img" xmlns="http://www.w3.org/2000/svg" width="48px" height="48px" viewBox="0 0 24 24" aria-labelledby="boltIconTitle" stroke="#ffffff" stroke-width="2" stroke-linecap="square" stroke-linejoin="miter" fill="none" color="#ffffff"> <title id="boltIconTitle">Bolt</title> <path d="M5 14l8-11v7h5l-8 11v-7z"/> </svg>
\ No newline at end of file
<svg role="img" xmlns="http://www.w3.org/2000/svg" width="48px" height="48px" viewBox="0 0 24 24" aria-labelledby="bugIconTitle" stroke="#ffffff" stroke-width="2" stroke-linecap="square" stroke-linejoin="miter" fill="none" color="#ffffff"> <title id="bugIconTitle">Bug</title> <path d="M15 6.99989086C16.1045695 6.99989086 17 7.89532136 17 8.99989086L17 16.458686C17 17.1113133 16.6815784 17.722892 16.1469254 18.0971494L12 21 7.85307456 18.0971494C7.31842164 17.722892 7 17.1113133 7 16.458686L7 8.99989086C7 7.89532136 7.8954305 6.99989086 9 6.99989086 9.00005899 5.34308677 10.3431821 4 12 4 13.6568179 4 14.999941 5.34308677 15 6.99989086zM4 13L7 13"/> <polyline points="3 7 5 9 7 9"/> <polyline points="21 7 19 9 17 9"/> <polyline points="3 19 5 17 7 17"/> <polyline points="17 17 19 17 21 19 21 19"/> <path d="M17,13 L20,13"/> </svg>
\ No newline at end of file
<svg role="img" xmlns="http://www.w3.org/2000/svg" width="48px" height="48px" viewBox="0 0 24 24" aria-labelledby="closeIconTitle" stroke="#ffffff" stroke-width="2" stroke-linecap="square" stroke-linejoin="miter" fill="none" color="#ffffff"> <title id="closeIconTitle">Close</title> <path d="M6.34314575 6.34314575L17.6568542 17.6568542M6.34314575 17.6568542L17.6568542 6.34314575"/> </svg>
\ No newline at end of file
<svg width="48px" height="48px" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" aria-labelledby="dialpadIconTitle" stroke="#ffffff" stroke-width="2" stroke-linecap="square" stroke-linejoin="miter" fill="none" color="#ffffff"> <title id="dialpadIconTitle">Dialpad</title> <circle cx="7" cy="5" r="1"/> <circle cx="12" cy="5" r="1"/> <circle cx="17" cy="5" r="1"/> <circle cx="7" cy="10" r="1"/> <circle cx="12" cy="10" r="1"/> <circle cx="17" cy="10" r="1"/> <circle cx="7" cy="15" r="1"/> <circle cx="12" cy="15" r="1"/> <circle cx="12" cy="20" r="1"/> <circle cx="17" cy="15" r="1"/> </svg>
\ No newline at end of file
examples/web/res/icon.png

13.3 KiB

<svg role="img" xmlns="http://www.w3.org/2000/svg" width="48px" height="48px" viewBox="0 0 24 24" aria-labelledby="infoIconTitle" stroke="#ffffff" stroke-width="2" stroke-linecap="square" stroke-linejoin="miter" fill="none" color="#ffffff"> <title id="infoIconTitle">Information</title> <path d="M12,12 L12,15"/> <line x1="12" y1="9" x2="12" y2="9"/> <circle cx="12" cy="12" r="10"/> </svg>
\ No newline at end of file
<svg width="48px" height="48px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" aria-labelledby="markerIconTitle" stroke="#ffffff" stroke-width="2" stroke-linecap="square" stroke-linejoin="miter" color="#ffffff"> <title id="markerIconTitle">Marker</title> <path fill-rule="evenodd" clip-rule="evenodd" d="M12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22Z"/> <path d="M6 20L8 13H16L18 20"/> <path d="M9.5 13L11.0299 6.88057C11.2823 5.87062 12.7177 5.87062 12.9701 6.88057L14.5 13"/> </svg>
\ No newline at end of file
<svg role="img" xmlns="http://www.w3.org/2000/svg" width="48px" height="48px" viewBox="0 0 24 24" aria-labelledby="maximiseIconTitle" stroke="#ffffff" stroke-width="2" stroke-linecap="square" stroke-linejoin="miter" fill="none" color="#ffffff"> <title id="maximiseIconTitle">Maximise View</title> <polyline points="21 16 21 21 16 21"/> <polyline points="8 21 3 21 3 16"/> <polyline points="16 3 21 3 21 8"/> <polyline points="3 8 3 3 8 3"/> </svg>
\ No newline at end of file
<svg role="img" xmlns="http://www.w3.org/2000/svg" width="48px" height="48px" viewBox="0 0 24 24" aria-labelledby="minimiseIconTitle" stroke="#ffffff" stroke-width="3" stroke-linecap="square" stroke-linejoin="miter" fill="none" color="#ffffff"> <title id="minimiseIconTitle">Minimise View</title> <polyline points="8 3 8 8 3 8"/> <polyline points="21 8 16 8 16 3"/> <polyline points="3 16 8 16 8 21"/> <polyline points="16 21 16 16 21 16"/> </svg>
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment