@ -0,0 +1,9 @@
|
|||||||
|
module.exports = {
|
||||||
|
plugins: ['prettier'],
|
||||||
|
extends: ['next/core-web-vitals'],
|
||||||
|
rules: {
|
||||||
|
'no-console': 'error',
|
||||||
|
'prettier/prettier': 'warn',
|
||||||
|
'react-hooks/exhaustive-deps': 'off',
|
||||||
|
},
|
||||||
|
};
|
@ -0,0 +1,35 @@
|
|||||||
|
# dependencies
|
||||||
|
/node_modules
|
||||||
|
/.pnp
|
||||||
|
.pnp.js
|
||||||
|
|
||||||
|
# testing
|
||||||
|
/coverage
|
||||||
|
|
||||||
|
# next.js
|
||||||
|
/.next/
|
||||||
|
/out/
|
||||||
|
|
||||||
|
# production
|
||||||
|
/build
|
||||||
|
|
||||||
|
# misc
|
||||||
|
.DS_Store
|
||||||
|
*.pem
|
||||||
|
|
||||||
|
# debug
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
.pnpm-debug.log*
|
||||||
|
|
||||||
|
# local env files
|
||||||
|
.env*.local
|
||||||
|
|
||||||
|
# vercel
|
||||||
|
.vercel
|
||||||
|
|
||||||
|
# typescript
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
.gitattributes
|
@ -0,0 +1,4 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
. "$(dirname "$0")/_/husky.sh"
|
||||||
|
|
||||||
|
yarn lint
|
@ -0,0 +1,33 @@
|
|||||||
|
# dependencies
|
||||||
|
/node_modules
|
||||||
|
/.pnp
|
||||||
|
.pnp.js
|
||||||
|
|
||||||
|
# testing
|
||||||
|
/coverage
|
||||||
|
|
||||||
|
# next.js
|
||||||
|
/.next/
|
||||||
|
/out/
|
||||||
|
|
||||||
|
# production
|
||||||
|
/build
|
||||||
|
|
||||||
|
# misc
|
||||||
|
.DS_Store
|
||||||
|
*.pem
|
||||||
|
|
||||||
|
# debug
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
.pnpm-debug.log*
|
||||||
|
|
||||||
|
# local env files
|
||||||
|
.env*.local
|
||||||
|
|
||||||
|
# vercel
|
||||||
|
.vercel
|
||||||
|
|
||||||
|
# typescript
|
||||||
|
*.tsbuildinfo
|
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"semi": true,
|
||||||
|
"singleQuote": true,
|
||||||
|
"trailingComma": "all",
|
||||||
|
"bracketSpacing": true,
|
||||||
|
"tabWidth": 2
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2022 Vincent Wu
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
@ -0,0 +1,117 @@
|
|||||||
|
# [LiveTerm - Make Terminal styled websites in minutes!](https://cveinnt.com)
|
||||||
|
|
||||||
|
Highly customizable, easy-to-use, and minimal terminal styled website template, written in Next.js.
|
||||||
|
|
||||||
|
# Table of Contents:
|
||||||
|
|
||||||
|
- [LiveTerm - Make Terminal styled websites in minutes!](#liveterm---make-terminal-styled-websites-in-minutes)
|
||||||
|
- [Table of Contents:](#table-of-contents)
|
||||||
|
- [Showcase](#showcase)
|
||||||
|
- [Quick Start](#quick-start)
|
||||||
|
- [Configuration](#configuration)
|
||||||
|
- [Basic Configuration](#basic-configuration)
|
||||||
|
- [Favicons](#favicons)
|
||||||
|
- [Banner](#banner)
|
||||||
|
- [Advanced Configuration](#advanced-configuration)
|
||||||
|
- [Deploy on Vercel](#deploy-on-vercel)
|
||||||
|
- [Credit](#credit)
|
||||||
|
|
||||||
|
## Showcase
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="./demo/demo.png" width="800"><br>
|
||||||
|
<strong>Default LiveTerm</strong>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
Live version [here](https://cveinnt.com)
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="./demo/cveinnt.png" width="800"><br>
|
||||||
|
<strong>my personal website</strong>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
Live version [here](https://cveinnt.com)
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
First, clone this repository:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/Cveinnt/LiveTerm.git
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, install dependencies:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
yarn install
|
||||||
|
```
|
||||||
|
|
||||||
|
Now you can start development!
|
||||||
|
|
||||||
|
```bash
|
||||||
|
yarn dev
|
||||||
|
```
|
||||||
|
|
||||||
|
Or, you can build the project:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
yarn build && yarn start
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
### Basic Configuration
|
||||||
|
|
||||||
|
Most of the configuration is done through the `config.json` file.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"readmeUrl": //create a Github README and link it here!
|
||||||
|
"title": //title of the website
|
||||||
|
"name": //returned by the command of the same name
|
||||||
|
"social": {
|
||||||
|
"github": // your handle
|
||||||
|
"linkedin": // your handle
|
||||||
|
},
|
||||||
|
"email": // your email
|
||||||
|
"ps1_hostname": //hostname in prompt
|
||||||
|
"ps1_username": "guest", // username in prompt
|
||||||
|
"non_terminal_url": "W",
|
||||||
|
"colors": {
|
||||||
|
... // you can use existing templates in themes.json or use your own!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Feel free to change it as you see fit!
|
||||||
|
|
||||||
|
You can find several pre-configured themes in `themes.json`, and you can replace the colors in `config.json` with the theme color you like! The themes are based on the themes on [this website](https://glitchbone.github.io/vscode-base16-term/#/).
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="./demo/themes.png" width="800"><br>
|
||||||
|
<strong>different LiveTerm themes</strong>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
Just replace `"light"` or `"dark"` in the `"color"` part of the config file!
|
||||||
|
|
||||||
|
### Favicons
|
||||||
|
|
||||||
|
Favicons are located in `public/`, along with other files you may want to upload. I used this [website](https://www.favicon-generator.org/) to generate favicons.
|
||||||
|
|
||||||
|
### Banner
|
||||||
|
|
||||||
|
You may also want to change the output of `banner` command. To do that, simply paste your generated banner in `src/utils/bin/utils.ts`. I used this [website](https://manytools.org/hacker-tools/ascii-banner/) to generate my banner.
|
||||||
|
|
||||||
|
### Advanced Configuration
|
||||||
|
|
||||||
|
If you want to further customize your page, feel free to change the source code to your preference!
|
||||||
|
|
||||||
|
## Deploy on Vercel
|
||||||
|
|
||||||
|
The easiest way to deploy a Next.js app is to use the [Vercel Platform](https://vercel.com/) from the creators of Next.js.
|
||||||
|
|
||||||
|
Check out [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
|
||||||
|
|
||||||
|
## Credit
|
||||||
|
|
||||||
|
Based on M4TT72's awesome [Terminal](https://github.com/m4tt72/terminal).
|
@ -0,0 +1,39 @@
|
|||||||
|
{
|
||||||
|
"readmeUrl": "https://raw.githubusercontent.com/cveinnt/cveinnt/master/README.md",
|
||||||
|
"title": "LiveTerm",
|
||||||
|
"name": "John Doe",
|
||||||
|
"ascii": "liveterm",
|
||||||
|
"social": {
|
||||||
|
"github": "",
|
||||||
|
"linkedin": ""
|
||||||
|
},
|
||||||
|
"email": "contact@johndoe.com",
|
||||||
|
"ps1_hostname": "liveterm",
|
||||||
|
"ps1_username": "visitor",
|
||||||
|
"non_terminal_url": "https://github.com/Cveinnt/LiveTerm",
|
||||||
|
"resume_url": "https://upload.wikimedia.org/wikipedia/commons/c/cc/Resume.pdf",
|
||||||
|
"donate_urls": {
|
||||||
|
"paypal": "https://paypal.me/cveinnt",
|
||||||
|
"patreon": "https://patreon.com/cveinnt"
|
||||||
|
},
|
||||||
|
"colors": {
|
||||||
|
"light": {
|
||||||
|
"background": "#FBF1C9",
|
||||||
|
"foreground": "#3C3836",
|
||||||
|
"yellow": "#D79921",
|
||||||
|
"green": "#98971A",
|
||||||
|
"gray": "#7C6F64",
|
||||||
|
"blue": "#458588",
|
||||||
|
"red": "#CA2124"
|
||||||
|
},
|
||||||
|
"dark": {
|
||||||
|
"background": "#2E3440",
|
||||||
|
"foreground": "#E5E9F0",
|
||||||
|
"yellow": "#5E81AC",
|
||||||
|
"green": "#A3BE8C",
|
||||||
|
"gray": "#88C0D0",
|
||||||
|
"blue": "#EBCB8B",
|
||||||
|
"red": "#BF616A"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
After Width: | Height: | Size: 352 KiB |
After Width: | Height: | Size: 310 KiB |
After Width: | Height: | Size: 334 KiB |
After Width: | Height: | Size: 335 KiB |
After Width: | Height: | Size: 334 KiB |
After Width: | Height: | Size: 332 KiB |
After Width: | Height: | Size: 335 KiB |
After Width: | Height: | Size: 332 KiB |
After Width: | Height: | Size: 330 KiB |
After Width: | Height: | Size: 216 KiB |
@ -0,0 +1,5 @@
|
|||||||
|
/// <reference types="next" />
|
||||||
|
/// <reference types="next/image-types/global" />
|
||||||
|
|
||||||
|
// NOTE: This file should not be edited
|
||||||
|
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
@ -0,0 +1 @@
|
|||||||
|
module.exports = {};
|
@ -0,0 +1,42 @@
|
|||||||
|
{
|
||||||
|
"name": "liveterm",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"license": "MIT",
|
||||||
|
"author": {
|
||||||
|
"name": "Vincent Wu",
|
||||||
|
"url": "https://cveinnt.com",
|
||||||
|
"email": "contact@wensenwu.com"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"dev": "next dev",
|
||||||
|
"build": "next build",
|
||||||
|
"start": "next start",
|
||||||
|
"lint": "next lint",
|
||||||
|
"prepare": "husky install"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"axios": "^0.27.2",
|
||||||
|
"next": "12.1.6",
|
||||||
|
"react": "18.1.0",
|
||||||
|
"react-dom": "18.1.0",
|
||||||
|
"react-icons": "^4.3.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "17.0.32",
|
||||||
|
"@types/react": "18.0.9",
|
||||||
|
"@types/react-dom": "18.0.3",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^5.23.0",
|
||||||
|
"@typescript-eslint/parser": "^5.23.0",
|
||||||
|
"autoprefixer": "^10.4.7",
|
||||||
|
"eslint": "8.15.0",
|
||||||
|
"eslint-config-next": "^12.1.6",
|
||||||
|
"eslint-plugin-next": "^0.0.0",
|
||||||
|
"eslint-plugin-prettier": "^4.0.0",
|
||||||
|
"eslint-plugin-react": "^7.29.4",
|
||||||
|
"husky": "^8.0.1",
|
||||||
|
"postcss": "^8.4.13",
|
||||||
|
"prettier": "^2.6.2",
|
||||||
|
"tailwindcss": "^3.0.24",
|
||||||
|
"typescript": "^4.6.4"
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
module.exports = {
|
||||||
|
plugins: {
|
||||||
|
tailwindcss: {},
|
||||||
|
autoprefixer: {},
|
||||||
|
},
|
||||||
|
};
|
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 21 KiB |
After Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 4.1 KiB |
After Width: | Height: | Size: 6.8 KiB |
After Width: | Height: | Size: 9.9 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 5.0 KiB |
After Width: | Height: | Size: 5.4 KiB |
After Width: | Height: | Size: 6.8 KiB |
After Width: | Height: | Size: 7.3 KiB |
After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 22 KiB |
@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<browserconfig><msapplication><tile><square70x70logo src="/ms-icon-70x70.png"/><square150x150logo src="/ms-icon-150x150.png"/><square310x310logo src="/ms-icon-310x310.png"/><TileColor>#ffffff</TileColor></tile></msapplication></browserconfig>
|
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 2.5 KiB |
After Width: | Height: | Size: 9.9 KiB |
After Width: | Height: | Size: 1.1 KiB |
@ -0,0 +1,49 @@
|
|||||||
|
{
|
||||||
|
"name": "LiveTerm",
|
||||||
|
"short_name": "LiveTerm",
|
||||||
|
"theme_color": "#2E3440",
|
||||||
|
"background_color": "#2E3440",
|
||||||
|
"display": "fullscreen",
|
||||||
|
"orientation": "portrait",
|
||||||
|
"scope": "/",
|
||||||
|
"start_url": "/",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "/android-icon-36x36.png",
|
||||||
|
"sizes": "36x36",
|
||||||
|
"type": "image/png",
|
||||||
|
"density": "0.75"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/android-icon-48x48.png",
|
||||||
|
"sizes": "48x48",
|
||||||
|
"type": "image/png",
|
||||||
|
"density": "1.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/android-icon-72x72.png",
|
||||||
|
"sizes": "72x72",
|
||||||
|
"type": "image/png",
|
||||||
|
"density": "1.5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/android-icon-96x96.png",
|
||||||
|
"sizes": "96x96",
|
||||||
|
"type": "image/png",
|
||||||
|
"density": "2.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/android-icon-144x144.png",
|
||||||
|
"sizes": "144x144",
|
||||||
|
"type": "image/png",
|
||||||
|
"density": "3.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/android-icon-192x192.png",
|
||||||
|
"sizes": "192x192",
|
||||||
|
"type": "image/png",
|
||||||
|
"density": "4.0"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"splash_pages": null
|
||||||
|
}
|
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 51 KiB |
After Width: | Height: | Size: 6.5 KiB |
@ -0,0 +1,3 @@
|
|||||||
|
User-agent: *
|
||||||
|
Disallow:
|
||||||
|
Disallow: /cgi-bin/
|
@ -0,0 +1,19 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import config from '../../config.json';
|
||||||
|
|
||||||
|
export const Ps1 = () => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<span className="text-light-yellow dark:text-dark-yellow">
|
||||||
|
{config.ps1_username}
|
||||||
|
</span>
|
||||||
|
<span className="text-light-gray dark:text-dark-gray">@</span>
|
||||||
|
<span className="text-light-green dark:text-dark-green">
|
||||||
|
{config.ps1_hostname}
|
||||||
|
</span>
|
||||||
|
<span className="text-light-gray dark:text-dark-gray">:$ ~ </span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Ps1;
|
@ -0,0 +1,31 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { History as HistoryInterface } from './interface';
|
||||||
|
import { Ps1 } from '../Ps1';
|
||||||
|
|
||||||
|
export const History: React.FC<{ history: Array<HistoryInterface> }> = ({
|
||||||
|
history,
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{history.map((entry: HistoryInterface, index: number) => (
|
||||||
|
<div key={entry.command + index}>
|
||||||
|
<div className="flex flex-row space-x-2">
|
||||||
|
<div className="flex-shrink">
|
||||||
|
<Ps1 />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex-grow">{entry.command}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p
|
||||||
|
className="whitespace-pre-wrap mb-2"
|
||||||
|
// style={{ lineHeight: 'normal' }}
|
||||||
|
dangerouslySetInnerHTML={{ __html: entry.output }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default History;
|
@ -0,0 +1,27 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { History } from './interface';
|
||||||
|
|
||||||
|
export const useHistory = (defaultValue: Array<History>) => {
|
||||||
|
const [history, setHistory] = React.useState<Array<History>>(defaultValue);
|
||||||
|
const [command, setCommand] = React.useState<string>('');
|
||||||
|
const [lastCommandIndex, setLastCommandIndex] = React.useState<number>(0);
|
||||||
|
|
||||||
|
return {
|
||||||
|
history,
|
||||||
|
command,
|
||||||
|
lastCommandIndex,
|
||||||
|
setHistory: (value: string) =>
|
||||||
|
setHistory([
|
||||||
|
...history,
|
||||||
|
{
|
||||||
|
id: history.length,
|
||||||
|
date: new Date(),
|
||||||
|
command,
|
||||||
|
output: value,
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
setCommand,
|
||||||
|
setLastCommandIndex,
|
||||||
|
clearHistory: () => setHistory([]),
|
||||||
|
};
|
||||||
|
};
|
@ -0,0 +1,6 @@
|
|||||||
|
export interface History {
|
||||||
|
id: number;
|
||||||
|
date: Date;
|
||||||
|
command: string;
|
||||||
|
output: string;
|
||||||
|
}
|
@ -0,0 +1,106 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { commandExists } from '../utils/commandExists';
|
||||||
|
import { shell } from '../utils/shell';
|
||||||
|
import { handleTabCompletion } from '../utils/tabCompletion';
|
||||||
|
import { Ps1 } from './Ps1';
|
||||||
|
|
||||||
|
export const Input = ({
|
||||||
|
inputRef,
|
||||||
|
containerRef,
|
||||||
|
command,
|
||||||
|
history,
|
||||||
|
lastCommandIndex,
|
||||||
|
setCommand,
|
||||||
|
setHistory,
|
||||||
|
setLastCommandIndex,
|
||||||
|
clearHistory,
|
||||||
|
}) => {
|
||||||
|
const onSubmit = async (event: React.KeyboardEvent<HTMLInputElement>) => {
|
||||||
|
const commands: [string] = history
|
||||||
|
.map(({ command }) => command)
|
||||||
|
.filter((command: string) => command);
|
||||||
|
|
||||||
|
if (event.key === 'c' && event.ctrlKey) {
|
||||||
|
event.preventDefault();
|
||||||
|
setCommand('');
|
||||||
|
setHistory('');
|
||||||
|
setLastCommandIndex(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.key === 'l' && event.ctrlKey) {
|
||||||
|
event.preventDefault();
|
||||||
|
clearHistory();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.key === 'Tab') {
|
||||||
|
event.preventDefault();
|
||||||
|
handleTabCompletion(command, setCommand);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.key === 'Enter' || event.code === '13') {
|
||||||
|
event.preventDefault();
|
||||||
|
setLastCommandIndex(0);
|
||||||
|
await shell(command, setHistory, clearHistory, setCommand);
|
||||||
|
containerRef.current.scrollTo(0, containerRef.current.scrollHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.key === 'ArrowUp') {
|
||||||
|
event.preventDefault();
|
||||||
|
if (!commands.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const index: number = lastCommandIndex + 1;
|
||||||
|
if (index <= commands.length) {
|
||||||
|
setLastCommandIndex(index);
|
||||||
|
setCommand(commands[commands.length - index]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.key === 'ArrowDown') {
|
||||||
|
event.preventDefault();
|
||||||
|
if (!commands.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const index: number = lastCommandIndex - 1;
|
||||||
|
if (index > 0) {
|
||||||
|
setLastCommandIndex(index);
|
||||||
|
setCommand(commands[commands.length - index]);
|
||||||
|
} else {
|
||||||
|
setLastCommandIndex(0);
|
||||||
|
setCommand('');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onChange = ({
|
||||||
|
target: { value },
|
||||||
|
}: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
setCommand(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-row space-x-2">
|
||||||
|
<label htmlFor="prompt" className="flex-shrink">
|
||||||
|
<Ps1 />
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<input
|
||||||
|
ref={inputRef}
|
||||||
|
id="prompt"
|
||||||
|
type="text"
|
||||||
|
className={`bg-light-background dark:bg-dark-background focus:outline-none flex-grow ${
|
||||||
|
commandExists(command) || command === ''
|
||||||
|
? 'text-dark-green'
|
||||||
|
: 'text-dark-red'
|
||||||
|
}`}
|
||||||
|
value={command}
|
||||||
|
onChange={onChange}
|
||||||
|
autoFocus
|
||||||
|
onKeyDown={onSubmit}
|
||||||
|
autoComplete="off"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Input;
|
@ -0,0 +1,14 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { useRouter } from 'next/router';
|
||||||
|
|
||||||
|
const NotFoundPage = () => {
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
router.replace('/');
|
||||||
|
});
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default NotFoundPage;
|
@ -0,0 +1,35 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import '../styles/global.css';
|
||||||
|
import Head from 'next/head';
|
||||||
|
|
||||||
|
const App = ({ Component, pageProps }) => {
|
||||||
|
const inputRef = React.useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
|
const onClickAnywhere = () => {
|
||||||
|
inputRef.current.focus();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Head>
|
||||||
|
<meta
|
||||||
|
name="viewport"
|
||||||
|
content="initial-scale=1.0, width=device-width"
|
||||||
|
key="viewport"
|
||||||
|
maximum-scale="1"
|
||||||
|
/>
|
||||||
|
</Head>
|
||||||
|
|
||||||
|
<div
|
||||||
|
className="text-light-foreground dark:text-dark-foreground min-w-max text-xs md:min-w-full md:text-base"
|
||||||
|
onClick={onClickAnywhere}
|
||||||
|
>
|
||||||
|
<main className="bg-light-background dark:bg-dark-background w-full h-full p-2">
|
||||||
|
<Component {...pageProps} inputRef={inputRef} />
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default App;
|
@ -0,0 +1,64 @@
|
|||||||
|
import Head from 'next/head';
|
||||||
|
import React from 'react';
|
||||||
|
import config from '../../config.json';
|
||||||
|
import { Input } from '../components/input';
|
||||||
|
import { useHistory } from '../components/history/hook';
|
||||||
|
import { History } from '../components/history/History';
|
||||||
|
import { banner } from '../utils/bin';
|
||||||
|
|
||||||
|
interface IndexPageProps {
|
||||||
|
inputRef: React.MutableRefObject<HTMLInputElement>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const IndexPage: React.FC<IndexPageProps> = ({ inputRef }) => {
|
||||||
|
const containerRef = React.useRef(null);
|
||||||
|
const {
|
||||||
|
history,
|
||||||
|
command,
|
||||||
|
lastCommandIndex,
|
||||||
|
setCommand,
|
||||||
|
setHistory,
|
||||||
|
clearHistory,
|
||||||
|
setLastCommandIndex,
|
||||||
|
} = useHistory([]);
|
||||||
|
|
||||||
|
const init = React.useCallback(() => setHistory(banner()), []);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
init();
|
||||||
|
}, [init]);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (inputRef.current) {
|
||||||
|
inputRef.current.focus();
|
||||||
|
}
|
||||||
|
}, [history]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Head>
|
||||||
|
<title>{config.title}</title>
|
||||||
|
</Head>
|
||||||
|
|
||||||
|
<div className="p-8 overflow-hidden h-full border-2 rounded border-light-yellow dark:border-dark-yellow">
|
||||||
|
<div ref={containerRef} className="overflow-y-auto h-full">
|
||||||
|
<History history={history} />
|
||||||
|
|
||||||
|
<Input
|
||||||
|
inputRef={inputRef}
|
||||||
|
containerRef={containerRef}
|
||||||
|
command={command}
|
||||||
|
history={history}
|
||||||
|
lastCommandIndex={lastCommandIndex}
|
||||||
|
setCommand={setCommand}
|
||||||
|
setHistory={setHistory}
|
||||||
|
setLastCommandIndex={setLastCommandIndex}
|
||||||
|
clearHistory={clearHistory}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default IndexPage;
|
@ -0,0 +1,41 @@
|
|||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Hack';
|
||||||
|
src: url(/assets/fonts/Hack-NF.ttf);
|
||||||
|
display: swap;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
font-family: 'Hack', monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
html,
|
||||||
|
body,
|
||||||
|
body > div:first-child,
|
||||||
|
div#__next,
|
||||||
|
div#__next > div {
|
||||||
|
height: 100%;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-track {
|
||||||
|
background: #1e252e;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
background: #ebdbb2;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: #ff8037;
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
import axios from 'axios';
|
||||||
|
import config from '../../config.json';
|
||||||
|
|
||||||
|
export const getProjects = async () => {
|
||||||
|
const { data } = await axios.get(
|
||||||
|
`https://api.github.com/users/${config.social.github}/repos`,
|
||||||
|
);
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getReadme = async () => {
|
||||||
|
const { data } = await axios.get(config.readmeUrl);
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getWeather = async (city: string) => {
|
||||||
|
try {
|
||||||
|
const { data } = await axios.get(`/api/weather/${city}`);
|
||||||
|
return data;
|
||||||
|
} catch (error) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getQuote = async () => {
|
||||||
|
const { data } = await axios.get('http://api.quotable.io/random');
|
||||||
|
return {
|
||||||
|
quote: `“${data.content}” — ${data.author}`,
|
||||||
|
};
|
||||||
|
};
|
@ -0,0 +1,36 @@
|
|||||||
|
// // List of commands that require API calls
|
||||||
|
|
||||||
|
import { getProjects } from '../api';
|
||||||
|
import { getQuote } from '../api';
|
||||||
|
import { getReadme } from '../api';
|
||||||
|
import { getWeather } from '../api';
|
||||||
|
|
||||||
|
export const projects = async (args: string[]): Promise<string> => {
|
||||||
|
const projects = await getProjects();
|
||||||
|
return projects
|
||||||
|
.map(
|
||||||
|
(repo) =>
|
||||||
|
`${repo.name} - <a class="text-light-blue dark:text-dark-blue underline" href="${repo.html_url}" target="_blank">${repo.html_url}</a>`,
|
||||||
|
)
|
||||||
|
.join('\n');
|
||||||
|
};
|
||||||
|
|
||||||
|
export const quote = async (args: string[]): Promise<string> => {
|
||||||
|
const data = await getQuote();
|
||||||
|
return data.quote;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const readme = async (args: string[]): Promise<string> => {
|
||||||
|
const readme = await getReadme();
|
||||||
|
return `Opening GitHub README...\n
|
||||||
|
${readme}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const weather = async (args: string[]): Promise<string> => {
|
||||||
|
const city = args.join('+');
|
||||||
|
if (!city) {
|
||||||
|
return 'Usage: weather [city]. Example: weather casablanca';
|
||||||
|
}
|
||||||
|
const weather = await getWeather(city);
|
||||||
|
return weather;
|
||||||
|
};
|
@ -0,0 +1,136 @@
|
|||||||
|
// List of commands that do not require API calls
|
||||||
|
|
||||||
|
import * as bin from './index';
|
||||||
|
import config from '../../../config.json';
|
||||||
|
|
||||||
|
// Help
|
||||||
|
export const help = async (args: string[]): Promise<string> => {
|
||||||
|
const commands = Object.keys(bin).sort().join(', ');
|
||||||
|
var c = '';
|
||||||
|
for (let i = 1; i <= Object.keys(bin).sort().length; i++) {
|
||||||
|
if (i % 7 === 0) {
|
||||||
|
c += Object.keys(bin).sort()[i - 1] + '\n';
|
||||||
|
} else {
|
||||||
|
c += Object.keys(bin).sort()[i - 1] + ' ';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return `Welcome! Here are all the available commands:
|
||||||
|
\n${c}\n
|
||||||
|
[tab]: trigger completion.
|
||||||
|
[ctrl+l]/clear: clear terminal.\n
|
||||||
|
Type 'sumfetch' to display summary.
|
||||||
|
Type 'gui' or click <u><a href="${config.non_terminal_url}" target="_blank">here</a></u> for a simpler version.
|
||||||
|
`;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Redirection
|
||||||
|
export const gui = async (args: string[]): Promise<string> => {
|
||||||
|
window.open(`${config.non_terminal_url}`);
|
||||||
|
return 'Opening GUI version...';
|
||||||
|
};
|
||||||
|
|
||||||
|
// About
|
||||||
|
export const about = async (args: string[]): Promise<string> => {
|
||||||
|
return `Hi, I am ${config.name}.
|
||||||
|
Welcome to my website!
|
||||||
|
More about me:
|
||||||
|
'sumfetch' - short summary.
|
||||||
|
'resume' - my latest resume.
|
||||||
|
'readme' - my github readme.`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const resume = async (args: string[]): Promise<string> => {
|
||||||
|
window.open(`${config.resume_url}`);
|
||||||
|
return 'Opening resume...';
|
||||||
|
};
|
||||||
|
|
||||||
|
// Donate
|
||||||
|
export const donate = async (args: string[]): Promise<string> => {
|
||||||
|
return `thank you for your interest.
|
||||||
|
here are the ways you can support my work:
|
||||||
|
- <u><a class="text-light-blue dark:text-dark-blue underline" href="${config.donate_urls.paypal}" target="_blank">paypal</a></u>
|
||||||
|
- <u><a class="text-light-blue dark:text-dark-blue underline" href="${config.donate_urls.patreon}" target="_blank">patreon</a></u>
|
||||||
|
`;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Contact
|
||||||
|
export const email = async (args: string[]): Promise<string> => {
|
||||||
|
window.open(`mailto:${config.email}`);
|
||||||
|
return `Opening mailto:${config.email}...`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const github = async (args: string[]): Promise<string> => {
|
||||||
|
window.open(`https://github.com/${config.social.github}/`);
|
||||||
|
|
||||||
|
return 'Opening github...';
|
||||||
|
};
|
||||||
|
|
||||||
|
export const linkedin = async (args: string[]): Promise<string> => {
|
||||||
|
window.open(`https://www.linkedin.com/in/${config.social.linkedin}/`);
|
||||||
|
|
||||||
|
return 'Opening linkedin...';
|
||||||
|
};
|
||||||
|
|
||||||
|
// Typical linux commands
|
||||||
|
export const echo = async (args: string[]): Promise<string> => {
|
||||||
|
return args.join(' ');
|
||||||
|
};
|
||||||
|
|
||||||
|
export const whoami = async (args: string[]): Promise<string> => {
|
||||||
|
return `${config.ps1_username}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ls = async (args: string[]): Promise<string> => {
|
||||||
|
return `a
|
||||||
|
bunch
|
||||||
|
of
|
||||||
|
fake
|
||||||
|
directories`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const cd = async (args: string[]): Promise<string> => {
|
||||||
|
return `unfortunately, i cannot afford more directories.
|
||||||
|
if you want to help, you can type 'donate'.`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const date = async (args: string[]): Promise<string> => {
|
||||||
|
return new Date().toString();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const vi = async (args: string[]): Promise<string> => {
|
||||||
|
return `woah, you still use 'vi'? just try 'vim'.`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const vim = async (args: string[]): Promise<string> => {
|
||||||
|
return `'vim' is so outdated. how about 'nvim'?`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const nvim = async (args: string[]): Promise<string> => {
|
||||||
|
return `'nvim'? too fancy. why not 'emacs'?`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const emacs = async (args?: string[]): Promise<string> => {
|
||||||
|
return `you know what? just use vscode.`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const sudo = async (args?: string[]): Promise<string> => {
|
||||||
|
window.open('https://www.youtube.com/watch?v=dQw4w9WgXcQ', '_blank'); // good ol' rick roll
|
||||||
|
return `Permission denied: with little power comes... no responsibility? `;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Banner
|
||||||
|
export const banner = (args?: string[]): string => {
|
||||||
|
return `
|
||||||
|
█████ ███ ███████████
|
||||||
|
░░███ ░░░ ░█░░░███░░░█
|
||||||
|
░███ ████ █████ █████ ██████ ░ ░███ ░ ██████ ████████ █████████████
|
||||||
|
░███ ░░███ ░░███ ░░███ ███░░███ ░███ ███░░███░░███░░███░░███░░███░░███
|
||||||
|
░███ ░███ ░███ ░███ ░███████ ░███ ░███████ ░███ ░░░ ░███ ░███ ░███
|
||||||
|
░███ █ ░███ ░░███ ███ ░███░░░ ░███ ░███░░░ ░███ ░███ ░███ ░███
|
||||||
|
███████████ █████ ░░█████ ░░██████ █████ ░░██████ █████ █████░███ █████
|
||||||
|
░░░░░░░░░░░ ░░░░░ ░░░░░ ░░░░░░ ░░░░░ ░░░░░░ ░░░░░ ░░░░░ ░░░ ░░░░░
|
||||||
|
Type 'help' to see the list of available commands.
|
||||||
|
Type 'sumfetch' to display summary.
|
||||||
|
Type 'gui' or click <u><a class="text-light-blue dark:text-dark-blue underline" href="${config.non_terminal_url}" target="_blank">here</a></u> for a simpler version.
|
||||||
|
`;
|
||||||
|
};
|
@ -0,0 +1,3 @@
|
|||||||
|
export * from './commands';
|
||||||
|
export * from './api_commands';
|
||||||
|
export { default as sumfetch } from './sumfetch';
|
@ -0,0 +1,6 @@
|
|||||||
|
import * as bin from './bin';
|
||||||
|
|
||||||
|
export const commandExists = (command: string) => {
|
||||||
|
const commands = ['clear', ...Object.keys(bin)];
|
||||||
|
return commands.indexOf(command.split(' ')[0].toLowerCase()) !== -1;
|
||||||
|
};
|
@ -0,0 +1,27 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import * as bin from './bin';
|
||||||
|
|
||||||
|
export const shell = async (
|
||||||
|
command: string,
|
||||||
|
setHistory: (value: string) => void,
|
||||||
|
clearHistory: () => void,
|
||||||
|
setCommand: React.Dispatch<React.SetStateAction<string>>,
|
||||||
|
) => {
|
||||||
|
const args = command.split(' ');
|
||||||
|
args[0] = args[0].toLowerCase();
|
||||||
|
|
||||||
|
if (args[0] === 'clear') {
|
||||||
|
clearHistory();
|
||||||
|
} else if (command === '') {
|
||||||
|
setHistory('');
|
||||||
|
} else if (Object.keys(bin).indexOf(args[0]) === -1) {
|
||||||
|
setHistory(
|
||||||
|
`shell: command not found: ${args[0]}. Try 'help' to get started.`,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
const output = await bin[args[0]](args.slice(1));
|
||||||
|
setHistory(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
setCommand('');
|
||||||
|
};
|
@ -0,0 +1,14 @@
|
|||||||
|
import * as bin from './bin';
|
||||||
|
|
||||||
|
export const handleTabCompletion = (
|
||||||
|
command: string,
|
||||||
|
setCommand: React.Dispatch<React.SetStateAction<string>>,
|
||||||
|
) => {
|
||||||
|
const commands = Object.keys(bin).filter((entry) =>
|
||||||
|
entry.startsWith(command),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (commands.length === 1) {
|
||||||
|
setCommand(commands[0]);
|
||||||
|
}
|
||||||
|
};
|
@ -0,0 +1,21 @@
|
|||||||
|
const { colors } = require('./config.json');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
content: [
|
||||||
|
'./src/pages/**/*.{js,ts,jsx,tsx}',
|
||||||
|
'./src/components/**/*.{js,ts,jsx,tsx}',
|
||||||
|
],
|
||||||
|
darkMode: 'media', // or 'media' or 'class'
|
||||||
|
theme: {
|
||||||
|
colors: {
|
||||||
|
transparent: 'transparent',
|
||||||
|
current: 'currentColor',
|
||||||
|
...colors,
|
||||||
|
},
|
||||||
|
extend: {},
|
||||||
|
},
|
||||||
|
variants: {
|
||||||
|
extend: {},
|
||||||
|
},
|
||||||
|
plugins: [],
|
||||||
|
};
|
@ -0,0 +1,162 @@
|
|||||||
|
{
|
||||||
|
"default": {
|
||||||
|
"light": {
|
||||||
|
"background": "#FBF1C9",
|
||||||
|
"foreground": "#3C3836",
|
||||||
|
"yellow": "#D79921",
|
||||||
|
"green": "#98971A",
|
||||||
|
"gray": "#7C6F64",
|
||||||
|
"blue": "#458588",
|
||||||
|
"red": "#CA2124"
|
||||||
|
},
|
||||||
|
"dark": {
|
||||||
|
"background": "#2E3440",
|
||||||
|
"foreground": "#E5E9F0",
|
||||||
|
"yellow": "#5E81AC",
|
||||||
|
"green": "#A3BE8C",
|
||||||
|
"gray": "#88C0D0",
|
||||||
|
"blue": "#EBCB8B",
|
||||||
|
"red": "#BF616A"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"gruvbox": {
|
||||||
|
"light": {
|
||||||
|
"background": "#FBF1C9",
|
||||||
|
"foreground": "#3C3836",
|
||||||
|
"yellow": "#D79921",
|
||||||
|
"green": "#98971A",
|
||||||
|
"gray": "#7C6F64",
|
||||||
|
"blue": "#458588",
|
||||||
|
"red": "#CA2124"
|
||||||
|
},
|
||||||
|
"dark": {
|
||||||
|
"background": "#1c1c1c",
|
||||||
|
"foreground": "#EBDBB2",
|
||||||
|
"yellow": "#D79921",
|
||||||
|
"green": "#98971A",
|
||||||
|
"gray": "#A89984",
|
||||||
|
"blue": "#458588",
|
||||||
|
"red": "#CA2124"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dracula": {
|
||||||
|
"light": {
|
||||||
|
"background": "#FFFFDB",
|
||||||
|
"foreground": "#282a36",
|
||||||
|
"yellow": "#ffb86c",
|
||||||
|
"green": "#50fa7b",
|
||||||
|
"gray": "#8B6BB9",
|
||||||
|
"blue": "#67AFC0",
|
||||||
|
"red": "#ff5555"
|
||||||
|
},
|
||||||
|
"dark": {
|
||||||
|
"background": "#282a36",
|
||||||
|
"foreground": "#f8f8f2",
|
||||||
|
"yellow": "#ffb86c",
|
||||||
|
"green": "#50fa7b",
|
||||||
|
"gray": "#bd93f9",
|
||||||
|
"blue": "#8be9fd",
|
||||||
|
"red": "#ff5555"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Nord": {
|
||||||
|
"light": {
|
||||||
|
"background": "#E5E9F0",
|
||||||
|
"foreground": "#2E3440",
|
||||||
|
"yellow": "#5E81AC",
|
||||||
|
"green": "#A3BE8C",
|
||||||
|
"gray": "#88C0D0",
|
||||||
|
"blue": "#EBCB8B",
|
||||||
|
"red": "#BF616A"
|
||||||
|
},
|
||||||
|
"dark": {
|
||||||
|
"background": "#2E3440",
|
||||||
|
"foreground": "#E5E9F0",
|
||||||
|
"yellow": "#5E81AC",
|
||||||
|
"green": "#A3BE8C",
|
||||||
|
"gray": "#88C0D0",
|
||||||
|
"blue": "#EBCB8B",
|
||||||
|
"red": "#BF616A"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Monokai": {
|
||||||
|
"light": {
|
||||||
|
"background": "#F8F8F2",
|
||||||
|
"foreground": "#272822",
|
||||||
|
"yellow": "#F4BF75",
|
||||||
|
"green": "#A6E22E",
|
||||||
|
"gray": "#AE81FF",
|
||||||
|
"blue": "#66D9EF",
|
||||||
|
"red": "#F92672"
|
||||||
|
},
|
||||||
|
"dark": {
|
||||||
|
"background": "#272822",
|
||||||
|
"foreground": "#F8F8F2",
|
||||||
|
"yellow": "#F4BF75",
|
||||||
|
"green": "#A6E22E",
|
||||||
|
"gray": "#AE81FF",
|
||||||
|
"blue": "#66D9EF",
|
||||||
|
"red": "#F92672"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Mocha": {
|
||||||
|
"light": {
|
||||||
|
"background": "#D0C8C6",
|
||||||
|
"foreground": "#3B3228",
|
||||||
|
"yellow": "#F4BC87",
|
||||||
|
"green": "#BEB55B",
|
||||||
|
"gray": "#A89BB9",
|
||||||
|
"blue": "#8AB3B5",
|
||||||
|
"red": "#CB6077"
|
||||||
|
},
|
||||||
|
"dark": {
|
||||||
|
"background": "#3B3228",
|
||||||
|
"foreground": "#D0C8C6",
|
||||||
|
"yellow": "#F4BC87",
|
||||||
|
"green": "#BEB55B",
|
||||||
|
"gray": "#A89BB9",
|
||||||
|
"blue": "#8AB3B5",
|
||||||
|
"red": "#CB6077"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Solarized": {
|
||||||
|
"light": {
|
||||||
|
"background": "#FDF6E3",
|
||||||
|
"foreground": "#586E75",
|
||||||
|
"yellow": "#B58900",
|
||||||
|
"green": "#859900",
|
||||||
|
"gray": "#6C71C4",
|
||||||
|
"blue": "#268BD2",
|
||||||
|
"red": "#DC322F"
|
||||||
|
},
|
||||||
|
"dark": {
|
||||||
|
"background": "#002B36",
|
||||||
|
"foreground": "#93A1A1",
|
||||||
|
"yellow": "#B58900",
|
||||||
|
"green": "#859900",
|
||||||
|
"gray": "#6C71C4",
|
||||||
|
"blue": "#268BD2",
|
||||||
|
"red": "#DC322F"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Paraiso": {
|
||||||
|
"light": {
|
||||||
|
"background": "#A39E9B",
|
||||||
|
"foreground": "#2F1E2E",
|
||||||
|
"yellow": "#FEC418",
|
||||||
|
"green": "#48B685",
|
||||||
|
"gray": "#815BA4",
|
||||||
|
"blue": "#06B6EF",
|
||||||
|
"red": "#EF6155"
|
||||||
|
},
|
||||||
|
"dark": {
|
||||||
|
"background": "#2F1E2E",
|
||||||
|
"foreground": "#A39E9B",
|
||||||
|
"yellow": "#FEC418",
|
||||||
|
"green": "#48B685",
|
||||||
|
"gray": "#815BA4",
|
||||||
|
"blue": "#06B6EF",
|
||||||
|
"red": "#EF6155"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es5",
|
||||||
|
"lib": ["dom", "dom.iterable", "esnext"],
|
||||||
|
"allowJs": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"strict": false,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"incremental": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"module": "esnext",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"jsx": "preserve"
|
||||||
|
},
|
||||||
|
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
|
||||||
|
"exclude": ["node_modules"]
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"github": {
|
||||||
|
"silent": true
|
||||||
|
}
|
||||||
|
}
|