🎉 Initial commit

This commit is contained in:
KaKi87 2021-11-12 18:30:08 +01:00
commit 9563924165
28 changed files with 23642 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
.idea
node_modules

15
package.json Normal file
View File

@ -0,0 +1,15 @@
{
"name": "thornhill-os-lite",
"version": "0.1.0",
"repository": "ssh://kaki@git.kaki87.net:3021/thornhill-corp/thornhill-os-lite.git",
"author": "KaKi87 <KaKi87@pm.me>",
"license": "CC-BY-NC-SA-4.0",
"devDependencies": {
"@tauri-apps/cli": "^1.0.0-beta.10",
"tiny-server": "^1.1.0"
},
"scripts": {
"start": "./node_modules/.bin/tauri dev",
"build": "./node_modules/.bin/tauri build"
}
}

4
src-tauri/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
# Generated by Cargo
# will have compiled files and executables
/target/
WixTools

3705
src-tauri/Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

24
src-tauri/Cargo.toml Normal file
View File

@ -0,0 +1,24 @@
[package]
name = "app"
version = "0.1.0"
description = "A Tauri App"
authors = ["you"]
license = ""
repository = ""
default-run = "app"
edition = "2018"
build = "src/build.rs"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[build-dependencies]
tauri-build = { version = "1.0.0-beta.4" }
[dependencies]
serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] }
tauri = { version = "1.0.0-beta.8", features = ["api-all", "window-all"] }
[features]
default = [ "custom-protocol" ]
custom-protocol = [ "tauri/custom-protocol" ]

14
src-tauri/rustfmt.toml Normal file
View File

@ -0,0 +1,14 @@
max_width = 100
hard_tabs = false
tab_spaces = 2
newline_style = "Auto"
use_small_heuristics = "Default"
reorder_imports = true
reorder_modules = true
remove_nested_parens = true
edition = "2018"
merge_derives = true
use_try_shorthand = false
use_field_init_shorthand = false
force_explicit_abi = true
imports_granularity = "Crate"

3
src-tauri/src/build.rs Normal file
View File

@ -0,0 +1,3 @@
fn main() {
tauri_build::build()
}

10
src-tauri/src/main.rs Normal file
View File

@ -0,0 +1,10 @@
#![cfg_attr(
all(not(debug_assertions), target_os = "windows"),
windows_subsystem = "windows"
)]
fn main() {
tauri::Builder::default()
.run(tauri::generate_context!())
.expect("error while running tauri application");
}

68
src-tauri/tauri.conf.json Normal file
View File

@ -0,0 +1,68 @@
{
"package": {
"productName": "thornhill-os-lite",
"version": "0.1.0"
},
"build": {
"distDir": "../www",
"devPath": "http://localhost:5492/index.html",
"beforeDevCommand": "env PORT=5492 ./node_modules/.bin/tiny-server ./www",
"beforeBuildCommand": "",
"withGlobalTauri": true
},
"tauri": {
"bundle": {
"active": true,
"targets": "all",
"identifier": "com.tauri.dev",
"icon": [
"icons/32x32.png",
"icons/128x128.png",
"icons/128x128@2x.png",
"icons/icon.icns",
"icons/icon.ico"
],
"resources": [],
"externalBin": [],
"copyright": "",
"category": "DeveloperTool",
"shortDescription": "",
"longDescription": "",
"deb": {
"depends": [],
"useBootstrapper": false
},
"macOS": {
"frameworks": [],
"minimumSystemVersion": "",
"useBootstrapper": false,
"exceptionDomain": "",
"signingIdentity": null,
"entitlements": null
},
"windows": {
"certificateThumbprint": null,
"digestAlgorithm": "sha256",
"timestampUrl": ""
}
},
"updater": {
"active": false
},
"allowlist": {
"all": true
},
"windows": [
{
"title": "thornhill-os-lite",
"width": 800,
"height": 600,
"resizable": true,
"fullscreen": false
}
],
"security": {
"csp": "default-src blob: data: filesystem: ws: wss: http: https: tauri: 'unsafe-eval' 'unsafe-inline' 'self' img-src: 'self'"
}
}
}

BIN
www/assets/Apple.woff2 Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
www/assets/Monaco.woff2 Normal file

