css, js, the works
This commit is contained in:
parent
0235b8dbf0
commit
25b0ead8b1
|
|
@ -6,8 +6,9 @@ export const Layout: FC = async ({ children, title }) => (
|
||||||
<title>{title || "Nose"}</title>
|
<title>{title || "Nose"}</title>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<link rel="stylesheet" href="/css/main.css" />
|
<link href="/css/reset.css" rel="stylesheet" />
|
||||||
<script src="/js/main.js" async></script>
|
<link href="/css/main.css" rel="stylesheet" />
|
||||||
|
<script src="/js/main.js" type="module" async></script>
|
||||||
</head>
|
</head>
|
||||||
<body data-mode="tall">
|
<body data-mode="tall">
|
||||||
<main>
|
<main>
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,30 @@ import type { FC } from "hono/jsx"
|
||||||
|
|
||||||
export const Terminal: FC = async () => (
|
export const Terminal: FC = async () => (
|
||||||
<>
|
<>
|
||||||
<h1>Hello NOSE!</h1>
|
<link rel="stylesheet" href="/css/terminal.css" />
|
||||||
<p>This is 960×540 space.</p>
|
|
||||||
|
<div id="command-line">
|
||||||
|
<span id="command-prompt">></span>
|
||||||
|
<textarea
|
||||||
|
type="text"
|
||||||
|
id="command-textbox"
|
||||||
|
autocomplete="off"
|
||||||
|
autocorrect="off"
|
||||||
|
autocapitalize="off"
|
||||||
|
spellcheck={false}
|
||||||
|
rows={1}
|
||||||
|
/>
|
||||||
|
<textarea
|
||||||
|
id="command-hint"
|
||||||
|
readonly={true}
|
||||||
|
tabIndex={-1}
|
||||||
|
rows={1}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ul id="command-history">
|
||||||
|
<li class="center">**** NOSE PLUTO V{new Date().getMonth() + 1}.{new Date().getDate()} ****</li>
|
||||||
|
<li class="center">VRAM <span id="vram-size">000KB</span></li>
|
||||||
|
</ul>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
|
@ -87,6 +87,7 @@ main {
|
||||||
width: 960px;
|
width: 960px;
|
||||||
height: 540px;
|
height: 540px;
|
||||||
background: var(--c64-dark-blue);
|
background: var(--c64-dark-blue);
|
||||||
|
color: var(--c64-light-blue);
|
||||||
/* nearest-neighbor scaling */
|
/* nearest-neighbor scaling */
|
||||||
image-rendering: pixelated;
|
image-rendering: pixelated;
|
||||||
transform-origin: center center;
|
transform-origin: center center;
|
||||||
|
|
@ -95,4 +96,8 @@ main {
|
||||||
body[data-mode=tall] #content {
|
body[data-mode=tall] #content {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
color: var(--c64-light-blue);
|
||||||
}
|
}
|
||||||
78
src/css/reset.css
Normal file
78
src/css/reset.css
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
/* https://www.joshwcomeau.com/css/custom-css-reset/ */
|
||||||
|
|
||||||
|
/* 1. Use a more-intuitive box-sizing model */
|
||||||
|
*,
|
||||||
|
*::before,
|
||||||
|
*::after {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 2. Remove default margin */
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 3. Enable keyword animations */
|
||||||
|
@media (prefers-reduced-motion: no-preference) {
|
||||||
|
html {
|
||||||
|
interpolate-size: allow-keywords;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
/* 4. Add accessible line-height */
|
||||||
|
line-height: 1.5;
|
||||||
|
/* 5. Improve text rendering */
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 6. Improve media defaults */
|
||||||
|
img,
|
||||||
|
picture,
|
||||||
|
video,
|
||||||
|
canvas,
|
||||||
|
svg {
|
||||||
|
display: block;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 7. Inherit fonts for form controls */
|
||||||
|
input,
|
||||||
|
button,
|
||||||
|
textarea,
|
||||||
|
select {
|
||||||
|
font: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 8. Avoid text overflows */
|
||||||
|
p,
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4,
|
||||||
|
h5,
|
||||||
|
h6 {
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 9. Improve line wrapping */
|
||||||
|
p {
|
||||||
|
text-wrap: pretty;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4,
|
||||||
|
h5,
|
||||||
|
h6 {
|
||||||
|
text-wrap: balance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
10. Create a root stacking context
|
||||||
|
*/
|
||||||
|
#root,
|
||||||
|
#__next {
|
||||||
|
isolation: isolate;
|
||||||
|
}
|
||||||
85
src/css/terminal.css
Normal file
85
src/css/terminal.css
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
:root {
|
||||||
|
--cli-spacing-vertical: 5px;
|
||||||
|
--cli-spacing-horizontal: 5px;
|
||||||
|
--cli-margin: 10px 0 0 25px;
|
||||||
|
--cli-font-size: 20px;
|
||||||
|
--cli-height: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#command-line {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
width: 100%;
|
||||||
|
font-size: var(--cli-font-size);
|
||||||
|
}
|
||||||
|
|
||||||
|
#command-prompt {
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#command-textbox {
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
|
||||||
|
background: transparent;
|
||||||
|
padding: var(--cli-spacing-vertical) var(--cli-element-spacing-horizontal);
|
||||||
|
min-height: 1.2em;
|
||||||
|
resize: none;
|
||||||
|
overflow: hidden;
|
||||||
|
font: inherit;
|
||||||
|
letter-spacing: inherit;
|
||||||
|
line-height: inherit;
|
||||||
|
|
||||||
|
height: var(--cli-height);
|
||||||
|
width: 100%;
|
||||||
|
box-shadow: none;
|
||||||
|
box-sizing: border-box;
|
||||||
|
outline: 0;
|
||||||
|
margin: var(--cli-margin);
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#command-hint {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: 1;
|
||||||
|
|
||||||
|
pointer-events: none;
|
||||||
|
padding: var(--cli-spacing-vertical) var(--cli-spacing-horizontal);
|
||||||
|
color: #666;
|
||||||
|
background: transparent;
|
||||||
|
resize: none;
|
||||||
|
overflow: hidden;
|
||||||
|
min-height: 1.2em;
|
||||||
|
height: auto;
|
||||||
|
font: inherit;
|
||||||
|
letter-spacing: inherit;
|
||||||
|
line-height: inherit;
|
||||||
|
|
||||||
|
height: var(--cli-height);
|
||||||
|
width: 100%;
|
||||||
|
box-shadow: none;
|
||||||
|
box-sizing: border-box;
|
||||||
|
outline: 0;
|
||||||
|
margin: var(--cli-margin);
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#command-history {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
font-size: var(--cli-font-size);
|
||||||
|
}
|
||||||
|
|
||||||
|
#command-history li {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.center {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
13
src/js/dom.ts
Normal file
13
src/js/dom.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
// finds an element by ID
|
||||||
|
export const $ = (id: string): HTMLElement | null =>
|
||||||
|
document.getElementById(id)
|
||||||
|
|
||||||
|
// creates an HTML element
|
||||||
|
export const $$ = (tag: string, innerHTML = ""): HTMLElement => {
|
||||||
|
const el = document.createElement(tag)
|
||||||
|
if (innerHTML) el.innerHTML = innerHTML
|
||||||
|
return el
|
||||||
|
}
|
||||||
|
|
||||||
|
// elements we know will be there... right?
|
||||||
|
export const cmdTextbox = document.getElementById("command-textbox")!
|
||||||
32
src/js/focus.ts
Normal file
32
src/js/focus.ts
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
import { cmdTextbox } from "./dom.js"
|
||||||
|
|
||||||
|
export function focusTextbox() {
|
||||||
|
cmdTextbox.focus()
|
||||||
|
}
|
||||||
|
|
||||||
|
// clicking anywhere outside of a link should focus the prompt, unless the user is
|
||||||
|
// selecting text or focusing an input
|
||||||
|
export function focusHandler(e: MouseEvent) {
|
||||||
|
const target = e.target
|
||||||
|
|
||||||
|
// who knows where they clicked... just focus the textbox
|
||||||
|
if (!(target instanceof HTMLElement)) {
|
||||||
|
cmdTextbox.focus()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// let them click on links
|
||||||
|
const a = target.closest("a")
|
||||||
|
|
||||||
|
if (!a) {
|
||||||
|
if (target.tagName === "INPUT" || target.tagName === "TEXTAREA")
|
||||||
|
return false
|
||||||
|
|
||||||
|
const selection = window.getSelection() || ""
|
||||||
|
if (selection.toString() === "")
|
||||||
|
cmdTextbox.focus()
|
||||||
|
|
||||||
|
e.preventDefault()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,28 +1,11 @@
|
||||||
const content = document.getElementById("content")!
|
import { resize } from "./resize.js"
|
||||||
|
import { focusTextbox, focusHandler } from "./focus.js"
|
||||||
|
import { startVramCounter } from "./vram.js"
|
||||||
|
|
||||||
function resize() {
|
window.addEventListener("click", focusHandler)
|
||||||
if (document.body.dataset.mode === "tall") {
|
focusTextbox()
|
||||||
resizeTall()
|
|
||||||
} else {
|
|
||||||
resizeCinema()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function resizeTall() {
|
|
||||||
const scale = Math.min(1, window.innerWidth / 960)
|
|
||||||
content.style.transformOrigin = 'top center'
|
|
||||||
content.style.transform = `scaleX(${scale})`
|
|
||||||
}
|
|
||||||
|
|
||||||
function resizeCinema() {
|
|
||||||
const scale = Math.min(
|
|
||||||
window.innerWidth / 960,
|
|
||||||
window.innerHeight / 540
|
|
||||||
)
|
|
||||||
|
|
||||||
content.style.transformOrigin = 'center center'
|
|
||||||
content.style.transform = `scale(${scale})`
|
|
||||||
}
|
|
||||||
|
|
||||||
window.addEventListener("resize", resize)
|
window.addEventListener("resize", resize)
|
||||||
resize()
|
resize()
|
||||||
|
|
||||||
|
startVramCounter()
|
||||||
26
src/js/resize.ts
Normal file
26
src/js/resize.ts
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
const content = document.getElementById("content")!
|
||||||
|
|
||||||
|
export function resize() {
|
||||||
|
if (document.body.dataset.mode === "tall") {
|
||||||
|
resizeTall()
|
||||||
|
} else {
|
||||||
|
resizeCinema()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function resizeTall() {
|
||||||
|
const scale = Math.min(1, window.innerWidth / 960)
|
||||||
|
content.style.transformOrigin = 'top center'
|
||||||
|
content.style.transform = `scaleX(${scale})`
|
||||||
|
}
|
||||||
|
|
||||||
|
function resizeCinema() {
|
||||||
|
const scale = Math.min(
|
||||||
|
window.innerWidth / 960,
|
||||||
|
window.innerHeight / 540
|
||||||
|
)
|
||||||
|
|
||||||
|
content.style.transformOrigin = 'center center'
|
||||||
|
content.style.transform = `scale(${scale})`
|
||||||
|
}
|
||||||
|
|
||||||
25
src/js/vram.ts
Normal file
25
src/js/vram.ts
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
//
|
||||||
|
// vram
|
||||||
|
//
|
||||||
|
|
||||||
|
import { $ } from "./dom.js"
|
||||||
|
|
||||||
|
const vramCounter = $("vram-size")!
|
||||||
|
|
||||||
|
export const startVramCounter = () => {
|
||||||
|
const timer = setInterval(() => {
|
||||||
|
const count = parseInt(vramCounter.textContent!) + 1
|
||||||
|
let val = count + "KB"
|
||||||
|
if (count < 10)
|
||||||
|
val = "00" + val
|
||||||
|
else if (count < 100)
|
||||||
|
val = "0" + val
|
||||||
|
|
||||||
|
vramCounter.textContent = val
|
||||||
|
|
||||||
|
if (count >= 640) {
|
||||||
|
vramCounter.textContent += " OK"
|
||||||
|
clearInterval(timer)
|
||||||
|
}
|
||||||
|
}, 5)
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user