├── .github └── dependabot.yml ├── .gitignore ├── GUIDES.md ├── README.md ├── babel.config.js ├── build.sh ├── manifest.json ├── package.json ├── public ├── favicon.ico └── index.html ├── screenshots ├── vue-flamingo.png ├── vue-glass.png └── vue-neu.png ├── src ├── App.vue ├── assets │ └── logo.png ├── components │ ├── TerminalBlinkingCaret.vue │ ├── TerminalComponent.vue │ ├── TerminalInsertField.vue │ ├── TerminalOutput.vue │ ├── TerminalPrompt.vue │ └── TerminalWindowComponent.vue ├── fonts │ └── AnonymousPro.ttf ├── loadscript.js ├── main.ts ├── manifest.json ├── scripts │ ├── command.ts │ ├── commands │ │ ├── calculate.ts │ │ ├── date.ts │ │ ├── echo.ts │ │ ├── exit.ts │ │ ├── export.ts │ │ ├── help.ts │ │ ├── search.ts │ │ ├── template.ts │ │ ├── theme.ts │ │ ├── time.ts │ │ └── weather.ts │ ├── parser.ts │ └── searchEngines.ts ├── shims-vue.d.ts ├── styles.sass └── styles │ ├── flamingo.sass │ ├── glass.sass │ └── vue-neu.sass ├── tsconfig.json ├── vue.config.js └── yarn.lock /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "monthly" 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | pnpm-debug.log* 14 | 15 | # Editor directories and files 16 | .idea 17 | .vscode 18 | *.suo 19 | *.ntvs* 20 | *.njsproj 21 | *.sln 22 | *.sw? 23 | 24 | # Termst build files 25 | /extension 26 | Termst.crx 27 | key.pem -------------------------------------------------------------------------------- /GUIDES.md: -------------------------------------------------------------------------------- 1 | # Guides 2 | --- 3 | 4 | ## Themes 5 | Currently styling the terminal is done via sass mixins. Go to the .sass files of any theme in the src/styles directory and add custom css rules into the mixins given below. 6 | 7 | *If you don't know what mixins are, they're just injections into the main css. For example, the TerminalWindowStyling mixin is just injecting those css rules into the main css* 8 | 9 | ## Commands 10 | 11 | ### How to make a command 12 | Firstly go to the src/scripts/commands directory and create a new command. 13 | 14 | Let's create a basic command which will return a Hello World output. 15 | 16 | Take a look at the src/scripts/commands/template.ts file. You want to implement the commands class. 17 | 18 | ```ts 19 | import {Command, CommandResponse} from "@/scripts/command"; 20 | import {CommandInterface} from "@/scripts/parser"; 21 | 22 | export class HelloWorld implements Command { 23 | constructor(public command: CommandInterface) {} 24 | 25 | async execute(): Promise { 26 | return { 27 | command: this.command.command, 28 | returnValue: {type: 'success', furtherDetails: 'The command ran with perfection!'}, 29 | output: 'Hello World!', 30 | fullCommand: this.command.fullCommand 31 | } 32 | } 33 | ``` 34 | 35 | 36 | Now let's go to src/scripts/export.ts and update our command to be in the commands array. 37 | 38 | ```ts 39 | { 40 | name: 'helloworld', 41 | ifMatches: (command) => { 42 | return new HelloWorld(command); 43 | }, 44 | helpCommand: `Returns hello world` 45 | } 46 | ``` 47 | 48 | There you go! Now you just need to run it. 49 | 50 | ### Command class options. 51 | 52 | One thing you need to know about is the information arguments which is basically: 53 | ```ts 54 | { 55 | key: string; 56 | keyDescription: string; 57 | } 58 | ``` 59 | 60 | Pass in descriptions about arguments, options, flags etc. 61 | 62 | For example, we have a search command which will take up the search arguments, search engine option and the new tab flag: 63 | 64 | ```ts 65 | argumentDescription = [ 66 | { 67 | key: "query", 68 | keyDescription: "Search query" 69 | } 70 | ] 71 | 72 | flagDescription = [ 73 | { 74 | key: "-n", 75 | keyDescription: "Open in new tab." 76 | } 77 | ]; 78 | 79 | optionsDescription = [ 80 | 81 | { 82 | key: "--engine", 83 | keyDescription: "Search engine." 84 | 85 | } 86 | ]; 87 | ``` 88 | 89 | This is basically describing what the values or options passed in will be. 90 | 91 | All properties of a command: 92 | ```ts 93 | name!: string; 94 | description!: string; 95 | argumentDescription?: KeyDescription[]; 96 | flagDescription?: KeyDescription[]; 97 | optionsDescription?: KeyDescription[]; 98 | 99 | constructor(command:CommandInterface){ 100 | // 101 | } 102 | 103 | execute!: () => Promise 104 | ``` 105 | 106 | You might be asking what the purpose of all this is? 107 | 108 | Well in the future, I want to get autocomplete into this and all these descriptions and whatnot will be really helpful. 109 | 110 | Any obscurity in the guide please don't hesitate to open an issue. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Termst(Theme toggle-r!) 2 | 3 | New theme toggle-r command! Just run theme (theme) and you can change themes directly from within the Terminal. 4 | Next goal: Add custom widgets!!! 5 | 6 | Thank you all so much for supporting me. Sorry for the lack of inactivity the previous week, just had tons of work 💪. 7 | --- 8 | 9 | ### Make the startpage more than just the first page you load up. 10 | 11 | The most extensible Terminal startpage you will ever find. 12 | 13 | ![vue-glass](./screenshots/vue-glass.png) 14 | ![vue-neu](./screenshots/vue-neu.png) 15 | ![vue-flamingo](./screenshots/vue-flamingo.png) 16 | 17 | This project exists to prove a point that the startpage is more than just a place for bookmarks. 18 | 19 | Make commands to do anything you want, interact with the filesystem or whatever you want. 20 | 21 | ## Customisable to the core. 22 | 23 | Termst is made using the most bleeding edge technologies out there, with Vue.js, Typescript and Sass. 24 | 25 | Termst's default set of components and commands will give you a head start to a clean, minimalist terminal. 26 | 27 | There's always room for add-ons, so you can configure, script, make Termst anyway you want to. 28 | 29 | Termst comes with 3 default themes: 30 | Flamingo, Glass and Vue-neu. 31 | 32 | Styling is simple enough, you can poke around, or you can fully modify any element you want to. 33 | 34 | 35 | 36 | # [Development Guides](./GUIDES.md) 37 | 38 | ## Development Roadmap. 39 | 40 | Termst is starting off small but has high hopes. 41 | 42 | - [X] Get a working build up 43 | - [ ] Exit private alpha 44 | - [X] Fix fonts 45 | - [X] Release extension publicly 46 | - [ ] Add more themes 47 | - [ ] Add more commands 48 | 49 | ### Developer Experience 50 | - [X] Create a basic command template/structure. 51 | - [X] Create a few themes and a kind of toolkit for themes 52 | - [ ] Create a framework for making themes. 53 | 54 | ### User Experience 55 | - [X] Add a few custom themes 56 | - [X] Add a few built in QOL commands 57 | - [X] Implement easier toggling of themes from within the extension 58 | - [ ] Remove dependence on Vue for scripts 59 | - [ ] Add ability to download commands from anywhere. 60 | ### Extension support 61 | - [X] Chromium 62 | - [ ] Firefox 63 | - [ ] Safari 64 | 65 | 66 | It's maybe starting off as a hobby project, but I have high hopes that this thing will be more than just a cool start-page. It will be a place for interaction and infinite productivity. To get things done faster whilst being simpler. 67 | 68 | We're going to set the bar high for what a fully featured startpage can be. 69 | 70 | 71 | 72 | 73 | 74 | ## The idea behind Termst 75 | 76 | ### Another Terminal? Great. 77 | 78 | While I feel the idea of a Terminal Start-page has been exploited to hell and beyond, I think there's room for improvement. 79 | 80 | Most of the startpages based on a Terminal may look good and enticing, but I find they barely offer much functionality. 81 | 82 | ### Commands are hard and complex to learn. 83 | 84 | Agreed. But if you're a hardcore terminal user you might find it natural to get used to. We want to create an experience which you can get used to without feeling alien. 85 | 86 | A GUI might be better option, but I think a Terminal is a good starting point. 87 | 88 | ### Why not make a GUI? 89 | 90 | GUI's offer very little room for creativity that raw commands can. 91 | 92 | However, Termst is a good starting point for it to evolve eventually to an interactive GUI. 93 | 94 | 95 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | # remove previous builds 2 | rm -rf extension 3 | # scripts 4 | yarn install 5 | yarn run build 6 | yarn run theme-compile 7 | cp src/manifest.json dist/ 8 | cp -r src/fonts dist/ 9 | cp src/loadscript.js dist/js 10 | mv dist extension 11 | yarn run pack 12 | echo 'Your extension is ready to be installed in Google Chrome! ✨' -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 2, 3 | "name": "Termst", 4 | "version": "1.0", 5 | "description": "Termst", 6 | "chrome_url_overrides": { 7 | "newtab": "index.html" 8 | } 9 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "terminal-newtab", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "lint": "vue-cli-service lint", 9 | "pack": "crx3 pack extension -o Termst.crx", 10 | "theme-compile": "sass src/styles:dist/css" 11 | }, 12 | "dependencies": { 13 | "core-js": "^3.8.3", 14 | "crx3": "^1.1.3", 15 | "mathjs": "^10.5.3", 16 | "vue": "^3.2.13", 17 | "vue-class-component": "^8.0.0-0" 18 | }, 19 | "devDependencies": { 20 | "@typescript-eslint/eslint-plugin": "^5.4.0", 21 | "@typescript-eslint/parser": "^5.4.0", 22 | "@vue/cli-plugin-babel": "~5.0.0", 23 | "@vue/cli-plugin-eslint": "~5.0.0", 24 | "@vue/cli-plugin-typescript": "~5.0.0", 25 | "@vue/cli-service": "~5.0.0", 26 | "@vue/eslint-config-typescript": "^9.1.0", 27 | "eslint": "^7.32.0", 28 | "eslint-plugin-vue": "^8.0.3", 29 | "sass": "^1.32.7", 30 | "sass-loader": "^12.0.0", 31 | "typescript": "~4.5.5" 32 | }, 33 | "eslintConfig": { 34 | "root": true, 35 | "env": { 36 | "node": true 37 | }, 38 | "extends": [ 39 | "plugin:vue/vue3-essential", 40 | "eslint:recommended", 41 | "@vue/typescript/recommended" 42 | ], 43 | "parserOptions": { 44 | "ecmaVersion": 2020 45 | }, 46 | "rules": {} 47 | }, 48 | "browserslist": [ 49 | "> 1%", 50 | "last 2 versions", 51 | "not dead", 52 | "not ie 11" 53 | ] 54 | } 55 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insomnia-creator/Termst/9fae2e09767ce2d19607c2eae0ea73cdd55086f8/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 10 | 11 | 12 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /screenshots/vue-flamingo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insomnia-creator/Termst/9fae2e09767ce2d19607c2eae0ea73cdd55086f8/screenshots/vue-flamingo.png -------------------------------------------------------------------------------- /screenshots/vue-glass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insomnia-creator/Termst/9fae2e09767ce2d19607c2eae0ea73cdd55086f8/screenshots/vue-glass.png -------------------------------------------------------------------------------- /screenshots/vue-neu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insomnia-creator/Termst/9fae2e09767ce2d19607c2eae0ea73cdd55086f8/screenshots/vue-neu.png -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 15 | 16 | 20 | -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insomnia-creator/Termst/9fae2e09767ce2d19607c2eae0ea73cdd55086f8/src/assets/logo.png -------------------------------------------------------------------------------- /src/components/TerminalBlinkingCaret.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 10 | 11 | -------------------------------------------------------------------------------- /src/components/TerminalComponent.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 18 | 19 | -------------------------------------------------------------------------------- /src/components/TerminalInsertField.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 37 | 38 | -------------------------------------------------------------------------------- /src/components/TerminalOutput.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 29 | 30 | -------------------------------------------------------------------------------- /src/components/TerminalPrompt.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 19 | 20 | -------------------------------------------------------------------------------- /src/components/TerminalWindowComponent.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 60 | 61 | -------------------------------------------------------------------------------- /src/fonts/AnonymousPro.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insomnia-creator/Termst/9fae2e09767ce2d19607c2eae0ea73cdd55086f8/src/fonts/AnonymousPro.ttf -------------------------------------------------------------------------------- /src/loadscript.js: -------------------------------------------------------------------------------- 1 | 2 | var theme = localStorage.getItem('theme') ? localStorage.getItem('theme') : 'glass'; 3 | var linkElement = Array.from(document.getElementsByTagName('link')); 4 | var link = linkElement[1]; 5 | link.setAttribute('href', `css/${theme}.css`); -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import App from './App.vue' 3 | 4 | createApp(App).mount('#app') 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 2, 3 | "name": "Termst", 4 | "version": "1", 5 | "chrome_url_overrides": { 6 | "newtab": "index.html" 7 | }, 8 | "web_accessible_resources": ["/fonts/*.ttf"] 9 | } -------------------------------------------------------------------------------- /src/scripts/command.ts: -------------------------------------------------------------------------------- 1 | import {CommandInterface} from "@/scripts/parser"; 2 | export interface CommandResponse { 3 | output: string; 4 | returnValue: { 5 | type: 'error' | 'success'; 6 | furtherDetails?: string; 7 | }; 8 | showAsRawHTML?: boolean; 9 | command: string; 10 | additionalStyling?: string; 11 | additionalClass?: string; 12 | fullCommand: string; 13 | } 14 | 15 | export interface KeyDescription { 16 | key: string; 17 | keyDescription: string 18 | } 19 | export class Command { 20 | name!: string; 21 | description!: string; 22 | argumentDescription?: KeyDescription[]; 23 | flagDescription?: KeyDescription[]; 24 | optionsDescription?: KeyDescription[]; 25 | 26 | constructor(command:CommandInterface){ 27 | // 28 | } 29 | 30 | execute!: () => Promise 31 | } 32 | 33 | 34 | export interface Commands { 35 | name: string; 36 | ifMatches: (command: CommandInterface) => Command; 37 | helpCommand: string; 38 | } 39 | 40 | -------------------------------------------------------------------------------- /src/scripts/commands/calculate.ts: -------------------------------------------------------------------------------- 1 | import {Command, CommandResponse} from "@/scripts/command"; 2 | import {CommandInterface} from "@/scripts/parser"; 3 | import {evaluate} from "mathjs"; 4 | 5 | 6 | export class Calculate implements Command { 7 | public argumentDescription: { key: string; keyDescription: string }[] = [ 8 | { 9 | keyDescription: "Calculation equation", 10 | key: "equation", 11 | } 12 | ] 13 | name = "calculate"; 14 | description = "Calculate a mathematical equation"; 15 | 16 | calculate(equation: string){ 17 | let res; 18 | try { 19 | res = evaluate(equation); 20 | } catch { 21 | res = 'Incorrect equation or an error occurred'; 22 | } 23 | return res; 24 | } 25 | 26 | 27 | constructor(public command: CommandInterface) { 28 | } 29 | 30 | async execute(): Promise { 31 | let output: string; 32 | if(this.command.argumentsAsString){ 33 | output = this.calculate(this.command.argumentsAsString); 34 | } else { 35 | output = "No equation provided"; 36 | } 37 | 38 | return { 39 | command: this.command.command, 40 | returnValue: {type: 'success', furtherDetails: 'The command ran with perfection!'}, 41 | output: output, 42 | fullCommand: this.command.fullCommand 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /src/scripts/commands/date.ts: -------------------------------------------------------------------------------- 1 | import { Command, CommandResponse } from "../command"; 2 | import { CommandInterface } from "../parser"; 3 | 4 | export class DateCommand implements Command { 5 | constructor(public command:CommandInterface){} 6 | 7 | name = "date"; 8 | description = 'Shows the date'; 9 | 10 | getday(){ 11 | //format date and display it as a string 12 | const date = new Date(); 13 | const year = date.getFullYear(); 14 | const month = date.getMonth(); 15 | const day = date.getDate(); 16 | const daydate = date.getDay() 17 | let dayName= '' 18 | 19 | switch (daydate) { 20 | case 0: 21 | dayName = 'Sunday'; 22 | break; 23 | case 1: 24 | dayName = 'Monday'; 25 | break; 26 | case 2: 27 | dayName = 'Tuesday'; 28 | break; 29 | case 3: 30 | dayName = 'Wednesday'; 31 | break; 32 | case 4: 33 | dayName = 'Thursday'; 34 | break; 35 | case 5: 36 | dayName = 'Friday'; 37 | break; 38 | case 6: 39 | dayName = 'Saturday'; 40 | break; 41 | default: 42 | break; 43 | } 44 | 45 | 46 | return `${dayName.toString()} ${month}/${day}/${year}`; 47 | } 48 | 49 | async execute(): Promise { 50 | return { 51 | fullCommand: this.command.fullCommand, 52 | command: this.command.command, 53 | returnValue: { 54 | type: "success", 55 | furtherDetails: 'date command' 56 | }, 57 | output: this.getday() 58 | 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /src/scripts/commands/echo.ts: -------------------------------------------------------------------------------- 1 | import {Command, CommandResponse} from "@/scripts/command"; 2 | import {CommandInterface} from "@/scripts/parser"; 3 | 4 | export class Echo implements Command { 5 | argumentDescription: { key: string; keyDescription: string }[] = [ 6 | { 7 | keyDescription: "The message to echo", 8 | key: "message", 9 | } 10 | ] 11 | description = 'Echo a message' 12 | name = 'echo' 13 | 14 | constructor(public command:CommandInterface) {} 15 | 16 | async execute(): Promise { 17 | return { 18 | command: this.command.command, 19 | returnValue: {type: 'success', furtherDetails: 'The command ran with perfection!'}, 20 | output: (this.command.argumentsAsString as string), 21 | fullCommand: this.command.fullCommand 22 | } 23 | } 24 | 25 | 26 | } -------------------------------------------------------------------------------- /src/scripts/commands/exit.ts: -------------------------------------------------------------------------------- 1 | import {Command, CommandResponse} from "@/scripts/command"; 2 | import {CommandInterface} from "@/scripts/parser"; 3 | 4 | export class Exit implements Command { 5 | public description = 'Exit the terminal.' 6 | public name = 'exit' 7 | 8 | 9 | constructor(public command: CommandInterface) { 10 | } 11 | 12 | async execute(): Promise { 13 | window.close(); 14 | return { 15 | command: this.command.command, 16 | returnValue: {type: 'success', furtherDetails: 'The command ran with perfection!'}, 17 | output: 'Exited.', 18 | fullCommand: this.command.fullCommand 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /src/scripts/commands/export.ts: -------------------------------------------------------------------------------- 1 | import {Command, CommandResponse, Commands} from "@/scripts/command"; 2 | import {Echo} from "@/scripts/commands/echo"; 3 | import { CommandInterface } from "../parser"; 4 | import {Weather} from "@/scripts/commands/weather"; 5 | import {Search} from "@/scripts/commands/search"; 6 | import {Help} from "@/scripts/commands/help"; 7 | import {Exit} from "@/scripts/commands/exit"; 8 | import {Calculate} from "@/scripts/commands/calculate"; 9 | import { Time } from "@/scripts/commands/time"; 10 | import { DateCommand } from "@/scripts/commands/date"; 11 | import { Theme } from "./theme"; 12 | 13 | export const commands: Commands[] = [ 14 | { 15 | name: 'echo', 16 | ifMatches: (command: CommandInterface) => { 17 | return new Echo(command); 18 | }, 19 | helpCommand: `Usage: 20 | echo ` 21 | }, 22 | { 23 | name: 'weather', 24 | ifMatches: (command) => { 25 | return new Weather(command); 26 | }, 27 | helpCommand: `Usage: 28 | weather ` 29 | }, 30 | { 31 | name: "search", 32 | ifMatches: (command => { 33 | return new Search(command); 34 | }), 35 | helpCommand: `search 36 | 37 | Options: 38 | --setengine= : Sets the default Search engine 39 | --engine= : Sets the search engine to use for this search 40 | 41 | Flags: 42 | 43 | -n : Open the result in a new tab` 44 | }, 45 | { 46 | name: "help", 47 | ifMatches: (command) => { 48 | return new Help(command) 49 | }, 50 | helpCommand: `Help???` 51 | }, 52 | { 53 | name: "exit", 54 | ifMatches: (command) => { 55 | return new Exit(command) 56 | }, 57 | helpCommand: `Exits the terminal.` 58 | }, 59 | { 60 | name: 'calculate', 61 | ifMatches: (command) => { 62 | return new Calculate(command); 63 | }, 64 | helpCommand: `Calculate an equation.` 65 | }, 66 | { 67 | name: "time", 68 | ifMatches: (command) => { 69 | return new Time(command); 70 | }, 71 | helpCommand: `Shows the time` 72 | }, 73 | { 74 | name: "date", 75 | ifMatches: (command) => { 76 | return new DateCommand(command) ; 77 | }, 78 | helpCommand: `Shows the date.` 79 | }, 80 | { 81 | name: "theme", 82 | ifMatches: (command) => { 83 | return new Theme(command); 84 | 85 | }, 86 | helpCommand: ` 87 | Change the theme: 88 | 89 | Usage: 90 | theme 91 | Available themes: 92 | vue-neu, flamingo and glass. 93 | ` 94 | } 95 | ]; 96 | 97 | 98 | 99 | export default (cmnd: CommandInterface): Promise => { 100 | 101 | return new Promise((resolve, reject) => { 102 | commands.forEach(command => { 103 | if (command.name === cmnd.command) { 104 | resolve(command.ifMatches(cmnd)); 105 | } 106 | }); 107 | 108 | const rejection:CommandResponse = { 109 | returnValue: { 110 | type: 'error', 111 | furtherDetails: 'Please re-check your command.' 112 | }, 113 | command: cmnd.command, 114 | output: `Command does not exist.`, 115 | additionalClass: 'error', 116 | fullCommand: cmnd.fullCommand, 117 | } 118 | reject(rejection); 119 | 120 | }) 121 | } 122 | 123 | 124 | export const getHelpMessage = (command: string): string => { 125 | let message = ''; 126 | 127 | 128 | for(let i = 0; i <= commands.length; i++){ 129 | if(commands[i].name.toLowerCase() === command){ 130 | message = commands[i].helpCommand; 131 | break; 132 | } else { 133 | message = `Command does not exist.`; 134 | } 135 | } 136 | 137 | return message; 138 | } 139 | -------------------------------------------------------------------------------- /src/scripts/commands/help.ts: -------------------------------------------------------------------------------- 1 | import {Command, CommandResponse} from "@/scripts/command"; 2 | import {CommandInterface} from "@/scripts/parser"; 3 | import {getHelpMessage} from "@/scripts/commands/export"; 4 | import {commands} from "@/scripts/commands/export"; 5 | 6 | export class Help implements Command { 7 | name = 'help'; 8 | description = "Help Command" 9 | argumentDescription = [ 10 | { 11 | key: "command", 12 | keyDescription: "Command to get help for." 13 | } 14 | ] 15 | 16 | 17 | constructor(public command:CommandInterface) {} 18 | 19 | 20 | getCommands(){ 21 | console.log(commands); 22 | const commandNames: Array = []; 23 | for(let i = 0; i < commands.length; i++) { 24 | commandNames.push(commands[i].name); 25 | } 26 | return commandNames; 27 | } 28 | 29 | 30 | async execute(): Promise { 31 | 32 | if(this.command.arguments?.length !== 0){ 33 | const message = getHelpMessage(this.command.argumentsAsString as string); 34 | return{ 35 | command: this.command.command, 36 | fullCommand: this.command.fullCommand, 37 | returnValue: { 38 | type: 'success', 39 | furtherDetails: `Help command for ${this.command}` 40 | }, 41 | output: message, 42 | showAsRawHTML: false 43 | } 44 | } else { 45 | let commandsString = `List of commands: \n`; 46 | const commandsArray = this.getCommands(); 47 | console.log(commandsArray); 48 | commandsArray.forEach(cmd => { 49 | commandsString += `${cmd}\n`; 50 | }); 51 | 52 | return{ 53 | command: this.command.command, 54 | fullCommand: this.command.fullCommand, 55 | returnValue: { 56 | type: 'success', 57 | furtherDetails: `List commands` 58 | }, 59 | showAsRawHTML: true, 60 | output: commandsString, 61 | } 62 | } 63 | 64 | } 65 | 66 | } -------------------------------------------------------------------------------- /src/scripts/commands/search.ts: -------------------------------------------------------------------------------- 1 | import {Command, CommandResponse} from "@/scripts/command"; 2 | import {CommandInterface} from "@/scripts/parser"; 3 | import searchEngines from "@/scripts/searchEngines"; 4 | 5 | export class Search implements Command { 6 | 7 | 8 | constructor(public command: CommandInterface) { 9 | } 10 | 11 | name = 'search' 12 | description = 'Use a search engine to search a query.' 13 | 14 | argumentDescription = [ 15 | { 16 | key: "query", 17 | keyDescription: "Search query" 18 | } 19 | ] 20 | 21 | flagDescription = [ 22 | { 23 | key: "-n", 24 | keyDescription: "Open the query in a new tab." 25 | } 26 | ]; 27 | 28 | optionsDescription = [ 29 | { 30 | key: '--setengine', 31 | keyDescription: "Sets the default search engine" 32 | }, 33 | { 34 | key: "--engine", 35 | keyDescription: "Sets the engine to use for this specific search" 36 | } 37 | ]; 38 | 39 | 40 | getDefaultSearchEngine(){ 41 | let defaultEng = localStorage.getItem('defaultSearchEngine') 42 | 43 | if(!defaultEng){ 44 | localStorage.setItem('defaultSearchEngine', 'google'); 45 | defaultEng = 'google'; 46 | } 47 | 48 | return defaultEng; 49 | 50 | } 51 | 52 | 53 | 54 | execute(): Promise { 55 | return new Promise(((resolve, reject) => { 56 | let engine = searchEngines[this.getDefaultSearchEngine()]; 57 | let openInNewTab = false; 58 | 59 | if(!this.command.argumentsAsString){ 60 | return this.command.options?.forEach(opt => { 61 | if(opt.key === 'setengine'){ 62 | const defaultEngine = searchEngines[opt.value]; 63 | if(!defaultEngine){ 64 | return reject({ 65 | command: this.command.command, 66 | output: `Engine ${opt.value} does not exist. If you wish to add your own search engine, take a look at: `, 67 | returnValue: { 68 | type: "error", 69 | furtherDetails: "Incorrect Engine Type Provided. " 70 | } 71 | }); 72 | } else { 73 | localStorage.setItem('defaultSearchEngine', opt.value) 74 | return resolve({ 75 | command: this.command.command, 76 | output: 'Set your default search engine as ' + opt.value, 77 | returnValue: { 78 | type: "success" 79 | }, 80 | fullCommand: this.command.fullCommand 81 | }); 82 | } 83 | } 84 | }) 85 | } 86 | 87 | 88 | this.command.flags?.forEach(fl => { 89 | if(fl === 'n'){ 90 | openInNewTab = true; 91 | } 92 | }); 93 | 94 | this.command.options?.forEach(opt => { 95 | if(opt.key === 'engine'){ 96 | engine = searchEngines[opt.value] ?? searchEngines[this.getDefaultSearchEngine()]; 97 | } 98 | }); 99 | 100 | if(openInNewTab){ 101 | window.open(`${engine.url}${encodeURIComponent(this.command.argumentsAsString as string)}`); 102 | return resolve({ 103 | command: this.command.command, 104 | output: `Searching for ${this.command.argumentsAsString} on a new tab`, 105 | returnValue: { 106 | type: "success" 107 | }, 108 | fullCommand: this.command.fullCommand 109 | }); 110 | } else { 111 | window.location.href = `${engine.url}${encodeURIComponent(this.command.argumentsAsString as string)}`; 112 | return resolve({ 113 | command: this.command.command, 114 | output: `Searching for ${this.command.argumentsAsString}`, 115 | returnValue: { 116 | type: "success" 117 | }, 118 | fullCommand: this.command.fullCommand 119 | }); 120 | } 121 | })) 122 | 123 | 124 | } 125 | } 126 | 127 | -------------------------------------------------------------------------------- /src/scripts/commands/template.ts: -------------------------------------------------------------------------------- 1 | import {Command, CommandResponse} from "@/scripts/command"; 2 | import {CommandInterface} from "@/scripts/parser"; 3 | 4 | export class Template implements Command { 5 | public argumentDescription: { key: string; keyDescription: string }[] = [ 6 | { 7 | keyDescription: "Template description", 8 | key: "key", 9 | } 10 | ] 11 | public flagDescription = [ 12 | { 13 | keyDescription: "Flags", 14 | key: "-f" 15 | } 16 | ]; 17 | public optionsDescription = [ 18 | { 19 | keyDescription: "An option", 20 | key: "--option" 21 | } 22 | ]; 23 | public description = 'Template.' 24 | public name = 'template' 25 | 26 | 27 | constructor(public command: CommandInterface) { 28 | } 29 | 30 | async execute(): Promise { 31 | return { 32 | command: this.command.command, 33 | returnValue: {type: 'success', furtherDetails: 'The command ran with perfection!'}, 34 | output: 'Template', 35 | fullCommand: this.command.fullCommand 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /src/scripts/commands/theme.ts: -------------------------------------------------------------------------------- 1 | import { Command, CommandResponse } from "../command"; 2 | import { CommandInterface } from "../parser"; 3 | 4 | export class Theme implements Command { 5 | constructor(public command:CommandInterface){} 6 | 7 | name = "theme"; 8 | description = 'Change the theme of the terminal'; 9 | 10 | argumentDescription = [ 11 | { 12 | key: "theme", 13 | keyDescription: "The theme to change to" 14 | } 15 | ]; 16 | 17 | async changeTheme(theme: string) : Promise { 18 | const linkElement = Array.from(document.getElementsByTagName('link')); 19 | const link = linkElement[1]; 20 | link.setAttribute('href', `css/${theme}.css`); 21 | localStorage.setItem('theme', theme); 22 | return { 23 | command: this.command.command, 24 | returnValue: {type: 'success', furtherDetails: 'Theme changed'}, 25 | output: 'Theme changed to ' + theme, 26 | fullCommand: this.command.fullCommand 27 | } 28 | } 29 | 30 | async execute(): Promise { 31 | if(this.command.arguments?.length === 0 || !this.command.arguments){ 32 | return { 33 | command: this.command.command, 34 | returnValue: {type: 'error', furtherDetails: 'No theme specified'}, 35 | output: 'Available themes: \n vue-neu, flamingo and glass', 36 | fullCommand: this.command.fullCommand 37 | } 38 | } else { 39 | const argument = this.command.arguments[0].toLowerCase(); 40 | 41 | switch (argument) { 42 | case 'vue-neu': 43 | return this.changeTheme('vue-neu'); 44 | case 'flamingo': 45 | return this.changeTheme('flamingo'); 46 | case 'glass': 47 | return this.changeTheme('glass'); 48 | default: 49 | return { 50 | command: this.command.command, 51 | returnValue: {type: 'error', furtherDetails: 'Invalid theme'}, 52 | output: 'Available themes: \n vue-neu, flamingo and glass', 53 | fullCommand: this.command.fullCommand 54 | } 55 | } 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /src/scripts/commands/time.ts: -------------------------------------------------------------------------------- 1 | import { Command, CommandResponse } from "../command"; 2 | import { CommandInterface } from "../parser"; 3 | 4 | export class Time implements Command { 5 | constructor(public command:CommandInterface) {} 6 | 7 | name = "time"; 8 | description = 'Shows the time'; 9 | 10 | getTime(){ 11 | //format date and display it as a string 12 | const date = new Date(); 13 | const hours = date.getHours(); 14 | const minutes = date.getMinutes(); 15 | const seconds = date.getSeconds(); 16 | return `${hours}:${minutes}:${seconds}`; 17 | } 18 | 19 | async execute(): Promise 20 | { 21 | return { 22 | output: this.getTime(), 23 | fullCommand: this.command.fullCommand, 24 | returnValue: { 25 | type: "success", 26 | furtherDetails: 'time command' 27 | }, 28 | command: this.command.command 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /src/scripts/commands/weather.ts: -------------------------------------------------------------------------------- 1 | import {Command, CommandResponse} from "@/scripts/command"; 2 | import {CommandInterface} from "@/scripts/parser"; 3 | 4 | export class Weather implements Command { 5 | argumentDescription = [ 6 | { 7 | key: "city", 8 | keyDescription: "City to look for the report, you don't need to pass in this parameter necessarily." 9 | } 10 | ]; 11 | description = 'Fetch your weather report, from wttr.in of course.' 12 | name = 'weather' 13 | 14 | 15 | 16 | constructor( public command:CommandInterface) {} 17 | 18 | async execute(): Promise { 19 | 20 | return new Promise( (resolve, reject) => { 21 | const url = this.command.argumentsAsString ? `https://wttr.in/${this.command.argumentsAsString}?0n` : `https://wttr.in?0n`; 22 | fetch(url).then(res => res.text().then(body => { 23 | resolve({ 24 | command: this.command.command, 25 | returnValue: {type: 'success', furtherDetails: "Got your weather!"}, 26 | output: body, 27 | showAsRawHTML: true, 28 | additionalStyling: `margin: 0px !important;`, 29 | fullCommand: this.command.fullCommand 30 | }) 31 | })).catch(e => { 32 | reject({ 33 | command: this.command.command, 34 | returnValue: {type: 'error', furtherDetails: "Something went wrong!"}, 35 | output: e.message 36 | }); 37 | }); 38 | }) 39 | } 40 | 41 | 42 | } -------------------------------------------------------------------------------- /src/scripts/parser.ts: -------------------------------------------------------------------------------- 1 | export interface CommandInterface { 2 | command: string; 3 | arguments?: string[]; 4 | flags?: string[]; 5 | options?: { 6 | key: string, 7 | value: string 8 | }[]; 9 | fullCommand: string; 10 | argumentsAsString?: string; 11 | } 12 | 13 | 14 | export const parse = (input: string): CommandInterface => { 15 | 16 | const inputArray = input.split(' '); 17 | const command = inputArray[0]; 18 | const args = inputArray.slice(1); 19 | const options: { key: string, value: string }[] = []; 20 | const flags: string[] = []; 21 | 22 | 23 | //we run this map because to intercept -- before we intercept - 24 | args.map((arg, index) => { 25 | if (arg.startsWith('--')) { 26 | //example value --name=value 27 | const opts = arg.split('='); 28 | args.splice(index, 1); 29 | options.push({ 30 | key: opts[0].slice(2), 31 | value: opts[1] 32 | }) 33 | } 34 | }) 35 | 36 | args.map((arg, index) => { 37 | if (arg.startsWith('-')) { 38 | const flag = arg.slice(1).split(''); 39 | args.splice(index, 1); 40 | flags.push(...flag) 41 | } 42 | }) 43 | 44 | return { 45 | command: command.toLowerCase(), 46 | arguments: args, 47 | flags: flags, 48 | options: options, 49 | argumentsAsString: args.join(' '), 50 | fullCommand: input 51 | } 52 | 53 | 54 | 55 | }; 56 | -------------------------------------------------------------------------------- /src/scripts/searchEngines.ts: -------------------------------------------------------------------------------- 1 | const searchEngines: {[index: string]:{url: string}} = { 2 | google: { 3 | url: 'https://www.google.com/search?q=' 4 | }, 5 | duckduckgo: { 6 | url: 'https://duckduckgo.com/?q=' 7 | }, 8 | bing: { 9 | url: 'https://www.bing.com/search?q=' 10 | }, 11 | yahoo: { 12 | url: 'https://search.yahoo.com/search?p=' 13 | }, 14 | baidu: { 15 | url: 'https://www.baidu.com/s?wd=' 16 | }, 17 | yandex: { 18 | url: 'https://yandex.ru/search/?text=' 19 | }, 20 | qwant: { 21 | url: 'https://www.qwant.com/?q=' 22 | }, 23 | startpage: { 24 | url: 'https://startpage.com/do/search?query=' 25 | }, 26 | kagi: { 27 | url: 'https://kagi.com/search?q=' 28 | }, 29 | amazon: { 30 | url: 'https://www.amazon.com/s/ref=nb_sb_noss?url=search-alias%3Daps&field-keywords=' 31 | }, 32 | 33 | } 34 | 35 | export default searchEngines; -------------------------------------------------------------------------------- /src/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | declare module '*.vue' { 3 | import type { DefineComponent } from 'vue' 4 | const component: DefineComponent<{}, {}, any> 5 | export default component 6 | } 7 | -------------------------------------------------------------------------------- /src/styles.sass: -------------------------------------------------------------------------------- 1 | // IMPORTANT: uncomment this out ONLY if you are going to SERVE the file. 2 | // DO NOT BUILD WITH THIS FILE INCLUDED!!!!!!!! 3 | 4 | //@import "@/styles/vue-neu" 5 | -------------------------------------------------------------------------------- /src/styles/flamingo.sass: -------------------------------------------------------------------------------- 1 | @font-face 2 | font-family: Anonymous 3 | src: url("../fonts/AnonymousPro.ttf") format("truetype") 4 | 5 | 6 | 7 | $terminal-win-height: 70vh 8 | $terminal-win-width: 70vw 9 | $padding: 25px 10 | $foreground: #67595E 11 | $background: #E8B4B8 12 | $font-size: 18px 13 | $font-size-output: 24px 14 | $caret-colour: #A49393 15 | $colour-as-success: inherit 16 | $colour-as-error: #de4e4e 17 | $colour-as-success: #95ca63 18 | $font-family: "Anonymous", monospace 19 | 20 | 21 | @mixin TerminalWindowStyling 22 | border-radius: 15px 23 | background: #EED6D3 24 | 25 | 26 | @mixin TerminalComponentStyling 27 | 28 | @mixin TerminalBlinkingCaretStyling 29 | 30 | @mixin TerminalPromptStyling 31 | 32 | @mixin TerminalInsertFieldStyling 33 | 34 | @mixin TerminalOutputStyling 35 | 36 | @mixin TerminalPromptStyling 37 | color: #A49393 38 | 39 | 40 | #app 41 | background: $background 42 | color: $foreground 43 | height: 100vh 44 | width: 100vw 45 | * 46 | margin: 0 !important 47 | font-family: $font-family 48 | 49 | 50 | @keyframes blink 51 | 0% 52 | opacity: 1 53 | 50% 54 | opacity: 0 55 | 100% 56 | opacity: 1 57 | 58 | .blinking-caret 59 | height: $font-size 60 | width: 10px 61 | background: $caret-colour 62 | animation: blink 1s infinite 63 | @include TerminalBlinkingCaretStyling() 64 | 65 | body 66 | display: grid 67 | place-items: center 68 | height: 100vh 69 | width: 100vw 70 | @include TerminalComponentStyling() 71 | 72 | .terminal-input 73 | caret: transparent !important 74 | text-shadow: 0px 0px 0px $foreground 75 | color: rgba(0,0,0,0) 76 | overflow-x: scroll 77 | font-weight: bold 78 | @include TerminalInsertFieldStyling() 79 | &:focus 80 | outline: none 81 | 82 | #base 83 | @include TerminalOutputStyling() 84 | 85 | p 86 | font-size: $font-size-output 87 | white-space: pre 88 | overflow-x: auto 89 | 90 | .terminal-command-group 91 | display: flex 92 | width: auto 93 | align-items: center 94 | font-size: $font-size 95 | margin-bottom: 5px 96 | 97 | 98 | .error 99 | color: $colour-as-error 100 | .success 101 | color: $colour-as-success 102 | 103 | .prompt 104 | color: #ff9f34 105 | font-weight: bold 106 | white-space: pre 107 | @include TerminalPromptStyling() 108 | 109 | .terminal 110 | height: $terminal-win-height 111 | padding: $padding 112 | width: $terminal-win-width 113 | word-wrap: break-word 114 | overflow: scroll 115 | @include TerminalWindowStyling() 116 | 117 | 118 | .terminal-input-group 119 | display: flex 120 | width: auto 121 | align-items: center 122 | font-size: $font-size 123 | 124 | 125 | -------------------------------------------------------------------------------- /src/styles/glass.sass: -------------------------------------------------------------------------------- 1 | @font-face 2 | font-family: Anonymous 3 | src: url("../fonts/AnonymousPro.ttf") format("truetype") 4 | 5 | $terminal-win-height: 70vh 6 | $terminal-win-width: 70vw 7 | $padding: 25px 8 | $foreground: #fff 9 | $background: #000 10 | $font-size: 18px 11 | $font-size-output: 24px 12 | $caret-colour: #fff 13 | $colour-as-success: #4bec4b 14 | $colour-as-error: #e12828 15 | $font-family: "Anonymous", monospace 16 | 17 | @mixin TerminalWindowStyling 18 | backdrop-filter: blur(16px) saturate(180%) 19 | -webkit-backdrop-filter: blur(16px) saturate(180%) 20 | background-color: rgba(17, 25, 37, 0.75) 21 | border-radius: 12px 22 | 23 | @mixin TerminalComponentStyling 24 | background-image: radial-gradient(at 50% 27%, #ff9f34 0, transparent 59%), radial-gradient(at 66% 59%, #61ffd8 0, transparent 59%), radial-gradient(at 32% 58%, #9d00ff 0, transparent 59%) 25 | 26 | @mixin TerminalBlinkingCaretStyling 27 | box-shadow: 10px 10px 60px #ffffff, -10px -10px 60px #ffffff 28 | 29 | @mixin TerminalPromptStyling 30 | text-shadow: 10px 10px 60px #ff9f34 31 | 32 | @mixin TerminalInsertFieldStyling 33 | 34 | @mixin TerminalOutputStyling 35 | 36 | @mixin TerminalPromptStyling 37 | 38 | #app 39 | background: $background 40 | color: $foreground 41 | height: 100vh 42 | width: 100vw 43 | * 44 | margin: 0 !important 45 | font-family: $font-family 46 | 47 | @keyframes blink 48 | 0% 49 | opacity: 1 50 | 50% 51 | opacity: 0 52 | 100% 53 | opacity: 1 54 | 55 | .blinking-caret 56 | height: $font-size 57 | width: 10px 58 | background: $caret-colour 59 | animation: blink 1s infinite 60 | @include TerminalBlinkingCaretStyling() 61 | 62 | body 63 | display: grid 64 | place-items: center 65 | height: 100vh 66 | width: 100vw 67 | @include TerminalComponentStyling() 68 | 69 | .terminal-input 70 | caret: transparent !important 71 | text-shadow: 0px 0px 0px $foreground 72 | color: rgba(0,0,0,0) 73 | overflow-x: scroll 74 | font-weight: bold 75 | @include TerminalInsertFieldStyling() 76 | &:focus 77 | outline: none 78 | 79 | #base 80 | @include TerminalOutputStyling() 81 | 82 | p 83 | font-size: $font-size-output 84 | white-space: pre 85 | overflow-x: auto 86 | 87 | .terminal-command-group 88 | display: flex 89 | width: auto 90 | align-items: center 91 | font-size: $font-size 92 | margin-bottom: 5px 93 | 94 | 95 | .error 96 | color: $colour-as-error 97 | .success 98 | color: $colour-as-success 99 | 100 | .prompt 101 | color: #ff9f34 102 | font-weight: bold 103 | white-space: pre 104 | @include TerminalPromptStyling() 105 | 106 | .terminal 107 | height: $terminal-win-height 108 | padding: $padding 109 | width: $terminal-win-width 110 | word-wrap: break-word 111 | overflow: scroll 112 | @include TerminalWindowStyling() 113 | 114 | .terminal-input-group 115 | display: flex 116 | width: auto 117 | align-items: center 118 | font-size: $font-size 119 | 120 | 121 | 122 | 123 | -------------------------------------------------------------------------------- /src/styles/vue-neu.sass: -------------------------------------------------------------------------------- 1 | @font-face 2 | font-family: Anonymous 3 | src: url("../fonts/AnonymousPro.ttf") format("truetype") 4 | 5 | $terminal-win-height: 70vh 6 | $terminal-win-width: 70vw 7 | $padding: 10px 8 | $foreground: #000 9 | $background: #e0e0ee 10 | $font-size: 18px 11 | $font-size-output: 24px 12 | $caret-colour: #000 13 | $colour-as-error: #f63737 14 | $colour-as-success: #3ef644 15 | $font-family: "Anonymous", monospace 16 | 17 | @mixin TerminalWindowCmponentStyling 18 | box-shadow: 0 4px 15px rgb(0 0 0 / 10%), -4px -4px 15px #ffffff 19 | border: rgba(247, 254, 255, 0.85) solid 10px 20 | border-radius: 15px 21 | 22 | @mixin TerminalComponentStyling 23 | 24 | @mixin TerminalWindowStyling 25 | box-shadow: 0 4px 15px rgb(0 0 0 / 10%), -4px -4px 15px #ffffff 26 | border: rgba(247, 254, 255, 0.85) solid 10px 27 | border-radius: 15px 28 | 29 | @mixin TerminalComponentStyling 30 | 31 | @mixin TerminalBlinkingCaretStyling 32 | 33 | @mixin TerminalPromptStyling 34 | 35 | @mixin TerminalInsertFieldStyling 36 | 37 | @mixin TerminalOutputStyling 38 | 39 | @mixin TerminalPromptStyling 40 | 41 | 42 | 43 | #app 44 | background: $background 45 | color: $foreground 46 | height: 100vh 47 | width: 100vw 48 | * 49 | margin: 0 !important 50 | font-family: $font-family 51 | 52 | @keyframes blink 53 | 0% 54 | opacity: 1 55 | 50% 56 | opacity: 0 57 | 100% 58 | opacity: 1 59 | 60 | .blinking-caret 61 | height: $font-size 62 | width: 10px 63 | background: $caret-colour 64 | animation: blink 1s infinite 65 | @include TerminalBlinkingCaretStyling() 66 | 67 | body 68 | display: grid 69 | place-items: center 70 | height: 100vh 71 | width: 100vw 72 | @include TerminalComponentStyling() 73 | 74 | .terminal-input 75 | caret: transparent !important 76 | text-shadow: 0px 0px 0px $foreground 77 | color: rgba(0,0,0,0) 78 | overflow-x: scroll 79 | font-weight: bold 80 | @include TerminalInsertFieldStyling() 81 | &:focus 82 | outline: none 83 | 84 | #base 85 | @include TerminalOutputStyling() 86 | 87 | p 88 | font-size: $font-size-output 89 | white-space: pre 90 | overflow-x: auto 91 | 92 | .terminal-command-group 93 | display: flex 94 | width: auto 95 | align-items: center 96 | font-size: $font-size 97 | margin-bottom: 5px 98 | 99 | 100 | .error 101 | color: $colour-as-error 102 | .success 103 | color: $colour-as-success 104 | 105 | .prompt 106 | color: #ff9f34 107 | font-weight: bold 108 | white-space: pre 109 | @include TerminalPromptStyling() 110 | 111 | .terminal 112 | height: $terminal-win-height 113 | padding: $padding 114 | width: $terminal-win-width 115 | word-wrap: break-word 116 | overflow: scroll 117 | @include TerminalWindowStyling() 118 | 119 | .terminal-input-group 120 | display: flex 121 | width: auto 122 | align-items: center 123 | font-size: $font-size 124 | 125 | 126 | 127 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "strict": true, 6 | "jsx": "preserve", 7 | "moduleResolution": "node", 8 | "experimentalDecorators": true, 9 | "skipLibCheck": true, 10 | "esModuleInterop": true, 11 | "allowSyntheticDefaultImports": true, 12 | "forceConsistentCasingInFileNames": true, 13 | "useDefineForClassFields": true, 14 | "sourceMap": true, 15 | "baseUrl": ".", 16 | "types": [ 17 | "webpack-env" 18 | ], 19 | "paths": { 20 | "@/*": [ 21 | "src/*" 22 | ] 23 | }, 24 | "lib": [ 25 | "esnext", 26 | "dom", 27 | "dom.iterable", 28 | "scripthost" 29 | ] 30 | }, 31 | "include": [ 32 | "src/**/*.ts", 33 | "src/**/*.tsx", 34 | "src/**/*.vue", 35 | "tests/**/*.ts", 36 | "tests/**/*.tsx" 37 | ], 38 | "exclude": [ 39 | "node_modules" 40 | ] 41 | } 42 | -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | const { defineConfig } = require('@vue/cli-service') 2 | module.exports = defineConfig({ 3 | transpileDependencies: true, 4 | filenameHashing : false 5 | }) 6 | --------------------------------------------------------------------------------