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>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="stylesheet" href="/css/main.css" />
|
||||
<script src="/js/main.js" async></script>
|
||||
<link href="/css/reset.css" rel="stylesheet" />
|
||||
<link href="/css/main.css" rel="stylesheet" />
|
||||
<script src="/js/main.js" type="module" async></script>
|
||||
</head>
|
||||
<body data-mode="tall">
|
||||
<main>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,30 @@ import type { FC } from "hono/jsx"
|
|||
|
||||
export const Terminal: FC = async () => (
|
||||
<>
|
||||
<h1>Hello NOSE!</h1>
|
||||
<p>This is 960×540 space.</p>
|
||||
<link rel="stylesheet" href="/css/terminal.css" />
|
||||
|
||||
<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;
|
||||
height: 540px;
|
||||
background: var(--c64-dark-blue);
|
||||
color: var(--c64-light-blue);
|
||||
/* nearest-neighbor scaling */
|
||||
image-rendering: pixelated;
|
||||
transform-origin: center center;
|
||||
|
|
@ -95,4 +96,8 @@ main {
|
|||
body[data-mode=tall] #content {
|
||||
height: 100%;
|
||||
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() {
|
||||
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})`
|
||||
}
|
||||
window.addEventListener("click", focusHandler)
|
||||
focusTextbox()
|
||||
|
||||
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