Binary file not shown.

420
www/assets/destyle.css Normal file
View File

@ -0,0 +1,420 @@
/*! destyle.css v3.0.0 | MIT License | https://github.com/nicolas-cusan/destyle.css */
/* Reset box-model and set borders */
/* ============================================ */
*,
::before,
::after {
box-sizing: border-box;
border-style: solid;
border-width: 0;
}
/* Document */
/* ============================================ */
/**
* 1. Correct the line height in all browsers.
* 2. Prevent adjustments of font size after orientation changes in iOS.
* 3. Remove gray overlay on links for iOS.
*/
html {
line-height: 1.15; /* 1 */
-webkit-text-size-adjust: 100%; /* 2 */
-webkit-tap-highlight-color: transparent; /* 3*/
}
/* Sections */
/* ============================================ */
/**
* Remove the margin in all browsers.
*/
body {
margin: 0;
}
/**
* Render the `main` element consistently in IE.
*/
main {
display: block;
}
/* Vertical rhythm */
/* ============================================ */
p,
table,
blockquote,
address,
pre,
iframe,
form,
figure,
dl {
margin: 0;
}
/* Headings */
/* ============================================ */
h1,
h2,
h3,
h4,
h5,
h6 {
font-size: inherit;
font-weight: inherit;
margin: 0;
}
/* Lists (enumeration) */
/* ============================================ */
ul,
ol {
margin: 0;
padding: 0;
list-style: none;
}
/* Lists (definition) */
/* ============================================ */
dt {
font-weight: bold;
}
dd {
margin-left: 0;
}
/* Grouping content */
/* ============================================ */
/**
* 1. Add the correct box sizing in Firefox.
* 2. Show the overflow in Edge and IE.
*/
hr {
box-sizing: content-box; /* 1 */
height: 0; /* 1 */
overflow: visible; /* 2 */
border-top-width: 1px;
margin: 0;
clear: both;
color: inherit;
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
pre {
font-family: monospace, monospace; /* 1 */
font-size: inherit; /* 2 */
}
address {
font-style: inherit;
}
/* Text-level semantics */
/* ============================================ */
/**
* Remove the gray background on active links in IE 10.
*/
a {
background-color: transparent;
text-decoration: none;
color: inherit;
}
/**
* 1. Remove the bottom border in Chrome 57-
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
*/
abbr[title] {
text-decoration: underline dotted; /* 2 */
}
/**
* Add the correct font weight in Chrome, Edge, and Safari.
*/
b,
strong {
font-weight: bolder;
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
code,
kbd,
samp {
font-family: monospace, monospace; /* 1 */
font-size: inherit; /* 2 */
}
/**
* Add the correct font size in all browsers.
*/
small {
font-size: 80%;
}
/**
* Prevent `sub` and `sup` elements from affecting the line height in
* all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
/* Replaced content */
/* ============================================ */
/**
* Prevent vertical alignment issues.
*/
svg,
img,
embed,
object,
iframe {
vertical-align: bottom;
}
/* Forms */
/* ============================================ */
/**
* Reset form fields to make them styleable.
* 1. Make form elements stylable across systems iOS especially.
* 2. Inherit text-transform from parent.
*/
button,
input,
optgroup,
select,
textarea {
-webkit-appearance: none; /* 1 */
appearance: none;
vertical-align: middle;
color: inherit;
font: inherit;
background: transparent;
padding: 0;
margin: 0;
border-radius: 0;
text-align: inherit;
text-transform: inherit; /* 2 */
}
/**
* Reset radio and checkbox appearance to preserve their look in iOS.
*/
[type="checkbox"] {
-webkit-appearance: checkbox;
appearance: checkbox;
}
[type="radio"] {
-webkit-appearance: radio;
appearance: radio;
}
/**
* Correct cursors for clickable elements.
*/
button,
[type="button"],
[type="reset"],
[type="submit"] {
cursor: pointer;
}
button:disabled,
[type="button"]:disabled,
[type="reset"]:disabled,
[type="submit"]:disabled {
cursor: default;
}
/**
* Improve outlines for Firefox and unify style with input elements & buttons.
*/
:-moz-focusring {
outline: auto;
}
select:disabled {
opacity: inherit;
}
/**
* Remove padding
*/
option {
padding: 0;
}
/**
* Reset to invisible
*/
fieldset {
margin: 0;
padding: 0;
min-width: 0;
}
legend {
padding: 0;
}
/**
* Add the correct vertical alignment in Chrome, Firefox, and Opera.
*/
progress {
vertical-align: baseline;
}
/**
* Remove the default vertical scrollbar in IE 10+.
*/
textarea {
overflow: auto;
}
/**
* Correct the cursor style of increment and decrement buttons in Chrome.
*/
[type="number"]::-webkit-inner-spin-button,
[type="number"]::-webkit-outer-spin-button {
height: auto;
}
/**
* 1. Correct the outline style in Safari.
*/
[type="search"] {
outline-offset: -2px; /* 1 */
}
/**
* Remove the inner padding in Chrome and Safari on macOS.
*/
[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
/**
* 1. Correct the inability to style clickable types in iOS and Safari.
* 2. Fix font inheritance.
*/
::-webkit-file-upload-button {
-webkit-appearance: button; /* 1 */
font: inherit; /* 2 */
}
/**
* Clickable labels
*/
label[for] {
cursor: pointer;
}
/* Interactive */
/* ============================================ */
/*
* Add the correct display in Edge, IE 10+, and Firefox.
*/
details {
display: block;
}
/*
* Add the correct display in all browsers.
*/
summary {
display: list-item;
}
/*
* Remove outline for editable content.
*/
[contenteditable]:focus {
outline: auto;
}
/* Tables */
/* ============================================ */
/**
1. Correct table border color inheritance in all Chrome and Safari.
*/
table {
border-color: inherit; /* 1 */
}
caption {
text-align: left;
}
td,
th {
vertical-align: top;
padding: 0;
}
th {
text-align: left;
font-weight: bold;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 829 B

15886
www/assets/vue.esm-browser.js Normal file

File diff suppressed because it is too large Load Diff

42
www/components/App.css Normal file
View File

@ -0,0 +1,42 @@
:root {
/*
Colors
*/
--color-white: #DEDEDE;
--color-gray: #959595;
--color-darkgray: #363A3F;
--color-darkergray: #1E2325;
--color-red: #E20000;
--color-yellow: #F2AF16;
--color-green: #01C17C;
--color-blue: #116BFF;
/*
Font sizes
*/
--font-big: 1.4rem;
--font-normal: 1.2rem;
--font-small: 0.9rem;
--font-smaller: 0.75rem;
}
/*
Fonts
*/
@font-face {
font-family: 'Futura';
src: url('../assets/Futura_Std_Medium.woff2') format('woff2');
font-weight: normal;
}
@font-face {
font-family: 'Futura';
src: url('../assets/Futura_Std_Bold.woff2') format('woff2');
font-weight: bold;
}
@font-face {
font-family: 'Monaco';
src: url('../assets/Monaco.woff2') format('woff2');
}
@font-face {
font-family: 'Apple';
src: url('../assets/Apple.woff2') format('woff2');
}

53
www/components/App.js Normal file
View File

@ -0,0 +1,53 @@
import { createApp } from '../assets/vue.esm-browser.js';
import css from '../lib/css.js';
import Terminal from './Terminal/Terminal.js';
import TerminalMainEntry from './Terminal/TerminalMainEntry.js';
css(import.meta.url);
const app = createApp({
'data': () => ({
isTerminalOpen: false
}),
'methods': {
keydown: function(event){
if(!this.isTerminalOpen && event.code === 'Space')
event.preventDefault();
},
keyup: function(event){
switch(event.code){
case 'Space': {
if(!this.isTerminalOpen)
this.isTerminalOpen = true;
break;
}
case 'Escape': {
if(this.isTerminalOpen)
this.isTerminalOpen = false;
break;
}
case 'F5': {
window.location.reload();
}
}
}
},
'mounted': function(){
window.addEventListener('keydown', this.keydown);
window.addEventListener('keyup', this.keyup);
},
'beforeUnmount': function(){
window.removeEventListener('keydown', this.keydown);
window.removeEventListener('keyup', this.keyup);
},
'template': `
<Terminal v-if="isTerminalOpen" />
`
});
app.component('Terminal', Terminal);
app.component('TerminalMainEntry', TerminalMainEntry);
export default app;

View File

@ -0,0 +1,23 @@
.Terminal {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 35rem;
box-shadow: 0 0 0.5rem;
}
.Terminal__Titlebar {
height: 1.5rem;
background: linear-gradient(#294A5B, #15303E);
}
.Terminal__Titlebar__Icon {
height: 1.5rem;
margin-left: 0.1rem;
}
.Terminal__Main {
height: 12rem;
overflow-y: scroll;
border: 0.1rem solid #6795A8;
background-color: black;
}
.Terminal__Main::-webkit-scrollbar { width: 0 !important }

View File

@ -0,0 +1,86 @@
import css from '../../lib/css.js';
import terminal from '../../lib/terminal.js';
css(import.meta.url);
const history = [];
export default {
'data': () => ({
entries: [],
isMounted: false,
mainRef: undefined
}),
'computed': {
history: () => history
},
'methods': {
focus: async function(){
// TODO prevent when selection
await this.$nextTick();
this.entries[this.entries.length - 1].inputRef.$el.focus();
},
scrollToBottom: function(){
this.mainRef.scrollTop = this.mainRef.scrollHeight;
},
newline: async function(){
this.entries.push({ value: '' });
this.scrollToBottom();
await this.focus();
},
parse: async function(command){
const
isComment = command.startsWith('#'),
isBackground = command.endsWith(' &');
if(isComment || isBackground){
this.history.push('');
await this.newline();
}
if(!isComment){
const value = await terminal(
isBackground
? command.slice(0, -2)
: command
);
if(!this.isMounted) return;
if(value)
this.entries.push({ value, isReadOnly: true });
this.history.push('');
await this.newline();
}
},
clear: async function(){
this.entries.splice(0, this.entries.length);
await this.$nextTick();
this.entries.push({ value: this.history[this.history.length - 1] });
await this.focus();
}
},
'mounted': async function(){
this.isMounted = true;
if(this.history[this.history.length - 1] !== '')
this.history.push('');
await this.newline();
},
'beforeUnmount': function(){
this.isMounted = false;
},
'template': `
<div class="Terminal" @click="focus">
<header class="Terminal__Titlebar">
<img class="Terminal__Titlebar__Icon" src="/assets/terminal__titlebar__icon.png" alt="">
</header>
<main class="Terminal__Main" :ref="element => $data.mainRef = element">
<TerminalMainEntry
v-for="entry in entries"
v-bind="entry"
:history="history"
@submit="parse"
@clear="clear"
@cancel="newline"
:ref="element => entry.inputRef = element"
/>
</main>
</div>
`
};

View File

@ -0,0 +1,17 @@
.Terminal__Main__Entry {
color: var(--color-green);
font-family: 'Monaco', monospace;
display: flex;
padding: 0.5rem;
--line-height: 1.25rem;
}
.Terminal__Main__Entry__Prompt {
padding-right: 0.5rem;
line-height: var(--line-height);
}
.Terminal__Main__Entry__Input {
flex-grow: 1;
resize: none;
line-height: var(--line-height);
outline: none;
}

View File

@ -0,0 +1,90 @@
import css from '../../lib/css.js';
css(import.meta.url);
export default {
'props': {
inputRef: undefined,
value: String,
isReadOnly: Boolean,
history: Array
},
'data': function(){
return {
input: this.value,
isActuallyReadOnly: this.isReadOnly,
historyIndex: this.history.length - 1
}
},
'methods': {
updateHeight: async function(){
await this.$nextTick();
},
keydown: async function(event){
if(event.ctrlKey){
switch(event.code){
case 'KeyL': {
this.$emit('clear');
break;
}
case 'KeyC': {
// TODO prevent when selection
this.isActuallyReadOnly = true;
this.history[this.history.length - 1] = '';
this.$emit('cancel');
break;
}
}
return;
}
else {
switch(event.code){
case 'Enter':
case 'NumpadEnter': {
event.preventDefault();
if(!this.input) return;
this.isActuallyReadOnly = true;
this.history[this.history.length - 1] = this.input;
this.$emit('submit', this.input);
break;
}
case 'ArrowUp': {
event.preventDefault();
if(this.historyIndex === 0) return;
this.input = this.history[--this.historyIndex];
break;
}
case 'ArrowDown': {
event.preventDefault();
if(this.historyIndex === this.history.length - 1) return;
this.input = this.history[++this.historyIndex];
break;
}
}
}
await this.updateHeight();
},
keyup: async function(event){
if(event.code.endsWith('Enter')) return event.preventDefault();
this.history[this.historyIndex] = this.input;
await this.updateHeight();
}
},
'mounted': async function(){
await this.updateHeight();
},
'template': `
<label class="Terminal__Main__Entry">
<span class="Terminal__Main__Entry__Prompt">
{{ isReadOnly ? '>' : '$' }}
</span>
<textarea
class="Terminal__Main__Entry__Input"
:readonly="isActuallyReadOnly"
v-model="input"
@keydown="keydown"
@keyup="keyup"
/>
</label>
`
};

25
www/index.html Normal file
View File

@ -0,0 +1,25 @@
<!DOCTYPE html>
<!--suppress JSUnresolvedFunction -->
<html lang="en">
<head>
<title>template-vue3-cdn</title>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="./assets/destyle.css">
<script defer type="module">
import { enableFullscreen } from './lib/system.js';
import app from './components/App.js';
(async () => {
await enableFullscreen();
app.mount('.app');
})();
</script>
<style>
body {
background-color: #2A3D52;
}
</style>
</head>
<body class="app"></body>
</html>

6
www/lib/css.js Normal file
View File

@ -0,0 +1,6 @@
export default componentUrl => {
const cssElement = document.createElement('link');
cssElement.setAttribute('rel', 'stylesheet');
cssElement.setAttribute('href', componentUrl.replace('.js', '.css'));
document.head.appendChild(cssElement);
};

8
www/lib/system.js Normal file
View File

@ -0,0 +1,8 @@
export const enableFullscreen = async () => {
if(!await __TAURI__.window.appWindow.isFullscreen())
await __TAURI__.window.appWindow.setFullscreen(true);
};
export const exit = () => __TAURI__.window.appWindow.close();
export const getAppVersion = __TAURI__.app.getVersion;

77
www/lib/terminal.js Normal file
View File

@ -0,0 +1,77 @@
import {
getAppVersion,
exit
} from './system.js';
export default async command => {
command = command.match(/("[^"]+"|[^\s]+)/g);
const
name = command[0],
args = [],
opts = {};
const parse = value => {
value = value.replace(/"/g, '');
const valueAsNumber = /^[0-9]+$/.test(value) ? +value : undefined;
const isValueSafeNumber =
valueAsNumber
&& valueAsNumber > Number.MIN_SAFE_INTEGER
&& valueAsNumber < Number.MAX_SAFE_INTEGER;
return isValueSafeNumber ? valueAsNumber : value;
};
for(let i = 1; i < command.length; i++){
const
current = command[i],
next = command[i+1];
if(current.startsWith('--')){
const
parsed = current
.slice(2)
.replace(/-[a-z]/gi, _ => _.toUpperCase())
.replace(/-/g, '');
if(!next || next.startsWith('-'))
opts[parsed] = true;
else {
opts[parsed] = parse(next);
i++;
}
}
else if(current.startsWith('-')){
if(current.length === 2){
const parsed = current[1];
if(!next || next.startsWith('-'))
opts[parsed] = true;
else {
opts[parsed] = parse(next);
i++;
}
}
else current.slice(1).split('').forEach(opt => opts[opt] = true);
}
else
args.push(parse(current));
}
switch(name){
case 'about': return `Thornhill O.S. Lite v${await getAppVersion()}`;
case 'shutdown': return exit();
case 'run': {
break;
}
case 'help': {
break;
}
default: {
try {
// TODO
}
catch(error){
console.error(error);
const { message } = error;
return message
? message.startsWith('Cannot find module')
? 'Command not found'
: message.split('\n')[0]
: undefined;
}
}
}
}

3064
yarn.lock Normal file

File diff suppressed because it is too large Load Diff