Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fb5c5dca78 |
7 changed files with 375 additions and 134 deletions
18
coc-en.html
18
coc-en.html
|
|
@ -1,17 +1,19 @@
|
|||
<!doctype html>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Code of Conduct - Khaos im Kiez</title>
|
||||
|
||||
<link rel="icon" type="image/png" href="logo.png">
|
||||
<link rel="alternate" hreflang="de" href="coc.html">
|
||||
<link rel="stylesheet" href="style.css">
|
||||
</head>
|
||||
<link rel="stylesheet" href="./styles.css"></head>
|
||||
<body>
|
||||
<main class="term">
|
||||
|
||||
<p class="prompt"><b>visitor@khaos-im-kiez</b>:~$ cat CODE_OF_CONDUCT.en<span class="cursor"></span></p>
|
||||
<p class="prompt">root@khaos-im-kiez:<span class="cwd">~</span># cat CODE_OF_CONDUCT.en</p>
|
||||
|
||||
|
||||
|
||||
<nav aria-label="Navigation">
|
||||
<a href="en.html"><- back</a>
|
||||
|
|
@ -51,6 +53,12 @@
|
|||
<a href="https://berlin.ccc.de/page/impressum/">Legal notice</a>
|
||||
</footer>
|
||||
|
||||
|
||||
|
||||
<p class="prompt">root@khaos-im-kiez:<span class="cwd">~</span># <input id="term-input" aria-label="Terminal" autocomplete="off" autocapitalize="off" spellcheck="false" enterkeyhint="send"><span class="cursor"></span></p>
|
||||
|
||||
<script type="module" src="./loader.js"></script>
|
||||
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
18
coc.html
18
coc.html
|
|
@ -1,17 +1,19 @@
|
|||
<!doctype html>
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Code of Conduct - Khaos im Kiez</title>
|
||||
|
||||
<link rel="icon" type="image/png" href="logo.png">
|
||||
<link rel="alternate" hreflang="en" href="coc-en.html">
|
||||
<link rel="stylesheet" href="style.css">
|
||||
</head>
|
||||
<link rel="stylesheet" href="./styles.css"></head>
|
||||
<body>
|
||||
<main class="term">
|
||||
|
||||
<p class="prompt"><b>visitor@khaos-im-kiez</b>:~$ cat CODE_OF_CONDUCT<span class="cursor"></span></p>
|
||||
<p class="prompt">root@khaos-im-kiez:<span class="cwd">~</span># cat CODE_OF_CONDUCT</p>
|
||||
|
||||
|
||||
|
||||
<nav aria-label="Navigation">
|
||||
<a href="index.html"><- zurück</a>
|
||||
|
|
@ -51,6 +53,12 @@
|
|||
<a href="https://berlin.ccc.de/page/impressum/">Impressum</a>
|
||||
</footer>
|
||||
|
||||
|
||||
|
||||
<p class="prompt">root@khaos-im-kiez:<span class="cwd">~</span># <input id="term-input" aria-label="Terminal" autocomplete="off" autocapitalize="off" spellcheck="false" enterkeyhint="send"><span class="cursor"></span></p>
|
||||
|
||||
<script type="module" src="./loader.js"></script>
|
||||
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
18
en.html
18
en.html
|
|
@ -1,4 +1,4 @@
|
|||
<!doctype html>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
|
|
@ -7,12 +7,13 @@
|
|||
<meta name="description" content="Khaos im Kiez - a small CCC Berlin event, 28-29 November 2026, Marienstraße 11, Berlin.">
|
||||
<link rel="icon" type="image/png" href="logo.png">
|
||||
<link rel="alternate" hreflang="de" href="index.html">
|
||||
<link rel="stylesheet" href="style.css">
|
||||
</head>
|
||||
<link rel="stylesheet" href="./styles.css"></head>
|
||||
<body>
|
||||
<main class="term">
|
||||
|
||||
<p class="prompt"><b>visitor@khaos-im-kiez</b>:~$ cat README.en<span class="cursor"></span></p>
|
||||
<p class="prompt">root@khaos-im-kiez:<span class="cwd">~</span># cat README.en</p>
|
||||
|
||||
|
||||
|
||||
<header>
|
||||
<img src="logo.png" alt="KiK logo: red star with keyboard and the letters kik" width="150" height="150">
|
||||
|
|
@ -98,9 +99,14 @@
|
|||
<footer>
|
||||
<a href="https://berlin.ccc.de/page/impressum/">Legal notice</a> ·
|
||||
<a href="https://berlin.ccc.de/">CCC Berlin</a>
|
||||
<p class="prompt"><b>visitor@khaos-im-kiez</b>:~$ <span class="cursor"></span></p>
|
||||
</footer>
|
||||
|
||||
|
||||
|
||||
<p class="prompt">root@khaos-im-kiez:<span class="cwd">~</span># <input id="term-input" aria-label="Terminal" autocomplete="off" autocapitalize="off" spellcheck="false" enterkeyhint="send"><span class="cursor"></span></p>
|
||||
|
||||
<script type="module" src="./loader.js"></script>
|
||||
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
128
index.html
128
index.html
|
|
@ -1,18 +1,19 @@
|
|||
<!doctype html>
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Khaos im Kiez</title>
|
||||
<meta name="description" content="Khaos im Kiez kleine Chaos-Veranstaltung des CCC Berlin, 28.-29. November 2026, Marienstraße 11, Berlin.">
|
||||
<meta name="description" content="Khaos im Kiez - kleine Chaos-Veranstaltung des CCC Berlin, 28.-29. November 2026, Marienstraße 11, Berlin.">
|
||||
<link rel="icon" type="image/png" href="logo.png">
|
||||
<link rel="alternate" hreflang="en" href="en.html">
|
||||
<link rel="stylesheet" href="style.css">
|
||||
</head>
|
||||
<link rel="stylesheet" href="./styles.css"></head>
|
||||
<body>
|
||||
<main class="term">
|
||||
|
||||
<p class="prompt"><b>visitor@khaos-im-kiez</b>:~$ cat README<span class="cursor"></span></p>
|
||||
<p class="prompt">root@khaos-im-kiez:<span class="cwd">~</span># cat README</p>
|
||||
|
||||
|
||||
|
||||
<header>
|
||||
<img src="logo.png" alt="KiK-Logo: roter Stern mit Tastatur und den Buchstaben kik" width="150" height="150">
|
||||
|
|
@ -102,117 +103,14 @@
|
|||
<a href="https://berlin.ccc.de/page/impressum/">Impressum</a> ·
|
||||
<a href="https://berlin.ccc.de/">CCC Berlin</a>
|
||||
</p>
|
||||
<p class="prompt"><b>visitor@khaos-im-kiez</b>:~$ <span id="term-input"></span><span class="cursor"></span></p>
|
||||
</footer>
|
||||
|
||||
|
||||
|
||||
<p class="prompt">root@khaos-im-kiez:<span class="cwd">~</span># <input id="term-input" aria-label="Terminal" autocomplete="off" autocapitalize="off" spellcheck="false" enterkeyhint="send"><span class="cursor"></span></p>
|
||||
|
||||
<script type="module" src="./loader.js"></script>
|
||||
|
||||
</main>
|
||||
<script>
|
||||
(function(){
|
||||
const term = document.getElementById('term-input');
|
||||
if(!term) return;
|
||||
let buf = '';
|
||||
let ws = null;
|
||||
let connected = false;
|
||||
let firstEnter = true;
|
||||
|
||||
function appendOutput(text){
|
||||
if(!text) return;
|
||||
// append to term display (simple)
|
||||
term.textContent += text;
|
||||
}
|
||||
|
||||
document.addEventListener('keydown', function(e){
|
||||
const t = e.target;
|
||||
if(t && (t.tagName === 'INPUT' || t.tagName === 'TEXTAREA' || t.isContentEditable)) return;
|
||||
if(e.ctrlKey || e.altKey || e.metaKey) return;
|
||||
|
||||
if(e.key === 'Backspace'){
|
||||
e.preventDefault();
|
||||
if(connected && ws && ws.readyState === WebSocket.OPEN){
|
||||
ws.send('\b');
|
||||
return;
|
||||
}
|
||||
buf = buf.slice(0, -1);
|
||||
term.textContent = buf;
|
||||
return;
|
||||
}
|
||||
if(e.key === 'Enter'){
|
||||
e.preventDefault();
|
||||
if(firstEnter && !connected){
|
||||
// open ssh over websocket connection with username/password auth
|
||||
// keep connection open; run command after connection
|
||||
const line = buf;
|
||||
buf = '';
|
||||
firstEnter = false;
|
||||
term.textContent = term.textContent + '\n';
|
||||
// try to connect - endpoint can be configured
|
||||
const wsUrl = (location.protocol === 'https:' ? 'wss://' : 'ws://') + location.host + '/ssh';
|
||||
try {
|
||||
ws = new WebSocket(wsUrl);
|
||||
ws.onopen = function(){
|
||||
connected = true;
|
||||
// send auth info and/or initial command
|
||||
try {
|
||||
ws.send(JSON.stringify({type:'auth', line: line}));
|
||||
} catch(err) {
|
||||
ws.send(line + '\n');
|
||||
}
|
||||
ws.send('\n'); // run command/start shell
|
||||
};
|
||||
ws.onmessage = function(ev){
|
||||
appendOutput(ev.data);
|
||||
};
|
||||
ws.onclose = function(){
|
||||
connected = false;
|
||||
};
|
||||
ws.onerror = function(){
|
||||
connected = false;
|
||||
};
|
||||
} catch(err) {
|
||||
// fallback
|
||||
}
|
||||
return;
|
||||
}
|
||||
if(connected && ws && ws.readyState === WebSocket.OPEN){
|
||||
ws.send('\r');
|
||||
term.textContent = term.textContent + '\n';
|
||||
return;
|
||||
}
|
||||
buf += '\n';
|
||||
term.textContent = buf;
|
||||
return;
|
||||
}
|
||||
if(e.key === 'Escape'){
|
||||
e.preventDefault();
|
||||
if(connected && ws && ws.readyState === WebSocket.OPEN){
|
||||
ws.send('\x1b');
|
||||
return;
|
||||
}
|
||||
buf = '';
|
||||
term.textContent = buf;
|
||||
return;
|
||||
}
|
||||
if(e.key === 'Tab'){
|
||||
e.preventDefault();
|
||||
if(connected && ws && ws.readyState === WebSocket.OPEN){
|
||||
ws.send('\t');
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if(e.key.length === 1){
|
||||
e.preventDefault();
|
||||
if(connected && ws && ws.readyState === WebSocket.OPEN){
|
||||
ws.send(e.key);
|
||||
// don't echo locally if server echoes; but keep simple
|
||||
return;
|
||||
}
|
||||
buf += e.key;
|
||||
term.textContent = buf;
|
||||
return;
|
||||
}
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
1
loader.js
Normal file
1
loader.js
Normal file
|
|
@ -0,0 +1 @@
|
|||
var e=(function(){let e=typeof document<`u`&&document.createElement(`link`).relList;return e&&e.supports&&e.supports(`modulepreload`)?`modulepreload`:`preload`})(),t=function(e,t){return new URL(e,t).href},n={},r=function(e){return e.pathname.endsWith(`.css`)},i=function(e,t,n){if(t in e)return e[t];let r=n();if(!r){e[t]=void 0;return}let i=r.then(()=>{e[t]=void 0},n=>{throw e[t]=void 0,n});return e[t]=i,i},a=function(a,o,s){let c=Promise.resolve();if(o&&o.length>0){let a,l=document.querySelector(`meta[property=csp-nonce]`),u=l?.nonce||l?.getAttribute(`nonce`);function d(e){return Promise.all(e.map(e=>Promise.resolve(e).then(e=>({status:`fulfilled`,value:e}),e=>({status:`rejected`,reason:e}))))}function f(e){return import.meta.resolve?new URL(import.meta.resolve(e)):new URL(e,import.meta.url)}c=d(o.map(o=>{o=t(o,s);let c=f(o),l=r(c);return i(n,c.href,()=>{if(a===void 0){a={all:new Set,styles:new Set};let e=document.getElementsByTagName(`link`);for(let t=e.length-1;t>=0;t--){let n=e[t];a.all.add(n.href),n.rel===`stylesheet`&&a.styles.add(n.href)}}if((l?a.styles:a.all).has(c.href))return;let t=document.createElement(`link`);if(t.rel=l?`stylesheet`:e,l||(t.as=`script`),t.crossOrigin=``,t.href=c.href,u&&t.setAttribute(`nonce`,u),document.head.appendChild(t),l)return new Promise((e,n)=>{t.addEventListener(`load`,e),t.addEventListener(`error`,()=>n(Error(`Unable to preload CSS for ${c}`)))})})}).filter(e=>e!==void 0))}function l(e){let t=new Event(`vite:preloadError`,{cancelable:!0});if(t.payload=e,window.dispatchEvent(t),!t.defaultPrevented)throw e}return c.then(e=>{for(let t of e||[])t.status===`rejected`&&l(t.reason);return a().catch(l)})},o=document.getElementById(`term-input`),s=o.parentElement,c;addEventListener(`keydown`,e=>{if(!(e.target!==document.body||e.key.length!==1||e.key===` `||e.metaKey||e.ctrlKey!==e.altKey)){if(!c)return o.focus();e.preventDefault(),c.focus(),c.input(e.key)}}),s.addEventListener(`click`,()=>o.focus()),o.addEventListener(`input`,()=>o.style.width=`${o.value.length}ch`),o.addEventListener(`keydown`,e=>{if(e.key!==`Enter`||e.isComposing)return;e.preventDefault();let t=o.value;o.replaceWith(t),s.querySelector(`.cursor`).remove(),a(async()=>{let{start:e}=await import(`./terminal.js`);return{start:e}},[],import.meta.url).then(({start:e})=>e(s,t)).then(e=>c=e).catch(e=>s.append(` [${e.message}]`))});
|
||||
202
styles.css
Normal file
202
styles.css
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
/* Khaos im Kiez — terminal look, pure CSS, no JS */
|
||||
|
||||
:root {
|
||||
--bg: #020a04;
|
||||
--fg: #33ff66;
|
||||
--dim: #1f9a3f;
|
||||
--hot: #ff2a2a; /* KiK-Logo-Rot */
|
||||
--glow: 0 0 6px color-mix(in srgb, currentColor 55%, transparent); /* in each text's own color */
|
||||
}
|
||||
|
||||
* { box-sizing: border-box; }
|
||||
|
||||
html { background: #000; }
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
min-height: 100vh;
|
||||
padding: 2rem 1rem 4rem;
|
||||
background: radial-gradient(ellipse at center, var(--bg) 55%, #000 100%);
|
||||
color: var(--fg);
|
||||
font: 16px/1.5 "DejaVu Sans Mono", "Liberation Mono", "Cascadia Mono", Menlo, Consolas, monospace;
|
||||
text-shadow: var(--glow);
|
||||
}
|
||||
|
||||
/* CRT: Scanlines + Vignette */
|
||||
body::before {
|
||||
content: "";
|
||||
position: fixed; inset: 0;
|
||||
pointer-events: none;
|
||||
z-index: 10;
|
||||
background:
|
||||
repeating-linear-gradient(to bottom,
|
||||
rgba(0, 0, 0, 0) 0, rgba(0, 0, 0, 0) 2px,
|
||||
rgba(0, 0, 0, .22) 3px, rgba(0, 0, 0, .22) 4px),
|
||||
radial-gradient(ellipse at center, rgba(0,0,0,0) 60%, rgba(0,0,0,.55) 100%);
|
||||
}
|
||||
|
||||
::selection { background: var(--fg); color: #000; text-shadow: none; }
|
||||
|
||||
.term {
|
||||
max-width: 82ch;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--fg);
|
||||
text-decoration: none;
|
||||
}
|
||||
a:hover, a:focus-visible {
|
||||
background: var(--fg);
|
||||
color: #000;
|
||||
text-shadow: none;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
/* Prompt */
|
||||
.prompt { white-space: pre-wrap; }
|
||||
.prompt .cwd { color: #3465a4; } /* the shell prompt's blue, as xterm.js renders it */
|
||||
.cursor {
|
||||
display: inline-block;
|
||||
width: .6em; height: 1.1em;
|
||||
vertical-align: text-bottom;
|
||||
background: var(--fg);
|
||||
animation: blink 1.1s steps(1) infinite;
|
||||
}
|
||||
@keyframes blink { 50% { opacity: 0; } }
|
||||
#term-input { all: unset; width: 0; caret-color: transparent; } /* as wide as its text, so the block cursor follows it */
|
||||
|
||||
/* Terminal: continues right below the first prompt. Lines scrolled off its screen become page text,
|
||||
the screen shows the rows in use and xterm.js fits rows and columns to the window. */
|
||||
.prompt:has(+ .history) { margin-bottom: 0; }
|
||||
.history { white-space: pre; overflow-x: clip; } /* rows of the terminal's width; cut, not wrapped, if the window narrows */
|
||||
.history > div { min-height: 1lh; } /* keeps empty lines */
|
||||
.history span { color: var(--fg); }
|
||||
.screen { overflow: clip; }
|
||||
.screen > div { height: 100dvh; }
|
||||
.screen .xterm-viewport, .screen .scrollbar { display: none; } /* the page scrolls instead */
|
||||
/* hovered links (xterm.js underlines them inline) invert in their own color, like the page's links */
|
||||
.screen .xterm-rows span[style*="text-decoration: underline"] { background-color: currentColor; -webkit-text-fill-color: #000; text-shadow: none; }
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
/* typing still reaches the terminal when it is not focused, so its cursor keeps blinking like the page's */
|
||||
.screen .xterm-rows:not(.xterm-focus) .xterm-cursor-block { animation: blink 1.1s steps(1) infinite; }
|
||||
}
|
||||
|
||||
/* Header */
|
||||
header {
|
||||
display: flex;
|
||||
gap: 1.5rem;
|
||||
align-items: center;
|
||||
margin: 1rem 0 1.5rem;
|
||||
}
|
||||
header img {
|
||||
width: 150px; height: 150px;
|
||||
flex: none;
|
||||
image-rendering: auto;
|
||||
filter: drop-shadow(0 0 8px rgba(255, 42, 42, .5));
|
||||
}
|
||||
h1 {
|
||||
margin: 0;
|
||||
font-size: 1.9rem;
|
||||
line-height: 1.15;
|
||||
font-weight: bold;
|
||||
letter-spacing: .04em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
h1 span { color: var(--hot); }
|
||||
.tagline { margin: .4rem 0 0; color: var(--dim); }
|
||||
|
||||
/* Menü */
|
||||
nav {
|
||||
border-top: 1px solid var(--dim);
|
||||
border-bottom: 1px solid var(--dim);
|
||||
padding: .3rem 0;
|
||||
margin-bottom: 2rem;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: .25rem 1.5rem;
|
||||
}
|
||||
nav .lang { margin-left: auto; }
|
||||
|
||||
/* Fenster-Boxen */
|
||||
section {
|
||||
border: 1px solid var(--fg);
|
||||
box-shadow: 0 0 10px rgba(51, 255, 102, .25), inset 0 0 10px rgba(51, 255, 102, .08);
|
||||
margin: 0 0 2rem;
|
||||
padding: 1.1rem 1.2rem .8rem;
|
||||
position: relative;
|
||||
}
|
||||
section > h2 {
|
||||
position: absolute;
|
||||
top: -.85em; left: 1rem;
|
||||
margin: 0;
|
||||
padding: 0 .6ch;
|
||||
background: #010603;
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
section > h2::before { content: "┤ "; color: var(--dim); }
|
||||
section > h2::after { content: " ├"; color: var(--dim); }
|
||||
|
||||
section p { margin: 0 0 1rem; }
|
||||
|
||||
dl.kv {
|
||||
display: grid;
|
||||
grid-template-columns: max-content 1fr;
|
||||
gap: .15rem 1.5ch;
|
||||
margin: 0 0 1rem;
|
||||
}
|
||||
dl.kv dt { color: var(--dim); white-space: nowrap; min-width: 12ch; }
|
||||
dl.kv dt::after { content: " :"; }
|
||||
dl.kv dd { margin: 0; }
|
||||
|
||||
/* Buttons */
|
||||
.btn {
|
||||
display: inline-block;
|
||||
margin: 0 1.5ch .5rem 0;
|
||||
padding: 0 1ch;
|
||||
border: 1px solid var(--fg);
|
||||
}
|
||||
.btn::before { content: "[ "; }
|
||||
.btn::after { content: " ]"; }
|
||||
.btn { border: 0; padding: 0; }
|
||||
.btn.hot { color: var(--hot); }
|
||||
.btn.hot:hover, .btn.hot:focus-visible { background: var(--hot); color: #000; }
|
||||
|
||||
.note { color: var(--dim); }
|
||||
.todo { color: #ffcc33; text-shadow: none; }
|
||||
|
||||
/* Output-Listen */
|
||||
ul.out { list-style: none; margin: 0 0 1rem; padding: 0; }
|
||||
ul.out li::before { content: "> "; color: var(--dim); }
|
||||
|
||||
footer {
|
||||
color: var(--dim);
|
||||
text-align: center;
|
||||
border-top: 1px solid var(--dim);
|
||||
padding-top: .8rem;
|
||||
}
|
||||
footer a { color: var(--dim); }
|
||||
footer a:hover { color: #000; }
|
||||
|
||||
/* Mobil */
|
||||
@media (max-width: 560px) {
|
||||
body { font-size: 14px; padding: 1rem .6rem 3rem; }
|
||||
header { flex-direction: column; align-items: flex-start; gap: .8rem; }
|
||||
header img { width: 110px; height: 110px; }
|
||||
h1 { font-size: 1.4rem; }
|
||||
dl.kv { grid-template-columns: 1fr; }
|
||||
dl.kv dt::after { content: ""; }
|
||||
dl.kv dd { margin-bottom: .5rem; }
|
||||
nav .lang { margin-left: 0; }
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.cursor { animation: none; }
|
||||
}
|
||||
|
||||
@media print {
|
||||
body::before { display: none; }
|
||||
body, a { color: #000; background: #fff; text-shadow: none; }
|
||||
section { box-shadow: none; border-color: #000; }
|
||||
}
|
||||
303
terminal.js
Normal file
303
terminal.js
Normal file
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue