Init rebase to Svelte and TS
This commit is contained in:
50
src/app.css
Normal file
50
src/app.css
Normal file
@@ -0,0 +1,50 @@
|
||||
@import './style/global.css';
|
||||
@import 'tailwindcss';
|
||||
|
||||
:root {
|
||||
font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
|
||||
line-height: 1.5;
|
||||
font-weight: 400;
|
||||
|
||||
color-scheme: light dark;
|
||||
|
||||
font-synthesis: none;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
body,
|
||||
#app {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
color: var(--text);
|
||||
background-color: var(--bg-dark);
|
||||
background-image: radial-gradient(circle at center, var(--bg-light) 1px, transparent 1px);
|
||||
}
|
||||
|
||||
a {
|
||||
font-weight: 500;
|
||||
color: #646cff;
|
||||
text-decoration: inherit;
|
||||
}
|
||||
a:hover {
|
||||
color: #535bf2;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 3.2em;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
.card {
|
||||
padding: 2em;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
border-color: #646cff;
|
||||
}
|
||||
button:focus,
|
||||
button:focus-visible {
|
||||
outline: 4px auto -webkit-focus-ring-color;
|
||||
}
|
||||
13
src/app.d.ts
vendored
Normal file
13
src/app.d.ts
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
// See https://svelte.dev/docs/kit/types#app.d.ts
|
||||
// for information about these interfaces
|
||||
declare global {
|
||||
namespace App {
|
||||
// interface Error {}
|
||||
// interface Locals {}
|
||||
// interface PageData {}
|
||||
// interface PageState {}
|
||||
// interface Platform {}
|
||||
}
|
||||
}
|
||||
|
||||
export {};
|
||||
11
src/app.html
Normal file
11
src/app.html
Normal file
@@ -0,0 +1,11 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
%sveltekit.head%
|
||||
</head>
|
||||
<body data-sveltekit-preload-data="hover">
|
||||
<div style="display: contents">%sveltekit.body%</div>
|
||||
</body>
|
||||
</html>
|
||||
7
src/demo.spec.ts
Normal file
7
src/demo.spec.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
|
||||
describe('sum test', () => {
|
||||
it('adds 1 + 2 to equal 3', () => {
|
||||
expect(1 + 2).toBe(3);
|
||||
});
|
||||
});
|
||||
10
src/lib/assets/deFlag.svg
Executable file
10
src/lib/assets/deFlag.svg
Executable file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
viewBox="0 0 512 512" xml:space="preserve">
|
||||
<path style="fill:#464655;" d="M473.655,88.276H38.345C17.167,88.276,0,105.443,0,126.621v73.471h512v-73.471
|
||||
C512,105.443,494.833,88.276,473.655,88.276z"/>
|
||||
<path style="fill:#FFE15A;" d="M0,385.379c0,21.177,17.167,38.345,38.345,38.345h435.31c21.177,0,38.345-17.167,38.345-38.345
|
||||
v-73.471H0V385.379z"/>
|
||||
<rect y="200.09" style="fill:#FF4B55;" width="512" height="111.81"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 668 B |
2
src/lib/assets/enFlag.svg
Executable file
2
src/lib/assets/enFlag.svg
Executable file
@@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg width="800px" height="800px" viewBox="0 0 36 36" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--twemoji" preserveAspectRatio="xMidYMid meet"><path fill="#00247D" d="M0 9.059V13h5.628zM4.664 31H13v-5.837zM23 25.164V31h8.335zM0 23v3.941L5.63 23zM31.337 5H23v5.837zM36 26.942V23h-5.631zM36 13V9.059L30.371 13zM13 5H4.664L13 10.837z"></path><path fill="#CF1B2B" d="M25.14 23l9.712 6.801a3.977 3.977 0 0 0 .99-1.749L28.627 23H25.14zM13 23h-2.141l-9.711 6.8c.521.53 1.189.909 1.938 1.085L13 23.943V23zm10-10h2.141l9.711-6.8a3.988 3.988 0 0 0-1.937-1.085L23 12.057V13zm-12.141 0L1.148 6.2a3.994 3.994 0 0 0-.991 1.749L7.372 13h3.487z"></path><path fill="#EEE" d="M36 21H21v10h2v-5.836L31.335 31H32a3.99 3.99 0 0 0 2.852-1.199L25.14 23h3.487l7.215 5.052c.093-.337.158-.686.158-1.052v-.058L30.369 23H36v-2zM0 21v2h5.63L0 26.941V27c0 1.091.439 2.078 1.148 2.8l9.711-6.8H13v.943l-9.914 6.941c.294.07.598.116.914.116h.664L13 25.163V31h2V21H0zM36 9a3.983 3.983 0 0 0-1.148-2.8L25.141 13H23v-.943l9.915-6.942A4.001 4.001 0 0 0 32 5h-.663L23 10.837V5h-2v10h15v-2h-5.629L36 9.059V9zM13 5v5.837L4.664 5H4a3.985 3.985 0 0 0-2.852 1.2l9.711 6.8H7.372L.157 7.949A3.968 3.968 0 0 0 0 9v.059L5.628 13H0v2h15V5h-2z"></path><path fill="#CF1B2B" d="M21 15V5h-6v10H0v6h15v10h6V21h15v-6z"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
1
src/lib/assets/favicon.svg
Normal file
1
src/lib/assets/favicon.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="107" height="128" viewBox="0 0 107 128"><title>svelte-logo</title><path d="M94.157 22.819c-10.4-14.885-30.94-19.297-45.792-9.835L22.282 29.608A29.92 29.92 0 0 0 8.764 49.65a31.5 31.5 0 0 0 3.108 20.231 30 30 0 0 0-4.477 11.183 31.9 31.9 0 0 0 5.448 24.116c10.402 14.887 30.942 19.297 45.791 9.835l26.083-16.624A29.92 29.92 0 0 0 98.235 78.35a31.53 31.53 0 0 0-3.105-20.232 30 30 0 0 0 4.474-11.182 31.88 31.88 0 0 0-5.447-24.116" style="fill:#ff3e00"/><path d="M45.817 106.582a20.72 20.72 0 0 1-22.237-8.243 19.17 19.17 0 0 1-3.277-14.503 18 18 0 0 1 .624-2.435l.49-1.498 1.337.981a33.6 33.6 0 0 0 10.203 5.098l.97.294-.09.968a5.85 5.85 0 0 0 1.052 3.878 6.24 6.24 0 0 0 6.695 2.485 5.8 5.8 0 0 0 1.603-.704L69.27 76.28a5.43 5.43 0 0 0 2.45-3.631 5.8 5.8 0 0 0-.987-4.371 6.24 6.24 0 0 0-6.698-2.487 5.7 5.7 0 0 0-1.6.704l-9.953 6.345a19 19 0 0 1-5.296 2.326 20.72 20.72 0 0 1-22.237-8.243 19.17 19.17 0 0 1-3.277-14.502 17.99 17.99 0 0 1 8.13-12.052l26.081-16.623a19 19 0 0 1 5.3-2.329 20.72 20.72 0 0 1 22.237 8.243 19.17 19.17 0 0 1 3.277 14.503 18 18 0 0 1-.624 2.435l-.49 1.498-1.337-.98a33.6 33.6 0 0 0-10.203-5.1l-.97-.294.09-.968a5.86 5.86 0 0 0-1.052-3.878 6.24 6.24 0 0 0-6.696-2.485 5.8 5.8 0 0 0-1.602.704L37.73 51.72a5.42 5.42 0 0 0-2.449 3.63 5.79 5.79 0 0 0 .986 4.372 6.24 6.24 0 0 0 6.698 2.486 5.8 5.8 0 0 0 1.602-.704l9.952-6.342a19 19 0 0 1 5.295-2.328 20.72 20.72 0 0 1 22.237 8.242 19.17 19.17 0 0 1 3.277 14.503 18 18 0 0 1-8.13 12.053l-26.081 16.622a19 19 0 0 1-5.3 2.328" style="fill:#fff"/></svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
10
src/lib/assets/frFlag.svg
Executable file
10
src/lib/assets/frFlag.svg
Executable file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
viewBox="0 0 512 512" xml:space="preserve">
|
||||
<path style="fill:#41479B;" d="M38.345,88.273C17.167,88.273,0,105.44,0,126.618v258.759c0,21.177,17.167,38.345,38.345,38.345
|
||||
h132.322V88.273H38.345z"/>
|
||||
<rect x="170.67" y="88.277" style="fill:#F5F5F5;" width="170.67" height="335.45"/>
|
||||
<path style="fill:#FF4B55;" d="M473.655,88.273H341.333v335.448h132.322c21.177,0,38.345-17.167,38.345-38.345V126.618
|
||||
C512,105.44,494.833,88.273,473.655,88.273z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 695 B |
1509
src/lib/assets/fs/fs.json
Executable file
1509
src/lib/assets/fs/fs.json
Executable file
File diff suppressed because it is too large
Load Diff
9
src/lib/assets/jaFlag.svg
Executable file
9
src/lib/assets/jaFlag.svg
Executable file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg height="800px" width="800px" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
viewBox="0 0 512 512" xml:space="preserve">
|
||||
<path style="fill:#F5F5F5;" d="M473.655,88.275H38.345C17.167,88.275,0,105.442,0,126.62V385.38
|
||||
c0,21.177,17.167,38.345,38.345,38.345h435.31c21.177,0,38.345-17.167,38.345-38.345V126.62
|
||||
C512,105.442,494.833,88.275,473.655,88.275z"/>
|
||||
<circle style="fill:#FF4B55;" cx="256" cy="255.999" r="97.1"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 620 B |
2
src/lib/assets/plFlag.svg
Executable file
2
src/lib/assets/plFlag.svg
Executable file
@@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg width="800px" height="800px" viewBox="0 0 36 36" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--twemoji" preserveAspectRatio="xMidYMid meet"><path fill="#EEE" d="M32 5H4a4 4 0 0 0-4 4v9h36V9a4 4 0 0 0-4-4z"></path><path fill="#DC143C" d="M0 27a4 4 0 0 0 4 4h28a4 4 0 0 0 4-4v-9H0v9z"></path></svg>
|
||||
|
After Width: | Height: | Size: 506 B |
BIN
src/lib/assets/quan.ttf
Normal file
BIN
src/lib/assets/quan.ttf
Normal file
Binary file not shown.
1
src/lib/index.ts
Normal file
1
src/lib/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
// place files you want to import through the `$lib` alias in this folder.
|
||||
152
src/lib/stores/bash/bash.ts
Normal file
152
src/lib/stores/bash/bash.ts
Normal file
@@ -0,0 +1,152 @@
|
||||
import { command } from '$app/server';
|
||||
import { COMMANDS, GROUP, HELP_ARGS, PASSWD, type CommandArg, type ICommand } from './static';
|
||||
import { VirtualFS } from './fs';
|
||||
import { Terminal, type PrintData } from '../terminal';
|
||||
import { Stack } from '../stack';
|
||||
import path from 'path';
|
||||
|
||||
export interface Permission {
|
||||
r: boolean;
|
||||
w: boolean;
|
||||
x: boolean;
|
||||
}
|
||||
|
||||
export interface BashInitArgs {
|
||||
stdio?: Terminal;
|
||||
user: User;
|
||||
fs: any;
|
||||
}
|
||||
|
||||
// TODO: Finish this
|
||||
export enum ExitCode {
|
||||
SUCCESS = 0,
|
||||
ERROR = 1
|
||||
}
|
||||
|
||||
export interface User {
|
||||
username: string;
|
||||
passwd: string; //HASHED PASSWORD
|
||||
uid: number; // Normal user 1000+ System user 1-999 root - 0
|
||||
gid: number; // Primary group | 'Users' 1000 - Others - 1000+ root - 0
|
||||
home: string;
|
||||
history: string[];
|
||||
cwd?: string[];
|
||||
pwd?: string[];
|
||||
}
|
||||
|
||||
export interface Group {
|
||||
groupname: string;
|
||||
gid: number; // Primary group 'Users' 1000 - Others - 1000+ root - 0
|
||||
members: string[];
|
||||
}
|
||||
|
||||
export class Bash {
|
||||
private vfs: VirtualFS;
|
||||
private _passwd: User[];
|
||||
private _instances: Stack<User>;
|
||||
private _group: Group[];
|
||||
private _terminal!: Terminal;
|
||||
private user: User;
|
||||
private _helpArgs: CommandArg[];
|
||||
private _commands: Record<string, ICommand>;
|
||||
|
||||
constructor(args: BashInitArgs) {
|
||||
this.user = args.user;
|
||||
this._helpArgs = HELP_ARGS;
|
||||
this._commands = COMMANDS;
|
||||
this._passwd = PASSWD;
|
||||
this._group = GROUP;
|
||||
this._terminal = args.stdio!;
|
||||
this._instances = new Stack<User>();
|
||||
|
||||
this.vfs = new VirtualFS({ fs: args.fs, user: args.user });
|
||||
|
||||
console.log(this._commands);
|
||||
}
|
||||
|
||||
updateHistory(input: string): void {
|
||||
if ((this.user.history.length = 255)) {
|
||||
this.user.history.unshift(...this.user.history.splice(-1));
|
||||
this.user.history[0] = input;
|
||||
} else {
|
||||
this.user.history.push(input);
|
||||
this.user.history.unshift(...this.user.history.splice(-1));
|
||||
}
|
||||
}
|
||||
|
||||
getCwd(): string[] {
|
||||
return this.vfs.cwd;
|
||||
}
|
||||
|
||||
getPwd(): string[] {
|
||||
return this.vfs.pwd;
|
||||
}
|
||||
|
||||
getUser(): User {
|
||||
return this.user;
|
||||
}
|
||||
|
||||
getFs(): VirtualFS {
|
||||
return this.vfs;
|
||||
}
|
||||
|
||||
changeUser(user: User) {
|
||||
this.user = user;
|
||||
this.vfs.home = this.vfs._splitPathString(user.home);
|
||||
this.vfs.cwd = user.cwd ? user.cwd : this.vfs._splitPathString(user.home);
|
||||
this.vfs.pwd = user.pwd ? user.pwd : this.vfs._splitPathString(user.home);
|
||||
}
|
||||
|
||||
executeCommand(commandName: string, ...args: string[]): void {
|
||||
const command = this._commands[commandName];
|
||||
if (!command) this.throwError(ExitCode.ERROR);
|
||||
if (command.root) {
|
||||
if (this._group[1].members.includes(this.user.username)) {
|
||||
let out: ExitCode = command.method.call(this, ...args);
|
||||
this.throwError(out);
|
||||
}
|
||||
this.throwError(ExitCode.ERROR);
|
||||
}
|
||||
|
||||
let out: ExitCode = command.method.call(this, ...args);
|
||||
this.throwError(out);
|
||||
}
|
||||
|
||||
throwError(code: ExitCode, data?: any): void {
|
||||
//TODO: Make data some interface format or smh.
|
||||
switch (code) {
|
||||
default:
|
||||
this.appendNewResult(this.vfs.pwd, 'Success!');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
userLogin(username: string, passwd: string): ExitCode {
|
||||
const user: User | undefined = this._passwd.find((u) => u.username === username);
|
||||
if (user === undefined) return ExitCode.ERROR;
|
||||
|
||||
if (user.passwd === passwd) {
|
||||
this._instances.push(user);
|
||||
this.changeUser(user);
|
||||
return ExitCode.ERROR; //TODO: Make it return the exitcode of changeUser() if needed
|
||||
} else return ExitCode.ERROR;
|
||||
}
|
||||
|
||||
userLogout() {
|
||||
this._instances.pop();
|
||||
if (this._instances.size() === 0) {
|
||||
//TODO: Implement system logout
|
||||
} else {
|
||||
this.changeUser(this._instances.peek()!);
|
||||
}
|
||||
}
|
||||
|
||||
appendNewResult(path: string[], output: any) {
|
||||
const data: PrintData = {
|
||||
path: this.vfs.formatPath(this.vfs.pathArrayToString(path)),
|
||||
output: output
|
||||
};
|
||||
console.log('NEW RESULT - ', data);
|
||||
this._terminal.PrintOutput(data);
|
||||
}
|
||||
}
|
||||
130
src/lib/stores/bash/fs.ts
Normal file
130
src/lib/stores/bash/fs.ts
Normal file
@@ -0,0 +1,130 @@
|
||||
import type { Permission, User } from './bash';
|
||||
|
||||
export enum Type {
|
||||
Directory = 16384,
|
||||
File = 32768
|
||||
}
|
||||
|
||||
type NodePerms = {
|
||||
user: Permission;
|
||||
group: Permission;
|
||||
other: Permission;
|
||||
};
|
||||
|
||||
export interface FsInitArgs {
|
||||
fs: any;
|
||||
user: User;
|
||||
}
|
||||
|
||||
export interface TreeNode {
|
||||
name: string;
|
||||
type: Type;
|
||||
readonly: boolean;
|
||||
interactible: boolean;
|
||||
func: any;
|
||||
children: TreeNode[];
|
||||
content: string; // Path to the content of the file
|
||||
link: string[]; // Symlink
|
||||
permission: NodePerms;
|
||||
owner: string;
|
||||
group: string;
|
||||
modtime: Date;
|
||||
}
|
||||
|
||||
export class VirtualFS {
|
||||
private root: TreeNode; // TODO make this the correct type
|
||||
|
||||
home: string[];
|
||||
cwd: string[];
|
||||
pwd: string[];
|
||||
|
||||
constructor(args: FsInitArgs) {
|
||||
this.root = args.fs;
|
||||
this.home = this._splitPathString(args.user.home);
|
||||
this.cwd = args.user.cwd ? args.user.cwd : this.home;
|
||||
this.pwd = args.user.pwd ? args.user.pwd : this.cwd;
|
||||
|
||||
console.log(this.home);
|
||||
console.log(this.cwd);
|
||||
console.log(this.pwd);
|
||||
|
||||
console.log('VFS INIT ', this._getNodeByPathArray(['/', 'home', 'kamil']));
|
||||
}
|
||||
|
||||
_splitPathString(path: string): string[] {
|
||||
if (path === '/') return ['/'];
|
||||
|
||||
const raw: string[] = path.split('/');
|
||||
const parts: string[] = [];
|
||||
|
||||
for (let i = 0; i < raw.length; i++) {
|
||||
if (raw[i].length > 0) parts.push(raw[i]);
|
||||
}
|
||||
return parts;
|
||||
}
|
||||
|
||||
_isAbsolutePath = (path: string): boolean => {
|
||||
return typeof path === 'string' && path.startsWith('/');
|
||||
};
|
||||
|
||||
pathArrayToString(path: string[]): string {
|
||||
if (path.length === 1 && path[0] === '/') return '/';
|
||||
return '/' + path.join('/');
|
||||
}
|
||||
|
||||
formatPath(path: string): string {
|
||||
console.log('FORMAT PATH ', path);
|
||||
const prefix = this.pathArrayToString(this.home);
|
||||
if (path.startsWith(prefix)) {
|
||||
return path.replace(prefix, '~');
|
||||
} else return path;
|
||||
}
|
||||
|
||||
resolvePath(path: string): string[] {
|
||||
if (path === '' || path === undefined || path === null) return this.cwd.slice();
|
||||
if (path.startsWith('/') && path.length === 1) return [];
|
||||
|
||||
if (path.startsWith('~')) {
|
||||
const trail: string = path === '~' ? '' : path.slice(1);
|
||||
const home: string = this.pathArrayToString(this.home);
|
||||
path = home + (trail ? (trail.startsWith('/') ? '' : '/') + trail : '');
|
||||
}
|
||||
|
||||
const start = this._isAbsolutePath(path) ? [] : this.cwd.slice();
|
||||
console.log('START', start);
|
||||
const parts = this._splitPathString(path);
|
||||
console.log('PARTS', parts);
|
||||
|
||||
for (let i = 0; i < parts.length; i++) {
|
||||
const seg = parts[i];
|
||||
|
||||
if (seg === '.' || seg === '') continue;
|
||||
if (seg === '..') {
|
||||
if (start.length > 1) start.pop();
|
||||
continue;
|
||||
}
|
||||
start.push(seg);
|
||||
}
|
||||
|
||||
if (start.length === 0) return [];
|
||||
console.log('OUTPUT', start);
|
||||
return start;
|
||||
}
|
||||
|
||||
_getNodeByPathArray(path: string[]): TreeNode {
|
||||
if (path.length === 1 && path[0] === '/') return this.root;
|
||||
|
||||
let node: TreeNode = this.root;
|
||||
const parts: string[] = path.slice(path[0] === '/' ? 1 : 0);
|
||||
|
||||
for (let i = 0; i < parts.length; i++) {
|
||||
const seg: string = parts[i];
|
||||
|
||||
if (node.type === Type.File) return node;
|
||||
const newNode = node.children.find((child) => child.name === seg);
|
||||
if (newNode !== undefined) node = newNode;
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
}
|
||||
229
src/lib/stores/bash/static.ts
Normal file
229
src/lib/stores/bash/static.ts
Normal file
@@ -0,0 +1,229 @@
|
||||
import { Bash, ExitCode, type Group, type User } from './bash';
|
||||
import { Type, type TreeNode } from './fs';
|
||||
|
||||
export type CommandArg = `-${string}`;
|
||||
|
||||
export interface ICommand {
|
||||
method: (this: Bash, ...args: any[]) => ExitCode;
|
||||
args: CommandArg[] | string[] | null;
|
||||
help: string;
|
||||
root: boolean;
|
||||
}
|
||||
|
||||
export const GROUP: Group[] = [
|
||||
{
|
||||
groupname: 'sudo',
|
||||
gid: 69,
|
||||
members: ['root', 'admin']
|
||||
},
|
||||
{
|
||||
groupname: 'users',
|
||||
gid: 1000,
|
||||
members: ['admin', 'user']
|
||||
}
|
||||
];
|
||||
|
||||
export const PASSWD: User[] = [
|
||||
{
|
||||
username: 'root',
|
||||
passwd: '123',
|
||||
uid: 0,
|
||||
gid: 0,
|
||||
home: '/',
|
||||
history: [] //TODO: Delete this and declare a new history array when logging the user in.
|
||||
},
|
||||
{
|
||||
username: 'admin',
|
||||
passwd: '456',
|
||||
uid: 1001,
|
||||
gid: 1000,
|
||||
home: '/home/admin',
|
||||
history: [] //TODO: Delete this and declare a new history array when logging the user in.
|
||||
},
|
||||
{
|
||||
username: 'user',
|
||||
passwd: '789',
|
||||
uid: 1002,
|
||||
gid: 1000,
|
||||
home: '/home/user',
|
||||
history: [] //TODO: Delete this and declare a new history array when logging the user in.
|
||||
}
|
||||
];
|
||||
|
||||
export const HELP_ARGS: CommandArg[] = ['-h', '--help'];
|
||||
|
||||
export const cmd_return = function (this: Bash, ...args: string[]): ExitCode {
|
||||
return 0;
|
||||
};
|
||||
|
||||
export const cmd_cd = function (this: Bash, ...args: string[]): ExitCode {
|
||||
const path = args[0];
|
||||
let targetNode: TreeNode;
|
||||
|
||||
if (args.length > 1) return ExitCode.ERROR; // Too many args
|
||||
|
||||
// if no args cd into home dir
|
||||
|
||||
if (args.length === 0) {
|
||||
this.getFs().cwd = this.getFs().home;
|
||||
return ExitCode.SUCCESS;
|
||||
}
|
||||
|
||||
// if the arg is - cd make your current dir the prev dir and vice versa
|
||||
|
||||
if (args[0] === '-') {
|
||||
[this.getFs().cwd, this.getFs().pwd] = [this.getFs().pwd, this.getFs().cwd];
|
||||
return ExitCode.SUCCESS;
|
||||
}
|
||||
|
||||
// Change the input STRING path from relative to absolute by replacing ~ with the home directory path
|
||||
|
||||
//TODO: Change that to a global function inside fs class to parse all possible path formats????? already exists, need to verify
|
||||
|
||||
let resolvedPath = path.startsWith('~')
|
||||
? path.replace('~', this.getFs().pathArrayToString(this.getFs().home))
|
||||
: path;
|
||||
|
||||
this.getFs().pwd = this.getFs().cwd;
|
||||
targetNode = this.getFs()._getNodeByPathArray(this.getFs().resolvePath(resolvedPath)); // Conversion from STRING path to ARRAY
|
||||
|
||||
if (!targetNode) return ExitCode.ERROR;
|
||||
if (targetNode.type !== Type.Directory) return ExitCode.ERROR;
|
||||
//if () return ExitCode.ERROR; // Check for read permissions on node and user
|
||||
|
||||
this.getFs().cwd = this.getFs().resolvePath(resolvedPath); // CD was successfull, change current dir to the verified target dir
|
||||
return ExitCode.SUCCESS;
|
||||
};
|
||||
|
||||
export const COMMANDS = {
|
||||
return: {
|
||||
method: cmd_return,
|
||||
args: [] as CommandArg[],
|
||||
help: 'PATH TO HELP.MD',
|
||||
root: false
|
||||
},
|
||||
cd: {
|
||||
method: cmd_cd,
|
||||
args: [] as string[],
|
||||
help: 'PATH TO HELP.MD',
|
||||
root: false
|
||||
}
|
||||
} satisfies Record<string, ICommand>;
|
||||
|
||||
/* //export const commands {
|
||||
return: {
|
||||
method: this.cmd_return,
|
||||
flags: [],
|
||||
help: "Help about this command",
|
||||
},
|
||||
ls: {
|
||||
method: this.cmd_ls,
|
||||
flags: [],
|
||||
help: "./help/ls.md",
|
||||
},
|
||||
echo: {
|
||||
method: this.cmd_echo,
|
||||
flags: [],
|
||||
help: "",
|
||||
},
|
||||
touch: {
|
||||
method: this.cmd_touch,
|
||||
flags: [],
|
||||
help: "",
|
||||
},
|
||||
mkdir: {
|
||||
method: this.cmd_mkdir,
|
||||
flags: [],
|
||||
help: "",
|
||||
},
|
||||
pwd: {
|
||||
method: this.cmd_pwd,
|
||||
flags: [],
|
||||
help: "",
|
||||
},
|
||||
cd: {
|
||||
method: this.cmd_cd,
|
||||
flags: [],
|
||||
help: "",
|
||||
},
|
||||
exit: {
|
||||
method: this.cmd_exit,
|
||||
flags: [],
|
||||
help: "",
|
||||
},
|
||||
cp: {
|
||||
method: this.cmd_cp,
|
||||
flags: [],
|
||||
help: "",
|
||||
},
|
||||
mv: {
|
||||
method: this.cmd_mv,
|
||||
flags: [],
|
||||
help: "",
|
||||
},
|
||||
rmdir: {
|
||||
method: this.cmd_rmdir,
|
||||
flags: [],
|
||||
help: "",
|
||||
},
|
||||
cat: {
|
||||
method: this.cmd_cat,
|
||||
flags: [],
|
||||
help: "",
|
||||
},
|
||||
dir: {
|
||||
method: this.cmd_dir,
|
||||
flags: [],
|
||||
help: "",
|
||||
},
|
||||
less: {
|
||||
method: this.cmd_less,
|
||||
flags: [],
|
||||
help: "",
|
||||
},
|
||||
chown: {
|
||||
method: this.cmd_chown,
|
||||
flags: [],
|
||||
help: "",
|
||||
},
|
||||
chmod: {
|
||||
method: this.cmd_chmod,
|
||||
flags: [],
|
||||
help: "",
|
||||
},
|
||||
reboot: {
|
||||
method: this.cmd_reboot,
|
||||
flags: [],
|
||||
help: "",
|
||||
},
|
||||
help: {
|
||||
method: this.cmd_help,
|
||||
flags: [],
|
||||
help: "",
|
||||
},
|
||||
whoami: {
|
||||
method: this.cmd_whoami,
|
||||
flags: [],
|
||||
help: "",
|
||||
},
|
||||
rm: {
|
||||
method: this.cmd_rm,
|
||||
flags: [],
|
||||
help: "",
|
||||
},
|
||||
sudo: {
|
||||
method: this.cmd_sudo,
|
||||
flags: [],
|
||||
help: "",
|
||||
},
|
||||
su: {
|
||||
method: this.cmd_su,
|
||||
flags: [],
|
||||
help: "",
|
||||
},
|
||||
clear: {
|
||||
method: this.cmd_clear,
|
||||
flags: [],
|
||||
help: "",
|
||||
}
|
||||
} */
|
||||
13
src/lib/stores/lang.ts
Normal file
13
src/lib/stores/lang.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import plFlag from '$lib/assets/plFlag.svg';
|
||||
import enFlag from '$lib/assets/enFlag.svg';
|
||||
import deFlag from '$lib/assets/deFlag.svg';
|
||||
import frFlag from '$lib/assets/frFlag.svg';
|
||||
import jaFlag from '$lib/assets/jaFlag.svg';
|
||||
|
||||
export const langs = {
|
||||
pl: {},
|
||||
en: {},
|
||||
de: {},
|
||||
ja: {},
|
||||
fr: {}
|
||||
};
|
||||
31
src/lib/stores/stack.ts
Normal file
31
src/lib/stores/stack.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
export class Stack<T> {
|
||||
private items: T[] = [];
|
||||
|
||||
push(element: T): void {
|
||||
this.items.push(element);
|
||||
}
|
||||
|
||||
pop(): T | undefined {
|
||||
return this.items.pop();
|
||||
}
|
||||
|
||||
peek(): T | undefined {
|
||||
return this.items[this.items.length - 1];
|
||||
}
|
||||
|
||||
isEmpty(): boolean {
|
||||
return this.items.length === 0;
|
||||
}
|
||||
|
||||
size(): number {
|
||||
return this.items.length;
|
||||
}
|
||||
|
||||
clear(): void {
|
||||
this.items = [];
|
||||
}
|
||||
|
||||
toArray(): T[] {
|
||||
return [...this.items];
|
||||
}
|
||||
}
|
||||
95
src/lib/stores/terminal.ts
Normal file
95
src/lib/stores/terminal.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
import { FileOutput } from '@lucide/svelte';
|
||||
import Cursor from '../../modules/terminal/Cursor.svelte';
|
||||
import { Bash, ExitCode, type BashInitArgs, type User } from './bash/bash';
|
||||
import { Stack } from './stack';
|
||||
import type { VirtualFS } from './bash/fs';
|
||||
|
||||
export interface TerminalMode {}
|
||||
|
||||
export interface TermInitArgs {
|
||||
bash: BashInitArgs;
|
||||
}
|
||||
|
||||
export interface ParsedInput {
|
||||
command: string;
|
||||
args: string[];
|
||||
}
|
||||
|
||||
export interface PrintData {
|
||||
path: string;
|
||||
output: any; // TODO: Make this be any predefined format of outputs like ls, ls w/ flags and so on;
|
||||
}
|
||||
|
||||
export interface PageCallbacks {
|
||||
print: (data: PrintData) => void;
|
||||
}
|
||||
|
||||
export class Terminal {
|
||||
private bash: Bash;
|
||||
private callbacks: Partial<PageCallbacks> = {};
|
||||
|
||||
constructor(args: TermInitArgs) {
|
||||
args.bash.stdio = this;
|
||||
this.bash = new Bash(args.bash);
|
||||
}
|
||||
|
||||
private _parseInput(input: string): ParsedInput {
|
||||
const result: ParsedInput = { command: '', args: [] };
|
||||
let current: string = '';
|
||||
let inQuotes: boolean = false;
|
||||
let quoteChar: Stack<string> = new Stack<string>();
|
||||
|
||||
for (let i = 0; i < input.length; i++) {
|
||||
const char = input[i];
|
||||
|
||||
if ((char === '"' || char === "'") && !inQuotes) {
|
||||
inQuotes = true;
|
||||
quoteChar.push(char);
|
||||
continue;
|
||||
} else if (char === quoteChar.peek() && inQuotes) {
|
||||
inQuotes = false;
|
||||
quoteChar.pop();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (char === ' ' && !inQuotes) {
|
||||
if (current !== '') {
|
||||
result.command = current;
|
||||
current = '';
|
||||
}
|
||||
} else {
|
||||
current += char;
|
||||
}
|
||||
}
|
||||
if (current !== '') result.args.push(current);
|
||||
return result;
|
||||
}
|
||||
|
||||
executeCommand(input: string): void {
|
||||
this.bash.updateHistory(input);
|
||||
const parsed: ParsedInput = this._parseInput(input);
|
||||
this.bash.executeCommand(parsed.command, ...parsed.args);
|
||||
}
|
||||
|
||||
registerCallbacks(callbacks: PageCallbacks): void {
|
||||
this.callbacks = callbacks;
|
||||
}
|
||||
|
||||
getUser(): User {
|
||||
return this.bash.getUser();
|
||||
}
|
||||
|
||||
getCwd(): string {
|
||||
const fs: VirtualFS = this.bash.getFs();
|
||||
let temp: string = fs.formatPath(fs.pathArrayToString(this.bash.getCwd()));
|
||||
return temp;
|
||||
}
|
||||
|
||||
userLogin(username: string, passwd: string): ExitCode {
|
||||
return this.bash.userLogin(username, passwd);
|
||||
}
|
||||
|
||||
PrintOutput(data: PrintData) {
|
||||
this.callbacks.print?.(data);
|
||||
}
|
||||
}
|
||||
17
src/lib/stores/theme.ts
Normal file
17
src/lib/stores/theme.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { writable } from 'svelte/store';
|
||||
|
||||
function getInitalTheme(): string {
|
||||
if (typeof window === 'undefined') return 'dark';
|
||||
|
||||
const savedTheme: string | null = localStorage.getItem('theme');
|
||||
if (savedTheme === 'dark' || savedTheme === 'light') return savedTheme;
|
||||
|
||||
const sysPrefTheme = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
return sysPrefTheme ? 'dark' : 'light';
|
||||
}
|
||||
|
||||
export const theme = writable(getInitalTheme());
|
||||
|
||||
theme.subscribe((value) => {
|
||||
if (typeof window !== 'undefined') localStorage.setItem('theme', value);
|
||||
});
|
||||
55
src/modules/Footer.svelte
Normal file
55
src/modules/Footer.svelte
Normal file
@@ -0,0 +1,55 @@
|
||||
<script lang="ts">
|
||||
import { Languages, SunMoon } from '@lucide/svelte';
|
||||
import { theme } from '$lib/stores/theme';
|
||||
import plFlag from '$lib/assets/plFlag.svg';
|
||||
import enFlag from '$lib/assets/enFlag.svg';
|
||||
</script>
|
||||
|
||||
<div
|
||||
id="footer"
|
||||
class="fixed bottom-0 flex w-max flex-row-reverse items-center justify-between p-2"
|
||||
>
|
||||
<div class="footer-child flex flex-row-reverse content-start items-center" id="footer-lang-child">
|
||||
<button
|
||||
type="button"
|
||||
id="lang-switch"
|
||||
class="button rounded-lg bg-bg text-primary hover:text-primary-hover"
|
||||
>
|
||||
<Languages class="m-2.5 size-10" />
|
||||
</button>
|
||||
<div id="lang-wrapper" class="">
|
||||
<div id="pl" class="lang visible">
|
||||
<img class="flags" src={plFlag} alt="PL" height="30" width="30" />
|
||||
</div>
|
||||
<div id="en" class="lang">
|
||||
<img class="flags" src={enFlag} alt="EN" height="30" width="30" />
|
||||
</div>
|
||||
<!-- <div id="ja" class="lang"><img class="flags" src="/images/japan-svgrepo-com.svg" alt="JA" height="30" width="30"/></div>
|
||||
<div id="de" class="lang"><img class="flags" src="/images/germany-svgrepo-com.svg" alt="DE" height="30" width="30"/></div>
|
||||
<div id="fr" class="lang"><img class="flags" src="/images/france-svgrepo-com.svg" alt="FR" height="30" width="30"/></div> -->
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="footer-child flex flex-row content-start items-center bg-bg text-primary hover:text-primary-hover"
|
||||
id="footer-theme-child"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
id="theme-switch"
|
||||
class="button rounded-lg"
|
||||
onclick={() => {
|
||||
$theme = $theme === 'dark' ? 'light' : 'dark';
|
||||
}}
|
||||
>
|
||||
<SunMoon class="m-2.5 size-10" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.button {
|
||||
transition: var(--transition-standard);
|
||||
padding: 0;
|
||||
border: none;
|
||||
}
|
||||
</style>
|
||||
57
src/modules/Loading.svelte
Normal file
57
src/modules/Loading.svelte
Normal file
@@ -0,0 +1,57 @@
|
||||
<div
|
||||
id="loader-master"
|
||||
class=" light:bg-dots-bg-2-light pointer-events-none visible absolute z-49 size-full bg-bg-dark select-none"
|
||||
>
|
||||
<div id="loader" class="tran absolute top-1/2 left-1/2 z-50 -translate-x-1/2 -translate-y-1/2">
|
||||
<div id="bar-wrapper" class="flex h-full flex-row items-center gap-2">
|
||||
<div
|
||||
class="bar h-10 w-2 origin-center rounded-[3px] bg-loader-primary-dark light:bg-loader-primary-light"
|
||||
></div>
|
||||
<div
|
||||
class="bar h-10 w-2 origin-center rounded-[3px] bg-loader-primary-dark light:bg-loader-primary-light"
|
||||
></div>
|
||||
<div
|
||||
class="bar h-10 w-2 origin-center rounded-[3px] bg-loader-primary-dark light:bg-loader-primary-light"
|
||||
></div>
|
||||
<div
|
||||
class="bar h-10 w-2 origin-center rounded-[3px] bg-loader-primary-dark light:bg-loader-primary-light"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
#loader-master {
|
||||
transition: all 0.3s cubic-bezier(0.41, 0.68, 0.45, 0.96);
|
||||
}
|
||||
|
||||
.bar {
|
||||
animation: pulse 1s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.bar:nth-child(1) {
|
||||
animation-delay: 0s;
|
||||
}
|
||||
.bar:nth-child(2) {
|
||||
animation-delay: 0.1s;
|
||||
}
|
||||
.bar:nth-child(3) {
|
||||
animation-delay: 0.2s;
|
||||
}
|
||||
.bar:nth-child(4) {
|
||||
animation-delay: 0.3s;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0% {
|
||||
transform: scaleY(1);
|
||||
}
|
||||
30% {
|
||||
transform: scaleY(0.35);
|
||||
background-color: oklch(0.8 0.15 292);
|
||||
}
|
||||
60% {
|
||||
transform: scaleY(1);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
4
src/modules/Panel.svelte
Normal file
4
src/modules/Panel.svelte
Normal file
@@ -0,0 +1,4 @@
|
||||
<section>
|
||||
<section></section>
|
||||
<section></section>
|
||||
</section>
|
||||
110
src/modules/Settings.svelte
Normal file
110
src/modules/Settings.svelte
Normal file
@@ -0,0 +1,110 @@
|
||||
<script lang="ts">
|
||||
import { Languages, Settings2, SunMoon } from '@lucide/svelte';
|
||||
import { theme } from '$lib/stores/theme';
|
||||
import plFlag from '$lib/assets/plFlag.svg';
|
||||
import enFlag from '$lib/assets/enFlag.svg';
|
||||
import deFlag from '$lib/assets/deFlag.svg';
|
||||
import frFlag from '$lib/assets/frFlag.svg';
|
||||
import jaFlag from '$lib/assets/jaFlag.svg';
|
||||
|
||||
function toggleSettings() {
|
||||
const settingsMenu = window.document.getElementById('settings-menu');
|
||||
const langsMenu = window.document.getElementById('langs-menu');
|
||||
langsMenu?.classList.add('hide');
|
||||
settingsMenu?.classList.toggle('hide');
|
||||
}
|
||||
|
||||
function toggleLangs() {
|
||||
const langsMenu = window.document.getElementById('langs-menu');
|
||||
langsMenu?.classList.toggle('hide');
|
||||
}
|
||||
</script>
|
||||
|
||||
<div
|
||||
id="options"
|
||||
class="absolute top-0 left-0 isolate mx-4 rounded-b-xl bg-bg-dark light:bg-bg-light"
|
||||
>
|
||||
<section class="hide grid" id="settings-menu">
|
||||
<div class="flex flex-col items-center justify-center gap-4 overflow-hidden">
|
||||
<div class=" mt-4 flex items-center justify-center rounded-lg">
|
||||
<button
|
||||
class=" rounded-lg bg-bg-lighter-dark p-1.5 text-text-dark shadow-button duration-75 hover:text-primary-hover-dark active:translate-y-0.5 light:bg-bg-lighter-light light:text-text-muted-light light:hover:text-primary-hover-light"
|
||||
id="theme-btn"
|
||||
onclick={() => {
|
||||
$theme = $theme === 'dark' ? 'light' : 'dark';
|
||||
}}
|
||||
>
|
||||
<SunMoon strokeWidth={1} size={32} />
|
||||
</button>
|
||||
</div>
|
||||
<div class="flex flex-col content-center items-center">
|
||||
<button
|
||||
class="z-3 mb-4 rounded-lg bg-bg-lighter-dark p-1.5 text-text-dark duration-75 hover:text-primary-hover-dark active:translate-y-0.5 light:bg-bg-lighter-light light:text-text-muted-light light:hover:text-primary-hover-light"
|
||||
id="langs-btn"
|
||||
onclick={toggleLangs}
|
||||
>
|
||||
<Languages strokeWidth={1} size={32} />
|
||||
</button>
|
||||
<form id="langs-menu" class="hide grid">
|
||||
<div class="z-2 mb-4 flex flex-col items-center gap-2 overflow-hidden">
|
||||
<label
|
||||
class="border-primary-dark duration-100 hover:ml-1 hover:pl-1 has-checked:border-l-3 has-checked:hover:m-0 has-checked:hover:p-0 light:border-primary-light"
|
||||
>
|
||||
<input name="langs" type="radio" class="lang hidden" id="pl" autocomplete="off" />
|
||||
<img class="flags mx-2" src={plFlag} alt="PL" height="26" width="26" />
|
||||
</label>
|
||||
<label
|
||||
class="border-primary-dark duration-100 hover:ml-1 hover:pl-1 has-checked:border-l-3 has-checked:hover:m-0 has-checked:hover:p-0 light:border-primary-light"
|
||||
>
|
||||
<input name="langs" type="radio" class="lang hidden" id="en" autocomplete="off" />
|
||||
<img class="flags mx-2" src={enFlag} alt="EN" height="26" width="26" />
|
||||
</label>
|
||||
<label
|
||||
class="border-primary-dark duration-100 hover:ml-1 hover:pl-1 has-checked:border-l-3 has-checked:hover:m-0 has-checked:hover:p-0 light:border-primary-light"
|
||||
>
|
||||
<input name="langs" type="radio" class="lang hidden" id="en" autocomplete="off" />
|
||||
<img class="flags mx-2" src={deFlag} alt="DE" height="26" width="26" />
|
||||
</label>
|
||||
<label
|
||||
class="border-primary-dark duration-100 hover:ml-1 hover:pl-1 has-checked:border-l-3 has-checked:hover:m-0 has-checked:hover:p-0 light:border-primary-light"
|
||||
>
|
||||
<input name="langs" type="radio" class="lang hidden" id="en" autocomplete="off" />
|
||||
<img class="flags mx-2" src={jaFlag} alt="JA" height="26" width="26" />
|
||||
</label>
|
||||
<label
|
||||
class="border-primary-dark duration-100 hover:ml-1 hover:pl-1 has-checked:border-l-3 has-checked:hover:m-0 has-checked:hover:p-0 light:border-primary-light"
|
||||
>
|
||||
<input name="langs" type="radio" class="lang hidden" id="en" autocomplete="off" />
|
||||
<img class="flags mx-2" src={frFlag} alt="FR" height="26" width="26" />
|
||||
</label>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<button
|
||||
class="rounded-b-xl border-t bg-bg-light-dark p-2 text-text-dark shadow-button hover:text-primary-dark light:bg-bg-lighter-light light:text-text-muted-light light:hover:text-primary-light"
|
||||
id="settings-btn"
|
||||
onclick={toggleSettings}
|
||||
>
|
||||
<Settings2 strokeWidth={1} size={38} />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
#langs-menu {
|
||||
transition: 0.2s ease-in;
|
||||
grid-template-rows: 1fr;
|
||||
&.hide {
|
||||
grid-template-rows: 0fr;
|
||||
}
|
||||
}
|
||||
|
||||
#settings-menu {
|
||||
transition: 0.2s ease-in;
|
||||
grid-template-rows: 1fr;
|
||||
&.hide {
|
||||
grid-template-rows: 0fr;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
217
src/modules/Terminal.svelte
Normal file
217
src/modules/Terminal.svelte
Normal file
@@ -0,0 +1,217 @@
|
||||
<script lang="ts">
|
||||
import type { User } from '$lib/stores/bash/bash';
|
||||
import type { TreeNode } from '$lib/stores/bash/fs';
|
||||
import { Terminal, type TermInitArgs } from '$lib/stores/terminal';
|
||||
import {
|
||||
onDestroy,
|
||||
onMount,
|
||||
type Snippet,
|
||||
type Component,
|
||||
type ComponentProps,
|
||||
mount
|
||||
} from 'svelte';
|
||||
|
||||
let { children, username, cwd }: { children: Snippet; username: string; cwd: string } = $props();
|
||||
|
||||
/* function jsonToTreeNode(data: any): TreeNode {
|
||||
return {
|
||||
name: data.Name,
|
||||
type: data.Type,
|
||||
readonly: data.ReadOnly,
|
||||
interactible: data.Interactible,
|
||||
func: data.Func,
|
||||
children: data.Children ? data.Children.map((child: any) => jsonToTreeNode(child)) : [],
|
||||
content: data.Content,
|
||||
link: data.Link || [],
|
||||
permission: {
|
||||
user: {
|
||||
r: data.Permission[0]?.Read,
|
||||
w: data.Permission[0]?.Write,
|
||||
x: data.Permission[0]?.Exec
|
||||
},
|
||||
group: {
|
||||
r: data.Permission[1]?.Read,
|
||||
w: data.Permission[1]?.Write,
|
||||
x: data.Permission[1]?.Exec
|
||||
},
|
||||
other: {
|
||||
r: data.Permission[2]?.Read,
|
||||
w: data.Permission[2]?.Write,
|
||||
x: data.Permission[2]?.Exec
|
||||
}
|
||||
},
|
||||
owner: data.Owner,
|
||||
group: data.Group,
|
||||
modtime: new Date(data.Mtime)
|
||||
};
|
||||
}
|
||||
|
||||
async function fetchFileSystem(path: string): Promise<any> {
|
||||
const response = await fetch(path);
|
||||
if (!response.ok) throw new Error('Failed to fetch the file system json');
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
const node: TreeNode = jsonToTreeNode(data);
|
||||
return node;
|
||||
}
|
||||
|
||||
let callbackInit = {
|
||||
print: (data: any) => {
|
||||
console.log('print callback executed');
|
||||
//print(data);
|
||||
}
|
||||
};
|
||||
|
||||
let testUser: User = {
|
||||
username: 'kamil',
|
||||
passwd: '123',
|
||||
uid: 0,
|
||||
gid: 0,
|
||||
home: '/home/kamil',
|
||||
history: []
|
||||
};
|
||||
|
||||
let terminal: Terminal;
|
||||
let username: string = $state(testUser.username);
|
||||
let cwd: string = $state(testUser.home);
|
||||
|
||||
let isInitializing = $state(true);
|
||||
|
||||
onMount(async () => {
|
||||
try {
|
||||
let fsJson = await fetchFileSystem('/src/lib/assets/fs/fs.json');
|
||||
|
||||
let args: TermInitArgs = {
|
||||
bash: {
|
||||
user: testUser,
|
||||
fs: fsJson
|
||||
}
|
||||
};
|
||||
|
||||
terminal = new Terminal(args);
|
||||
terminal.registerCallbacks(callbackInit);
|
||||
|
||||
username = terminal.getUser().username;
|
||||
cwd = terminal.getCwd();
|
||||
} catch (error) {
|
||||
console.error('Failed to initialize terminal:', error);
|
||||
} finally {
|
||||
updateTerminal();
|
||||
isInitializing = false;
|
||||
}
|
||||
});
|
||||
|
||||
function updateTerminal() {
|
||||
username = terminal!.getUser().username;
|
||||
cwd = terminal!.getCwd();
|
||||
} */
|
||||
|
||||
let outputContainer = $state<HTMLElement>();
|
||||
let instances = $state<Set<ReturnType<typeof mount>>>(new Set());
|
||||
|
||||
onMount(() => {
|
||||
const scrollable = document.getElementById('cout');
|
||||
|
||||
const config = { childList: true };
|
||||
|
||||
const callback = function (mutationList: any, observer: any) {
|
||||
for (let mutation of mutationList) {
|
||||
if (mutation.type === 'childList') {
|
||||
scrollable?.scrollTo(0, scrollable.scrollHeight);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const observer = new MutationObserver(callback);
|
||||
observer.observe(scrollable!, config);
|
||||
});
|
||||
|
||||
export function addComponent<T extends Component>(
|
||||
component: T,
|
||||
props: ComponentProps<T> = {} as ComponentProps<T>
|
||||
): ReturnType<typeof mount> | undefined {
|
||||
if (!outputContainer) return;
|
||||
|
||||
const instance = mount(component, {
|
||||
target: outputContainer,
|
||||
props
|
||||
});
|
||||
|
||||
instances.add(instance);
|
||||
return instance;
|
||||
}
|
||||
|
||||
export function clearComponents(): void {
|
||||
for (const instance of instances) {
|
||||
instance.$destroy();
|
||||
}
|
||||
instances.clear();
|
||||
}
|
||||
|
||||
onDestroy(() => {
|
||||
for (const instance of instances) {
|
||||
instance.$destroy();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="terminal" class="terminal-window shadow-() size-full rounded-md shadow-bg">
|
||||
<div
|
||||
class="terminal-bar flex h-9 w-full flex-row items-center rounded-t-md bg-bg-dark text-center font-terminal text-sm font-bold text-primary-dark light:bg-bg-dark-light light:text-primary-light"
|
||||
>
|
||||
<div class="dots-wrapper mx-2.5 flex h-full flex-row items-center justify-center gap-2.5">
|
||||
<button class="size-2.5 cursor-pointer rounded-full p-0" title=""></button>
|
||||
<button class="size-2.5 cursor-pointer rounded-full p-0" title=""></button>
|
||||
<button class="size-2.5 cursor-pointer rounded-full p-0" title=""></button>
|
||||
</div>
|
||||
<div class=" flex">
|
||||
<h5>{username}</h5>
|
||||
<!-- prettier-ignore -->
|
||||
<h5 class=" mr-2">@terminal: </h5>
|
||||
<h5>{cwd}</h5>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="inner-content scroll-hidden h-[860px] origin-top overflow-y-auto rounded-b-md bg-bg-light-dark p-4 text-text-dark shadow-subtle light:bg-bg-lighter-light light:text-text-light"
|
||||
id="cout"
|
||||
>
|
||||
<div bind:this={outputContainer}></div>
|
||||
{@render children()}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
* {
|
||||
transition: var(--transition-standard);
|
||||
}
|
||||
.dots-wrapper {
|
||||
& > button {
|
||||
border: none;
|
||||
&:hover {
|
||||
transform: translateY(-0.1rem);
|
||||
}
|
||||
&:active {
|
||||
transform: translate(0);
|
||||
}
|
||||
&:nth-child(1) {
|
||||
background-color: rgb(255, 0, 0);
|
||||
&:active {
|
||||
background-color: rgb(255, 100, 100);
|
||||
}
|
||||
}
|
||||
&:nth-child(2) {
|
||||
background-color: rgb(255, 165, 0);
|
||||
&:active {
|
||||
background-color: rgb(255, 215, 50);
|
||||
}
|
||||
}
|
||||
&:nth-child(3) {
|
||||
background-color: rgb(50, 205, 50);
|
||||
&:active {
|
||||
background-color: rgb(100, 255, 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
36
src/modules/panel/LangModule.svelte
Normal file
36
src/modules/panel/LangModule.svelte
Normal file
@@ -0,0 +1,36 @@
|
||||
<script lang="ts">
|
||||
import { SquarePen } from '@lucide/svelte';
|
||||
|
||||
let {
|
||||
langName,
|
||||
icon,
|
||||
checked = false
|
||||
}: { langName: string; icon: string; checked?: boolean } = $props();
|
||||
</script>
|
||||
|
||||
<div
|
||||
class=" flex items-center text-text-dark saturate-50 has-checked:saturate-100 light:text-text-light"
|
||||
>
|
||||
<div class=" flex w-full flex-col justify-center gap-1 rounded-lg bg-bg-mid-dark">
|
||||
<div class="flex items-center gap-2">
|
||||
<button
|
||||
class="m-2 mr-1 rounded-md bg-bg-lighter-dark p-1 shadow-subtle hover:text-primary-hover-dark active:text-primary-dark"
|
||||
>
|
||||
<SquarePen size={24} strokeWidth={1} />
|
||||
</button>
|
||||
<img alt="" width="32" height="32" src={icon} />
|
||||
<h5 class=" mr-2 font-primary font-bold">{langName}</h5>
|
||||
<label
|
||||
for={langName}
|
||||
class="relative m-2 ml-auto block h-5 w-10 cursor-pointer rounded-full bg-bg-lighter-dark shadow-subtle"
|
||||
>
|
||||
<input type="checkbox" {checked} id={langName} class=" peer sr-only" />
|
||||
<span
|
||||
class=" absolute top-0.5 left-0.5 h-4/5 w-2/5 rounded-full bg-bg-dark peer-checked:left-5.5 peer-checked:bg-primary-dark"
|
||||
></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class=" flex items-center justify-center rounded-r-lg bg-bg-dark"></div>
|
||||
</div>
|
||||
26
src/modules/panel/ProjectModule.svelte
Normal file
26
src/modules/panel/ProjectModule.svelte
Normal file
@@ -0,0 +1,26 @@
|
||||
<script lang="ts">
|
||||
import { ChevronRight, Power, SquarePen } from '@lucide/svelte';
|
||||
|
||||
let { projectName, checked = true }: { projectName: string; checked?: boolean } = $props();
|
||||
</script>
|
||||
|
||||
<div
|
||||
class=" flex items-center text-text-dark saturate-50 has-checked:saturate-100 light:text-text-light"
|
||||
>
|
||||
<div class=" flex w-full flex-col justify-center gap-1 rounded-lg bg-bg-mid-dark">
|
||||
<h5 class=" m-4 font-primary text-xl font-bold">{projectName}</h5>
|
||||
<div class="flex items-center gap-2">
|
||||
<label
|
||||
for={projectName}
|
||||
class="relative m-2 ml-auto block h-5 w-10 cursor-pointer rounded-full bg-bg-lighter-dark shadow-subtle"
|
||||
>
|
||||
<input type="checkbox" {checked} id={projectName} class=" peer sr-only" />
|
||||
<span
|
||||
class=" absolute top-0.5 left-0.5 h-4/5 w-2/5 rounded-full bg-bg-dark peer-checked:left-5.5 peer-checked:bg-primary-dark"
|
||||
></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class=" flex items-center justify-center rounded-r-lg bg-bg-dark"></div>
|
||||
</div>
|
||||
29
src/modules/panel/SidebarButton.svelte
Normal file
29
src/modules/panel/SidebarButton.svelte
Normal file
@@ -0,0 +1,29 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import { onMount, type Snippet } from 'svelte';
|
||||
|
||||
let {
|
||||
children,
|
||||
path = '/',
|
||||
checked = false
|
||||
}: { children: Snippet; path?: string; checked?: boolean } = $props();
|
||||
|
||||
onMount(() => {
|
||||
if (checked) goto(path);
|
||||
});
|
||||
</script>
|
||||
|
||||
<label
|
||||
class="m-0 flex size-12 items-center justify-center rounded-xl bg-bg-lighter-dark shadow-subtle hover:text-primary-hover-dark has-checked:text-primary-dark has-checked:hover:text-primary-hover-dark light:bg-bg-light-light light:hover:text-primary-hover-light light:has-checked:text-primary-light light:has-checked:hover:text-primary-hover-light"
|
||||
>
|
||||
<input
|
||||
onclick={() => goto(path)}
|
||||
name="langs"
|
||||
type="radio"
|
||||
class="hidden"
|
||||
id="pl"
|
||||
autocomplete="off"
|
||||
{checked}
|
||||
/>
|
||||
{@render children()}
|
||||
</label>
|
||||
10
src/modules/terminal/Cursor.svelte
Normal file
10
src/modules/terminal/Cursor.svelte
Normal file
@@ -0,0 +1,10 @@
|
||||
<script lang="ts">
|
||||
let { path, output }: { path: string; output: any } = $props(); //TODO: change any to matching
|
||||
</script>
|
||||
|
||||
<p class="cwd" id="cwd">{path}</p>
|
||||
<div class="pointer-wrapper mr-4 mb-2.5 flex flex-row items-center font-terminal">
|
||||
<span class="pointer pr-2">$</span>
|
||||
<!-- prettier-ignore -->
|
||||
<div style="white-space: preserve;" class=" relative wrap-break-word">{output}</div>
|
||||
</div>
|
||||
50
src/modules/terminal/Input.svelte
Normal file
50
src/modules/terminal/Input.svelte
Normal file
@@ -0,0 +1,50 @@
|
||||
<script lang="ts">
|
||||
let inputElement = $state<HTMLInputElement>();
|
||||
|
||||
let { value, isFocused, cwd }: { value?: string; isFocused?: boolean; cwd: string } = $props();
|
||||
|
||||
export function focus() {
|
||||
console.log('WOOOOW');
|
||||
inputElement?.focus();
|
||||
}
|
||||
|
||||
export function blur() {
|
||||
console.log('beeeeeeeee');
|
||||
inputElement?.blur();
|
||||
}
|
||||
|
||||
export function setValue(newValue: string) {
|
||||
if (inputElement) {
|
||||
inputElement.value = newValue;
|
||||
}
|
||||
}
|
||||
|
||||
export function clear() {
|
||||
setValue('');
|
||||
}
|
||||
|
||||
function handleInput(event: Event) {
|
||||
value = (event.target as HTMLInputElement).value;
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class=" relative">
|
||||
<input
|
||||
bind:this={inputElement}
|
||||
oninput={handleInput}
|
||||
onfocus={() => (isFocused = true)}
|
||||
onblur={() => (isFocused = false)}
|
||||
type="text"
|
||||
class=" pointer-events-none absolute left-0 m-0 w-0 border-none p-0 opacity-0"
|
||||
/>
|
||||
<p class="cwd" id="cwd">{cwd}</p>
|
||||
<div class="w flex-column flex flex-row flex-wrap font-terminal">
|
||||
<span class="pointer pr-2">$</span>
|
||||
<!-- prettier-ignore -->
|
||||
<div style="white-space: preserve;" class=" relative wrap-break-word">{value}</div>
|
||||
<span id="cursor" class={isFocused ? 'animate-cursor-blink' : ''}>_</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
54
src/routes/+layout.svelte
Normal file
54
src/routes/+layout.svelte
Normal file
@@ -0,0 +1,54 @@
|
||||
<script lang="ts">
|
||||
import '../app.css';
|
||||
import favicon from '$lib/assets/favicon.svg';
|
||||
import { onMount, type Snippet } from 'svelte';
|
||||
import { theme } from '$lib/stores/theme';
|
||||
|
||||
let { children }: { children: Snippet } = $props();
|
||||
|
||||
let bg1: string | null;
|
||||
let bg2: string | null;
|
||||
|
||||
onMount(() => {
|
||||
const style = window.getComputedStyle(document.documentElement);
|
||||
|
||||
bg1 = style.getPropertyValue('--dots-bg-1');
|
||||
bg2 = style.getPropertyValue('--dots-bg-2');
|
||||
|
||||
if (bg2 && bg1) {
|
||||
window.CSS.registerProperty({
|
||||
name: '--dots-bg-2',
|
||||
syntax: '<color>',
|
||||
inherits: true,
|
||||
initialValue: bg2
|
||||
});
|
||||
|
||||
window.CSS.registerProperty({
|
||||
name: '--dots-bg-1',
|
||||
syntax: '<color>',
|
||||
inherits: true,
|
||||
initialValue: bg1
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<link rel="icon" href={favicon} />
|
||||
</svelte:head>
|
||||
|
||||
<main
|
||||
class="text-text m-auto flex min-h-dvh w-full flex-col items-center justify-center overflow-hidden bg-dots-bg bg-size-[20px_20px]"
|
||||
class:dark={$theme === 'dark'}
|
||||
class:light={$theme === 'light'}
|
||||
>
|
||||
{@render children()}
|
||||
</main>
|
||||
|
||||
<style>
|
||||
main {
|
||||
transition:
|
||||
--dots-bg-2 0.2s ease-in,
|
||||
--dots-bg-1 0.2s ease-in;
|
||||
}
|
||||
</style>
|
||||
166
src/routes/+page.svelte
Normal file
166
src/routes/+page.svelte
Normal file
@@ -0,0 +1,166 @@
|
||||
<script lang="ts">
|
||||
import TerminalModule from '../modules/Terminal.svelte';
|
||||
|
||||
import { Terminal, type PrintData, type TermInitArgs } from '$lib/stores/terminal';
|
||||
import type { User } from '$lib/stores/bash/bash';
|
||||
import { onMount } from 'svelte';
|
||||
import Settings from '../modules/Settings.svelte';
|
||||
import Loading from '../modules/Loading.svelte';
|
||||
import type { TreeNode } from '$lib/stores/bash/fs';
|
||||
import Input from '../modules/terminal/Input.svelte';
|
||||
import Cursor from '../modules/terminal/Cursor.svelte';
|
||||
|
||||
//let terminalMode =
|
||||
|
||||
function jsonToTreeNode(data: any): TreeNode {
|
||||
return {
|
||||
name: data.Name,
|
||||
type: data.Type,
|
||||
readonly: data.ReadOnly,
|
||||
interactible: data.Interactible,
|
||||
func: data.Func,
|
||||
children: data.Children ? data.Children.map((child: any) => jsonToTreeNode(child)) : [],
|
||||
content: data.Content,
|
||||
link: data.Link || [],
|
||||
permission: {
|
||||
user: {
|
||||
r: data.Permission[0]?.Read,
|
||||
w: data.Permission[0]?.Write,
|
||||
x: data.Permission[0]?.Exec
|
||||
},
|
||||
group: {
|
||||
r: data.Permission[1]?.Read,
|
||||
w: data.Permission[1]?.Write,
|
||||
x: data.Permission[1]?.Exec
|
||||
},
|
||||
other: {
|
||||
r: data.Permission[2]?.Read,
|
||||
w: data.Permission[2]?.Write,
|
||||
x: data.Permission[2]?.Exec
|
||||
}
|
||||
},
|
||||
owner: data.Owner,
|
||||
group: data.Group,
|
||||
modtime: new Date(data.Mtime)
|
||||
};
|
||||
}
|
||||
|
||||
async function fetchFileSystem(path: string): Promise<any> {
|
||||
const response = await fetch(path);
|
||||
if (!response.ok) throw new Error('Failed to fetch the file system json');
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
const node: TreeNode = jsonToTreeNode(data);
|
||||
return node;
|
||||
}
|
||||
|
||||
let callbackInit = {
|
||||
print: (data: any) => {
|
||||
console.log('print callback executed');
|
||||
print(data);
|
||||
}
|
||||
};
|
||||
|
||||
let testUser: User = {
|
||||
username: 'kamil',
|
||||
passwd: '123',
|
||||
uid: 0,
|
||||
gid: 0,
|
||||
home: '/home/kamil',
|
||||
history: []
|
||||
};
|
||||
|
||||
let terminal: Terminal;
|
||||
let username: string = $state(testUser.username);
|
||||
let cwd: string = $state(testUser.home);
|
||||
|
||||
let isInitializing = $state(true);
|
||||
|
||||
onMount(async () => {
|
||||
try {
|
||||
let fsJson = await fetchFileSystem('/src/lib/assets/fs/fs.json');
|
||||
|
||||
let args: TermInitArgs = {
|
||||
bash: {
|
||||
user: testUser,
|
||||
fs: fsJson
|
||||
}
|
||||
};
|
||||
|
||||
terminal = new Terminal(args);
|
||||
terminal.registerCallbacks(callbackInit);
|
||||
|
||||
username = terminal.getUser().username;
|
||||
cwd = terminal.getCwd();
|
||||
} catch (error) {
|
||||
console.error('Failed to initialize terminal:', error);
|
||||
} finally {
|
||||
updateTerminal();
|
||||
isInitializing = false;
|
||||
}
|
||||
});
|
||||
|
||||
function updateTerminal() {
|
||||
username = terminal!.getUser().username;
|
||||
cwd = terminal!.getCwd();
|
||||
}
|
||||
|
||||
let terminalComponent = $state<any>();
|
||||
|
||||
let inputComponent = $state<any>();
|
||||
|
||||
function focusInput() {
|
||||
console.log('focus');
|
||||
inputComponent.focus();
|
||||
}
|
||||
|
||||
function blurInput() {
|
||||
console.log('blur');
|
||||
inputComponent.blur();
|
||||
}
|
||||
|
||||
function clearInput() {
|
||||
inputComponent.value = '';
|
||||
}
|
||||
|
||||
function print(data: PrintData): void {
|
||||
if (isInitializing) {
|
||||
console.error('Terminal is initializing! Skipping Print');
|
||||
return;
|
||||
}
|
||||
terminalComponent.addComponent(Cursor, {
|
||||
path: data.path,
|
||||
output: data.output
|
||||
});
|
||||
updateTerminal();
|
||||
}
|
||||
|
||||
function testAction() {
|
||||
if (!terminal || isInitializing) {
|
||||
console.error('Terminal is initializing!');
|
||||
return;
|
||||
}
|
||||
|
||||
terminal.executeCommand('cd ~/.config');
|
||||
}
|
||||
</script>
|
||||
|
||||
<Settings></Settings>
|
||||
{#if !isInitializing}
|
||||
<div class="h-dvh w-full p-24">
|
||||
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
||||
<div onclick={() => focusInput()}>
|
||||
<TerminalModule bind:this={terminalComponent} {username} {cwd}>
|
||||
<Input {cwd} bind:this={inputComponent} />
|
||||
</TerminalModule>
|
||||
<button title="" onclick={() => testAction()}>Test Action</button>
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
<Loading></Loading>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
</style>
|
||||
0
src/routes/errors/404/+page.svelte
Normal file
0
src/routes/errors/404/+page.svelte
Normal file
13
src/routes/page.svelte.spec.ts
Normal file
13
src/routes/page.svelte.spec.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { page } from '@vitest/browser/context';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { render } from 'vitest-browser-svelte';
|
||||
import Page from './+page.svelte';
|
||||
|
||||
describe('/+page.svelte', () => {
|
||||
it('should render h1', async () => {
|
||||
render(Page);
|
||||
|
||||
const heading = page.getByRole('heading', { level: 1 });
|
||||
await expect.element(heading).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
50
src/routes/panel/+layout.svelte
Normal file
50
src/routes/panel/+layout.svelte
Normal file
@@ -0,0 +1,50 @@
|
||||
<script lang="ts">
|
||||
import '../../app.css';
|
||||
import favicon from '$lib/assets/favicon.svg';
|
||||
import type { Snippet } from 'svelte';
|
||||
import { BookA, ChartLine, ChartNoAxesColumn, FolderArchive, Plus } from '@lucide/svelte';
|
||||
import SidebarButton from '../../modules/panel/SidebarButton.svelte';
|
||||
|
||||
let { children }: { children: Snippet } = $props();
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<link rel="icon" href={favicon} />
|
||||
</svelte:head>
|
||||
|
||||
<div class="flex h-dvh w-full flex-row text-text-dark light:text-text-light">
|
||||
<div class="flex h-full w-fit flex-col items-center bg-bg-dark light:bg-bg-light">
|
||||
<img class="m-3 mb-8" alt="Logo" src={favicon} height="32" width="32" />
|
||||
<form class="flex h-full w-fit flex-col items-center gap-3">
|
||||
<label
|
||||
class="m-0 mb-4 flex size-12 items-center justify-center rounded-xl bg-bg-lighter-dark shadow-subtle hover:text-primary-hover-dark has-checked:text-primary-dark has-checked:hover:text-primary-hover-dark light:bg-bg-light-light light:hover:text-primary-hover-light light:has-checked:text-primary-light light:has-checked:hover:text-primary-hover-light"
|
||||
>
|
||||
<input name="langs" type="radio" class="hidden" id="pl" autocomplete="off" />
|
||||
<Plus strokeWidth={1} />
|
||||
</label>
|
||||
<SidebarButton checked path="/panel/projects">
|
||||
<FolderArchive strokeWidth={1} />
|
||||
</SidebarButton>
|
||||
<SidebarButton path="/panel/langs">
|
||||
<BookA strokeWidth={1} />
|
||||
</SidebarButton>
|
||||
<SidebarButton path="/panel/metrics">
|
||||
<ChartLine strokeWidth={1} />
|
||||
</SidebarButton>
|
||||
<SidebarButton path="/panel/stats">
|
||||
<ChartNoAxesColumn strokeWidth={1} />
|
||||
</SidebarButton>
|
||||
</form>
|
||||
</div>
|
||||
<div class="flex w-full grow">
|
||||
{@render children()}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
main {
|
||||
transition:
|
||||
--dots-bg-2 0.2s ease-in,
|
||||
--dots-bg-1 0.2s ease-in;
|
||||
}
|
||||
</style>
|
||||
0
src/routes/panel/+page.svelte
Normal file
0
src/routes/panel/+page.svelte
Normal file
19
src/routes/panel/langs/+page.svelte
Normal file
19
src/routes/panel/langs/+page.svelte
Normal file
@@ -0,0 +1,19 @@
|
||||
<script lang="ts">
|
||||
import LangModule from '../../../modules/panel/LangModule.svelte';
|
||||
import plFlag from '$lib/assets/plFlag.svg';
|
||||
import enFlag from '$lib/assets/enFlag.svg';
|
||||
import deFlag from '$lib/assets/deFlag.svg';
|
||||
import frFlag from '$lib/assets/frFlag.svg';
|
||||
import jaFlag from '$lib/assets/jaFlag.svg';
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<form name="langs" class="m-10 flex flex-col gap-4">
|
||||
<LangModule checked langName="Polish" icon={plFlag} />
|
||||
<LangModule checked langName="English" icon={enFlag} />
|
||||
<LangModule checked langName="German" icon={deFlag} />
|
||||
<LangModule langName="Japanese" icon={jaFlag} />
|
||||
<LangModule langName="French" icon={frFlag} />
|
||||
</form>
|
||||
<div></div>
|
||||
</div>
|
||||
1
src/routes/panel/metrics/+page.svelte
Normal file
1
src/routes/panel/metrics/+page.svelte
Normal file
@@ -0,0 +1 @@
|
||||
METRICS
|
||||
7
src/routes/panel/projects/+page.svelte
Normal file
7
src/routes/panel/projects/+page.svelte
Normal file
@@ -0,0 +1,7 @@
|
||||
<script lang="ts">
|
||||
import ProjectModule from '../../../modules/panel/ProjectModule.svelte';
|
||||
</script>
|
||||
|
||||
<div class=" m-10">
|
||||
<ProjectModule projectName="ProjectName" />
|
||||
</div>
|
||||
1
src/routes/panel/stats/+page.svelte
Normal file
1
src/routes/panel/stats/+page.svelte
Normal file
@@ -0,0 +1 @@
|
||||
STATS
|
||||
26
src/style/animations.css
Normal file
26
src/style/animations.css
Normal file
@@ -0,0 +1,26 @@
|
||||
@theme {
|
||||
--animate-loading: loading 1s ease-in-out infinite;
|
||||
--animate-cursor-blink: blink 1s step-end infinite;
|
||||
|
||||
@keyframes blink {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes loading {
|
||||
0% {
|
||||
transform: scaleY(1);
|
||||
}
|
||||
30% {
|
||||
transform: scaleY(0.35);
|
||||
background-color: var(--color-loader-primary-hl);
|
||||
}
|
||||
60% {
|
||||
transform: scaleY(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
955
src/style/fonts.css
Normal file
955
src/style/fonts.css
Normal file
@@ -0,0 +1,955 @@
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Atkinson Hyperlegible Mono';
|
||||
font-style: italic;
|
||||
font-weight: 800;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/atkinsonhyperlegiblemono/v8/tssPAoFBci4C4gvhPXrt3wjT1MqSzhA4t7IIcncBiwKonlKh6PW-UyGM1JTKSRMW8qj-lw.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329,
|
||||
U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F,
|
||||
U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Atkinson Hyperlegible Mono';
|
||||
font-style: italic;
|
||||
font-weight: 800;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/atkinsonhyperlegiblemono/v8/tssPAoFBci4C4gvhPXrt3wjT1MqSzhA4t7IIcncBiwKonlKh6PW-UyGM1JTKSRMY8qg.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329,
|
||||
U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Atkinson Hyperlegible Mono';
|
||||
font-style: normal;
|
||||
font-weight: 800;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/atkinsonhyperlegiblemono/v8/tssNAoFBci4C4gvhPXrt3wjT1MqSzhA4t7IIcncBiyihrK15gZ4k_SaZnNCSBCMa6qw.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329,
|
||||
U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F,
|
||||
U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Atkinson Hyperlegible Mono';
|
||||
font-style: normal;
|
||||
font-weight: 800;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/atkinsonhyperlegiblemono/v8/tssNAoFBci4C4gvhPXrt3wjT1MqSzhA4t7IIcncBiyihrK15gZ4k_SaZnNCSCiMa.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329,
|
||||
U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Bungee Hairline';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/bungeehairline/v26/snfys0G548t04270a_ljTLUVrv-LaBecc5Y.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301,
|
||||
U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Bungee Hairline';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/bungeehairline/v26/snfys0G548t04270a_ljTLUVrv-LaRecc5Y.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329,
|
||||
U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F,
|
||||
U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Bungee Hairline';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/bungeehairline/v26/snfys0G548t04270a_ljTLUVrv-LZxec.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329,
|
||||
U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Doto';
|
||||
font-style: normal;
|
||||
font-weight: 100 900;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/doto/v3/t5t6IRMbNJ6TQG7Il_EKPqP9zTkn6IuPWhojrg.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329,
|
||||
U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F,
|
||||
U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Doto';
|
||||
font-style: normal;
|
||||
font-weight: 100 900;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/doto/v3/t5t6IRMbNJ6TQG7Il_EKPqP9zTkn6IuBWho.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329,
|
||||
U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Just Me Again Down Here';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/justmeagaindownhere/v25/MwQmbgXtz-Wc6RUEGNMc0QpRrfUh2hSdBBMoAtwOtKHAcw.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329,
|
||||
U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F,
|
||||
U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Just Me Again Down Here';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/justmeagaindownhere/v25/MwQmbgXtz-Wc6RUEGNMc0QpRrfUh2hSdBBMoAtwAtKE.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329,
|
||||
U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Space Mono';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/spacemono/v17/i7dNIFZifjKcF5UAWdDRYERMSHK_IwU.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301,
|
||||
U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Space Mono';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/spacemono/v17/i7dNIFZifjKcF5UAWdDRYERMSXK_IwU.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329,
|
||||
U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F,
|
||||
U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Space Mono';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/spacemono/v17/i7dNIFZifjKcF5UAWdDRYERMR3K_.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329,
|
||||
U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Space Mono';
|
||||
font-style: italic;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/spacemono/v17/i7dSIFZifjKcF5UAWdDRYERE_FeqEySRV3U.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301,
|
||||
U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Space Mono';
|
||||
font-style: italic;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/spacemono/v17/i7dSIFZifjKcF5UAWdDRYERE_FeqEiSRV3U.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329,
|
||||
U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F,
|
||||
U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Space Mono';
|
||||
font-style: italic;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/spacemono/v17/i7dSIFZifjKcF5UAWdDRYERE_FeqHCSR.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329,
|
||||
U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Space Mono';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/spacemono/v17/i7dPIFZifjKcF5UAWdDRYE58RWq7.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301,
|
||||
U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Space Mono';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/spacemono/v17/i7dPIFZifjKcF5UAWdDRYE98RWq7.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329,
|
||||
U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F,
|
||||
U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Space Mono';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/spacemono/v17/i7dPIFZifjKcF5UAWdDRYEF8RQ.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329,
|
||||
U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Space Mono';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/spacemono/v17/i7dMIFZifjKcF5UAWdDRaPpZUFqaHjyV.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301,
|
||||
U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Space Mono';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/spacemono/v17/i7dMIFZifjKcF5UAWdDRaPpZUFuaHjyV.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329,
|
||||
U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F,
|
||||
U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Space Mono';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/spacemono/v17/i7dMIFZifjKcF5UAWdDRaPpZUFWaHg.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329,
|
||||
U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Atkinson Hyperlegible Mono';
|
||||
font-style: italic;
|
||||
font-weight: 200 800;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/atkinsonhyperlegiblemono/v8/tss6AoFBci4C4gvhPXrt3wjT1MqSzhA4t7IIcncBiwKotFCJGR0i.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329,
|
||||
U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F,
|
||||
U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Atkinson Hyperlegible Mono';
|
||||
font-style: italic;
|
||||
font-weight: 200 800;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/atkinsonhyperlegiblemono/v8/tss6AoFBci4C4gvhPXrt3wjT1MqSzhA4t7IIcncBiwKotF6JGQ.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329,
|
||||
U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Quicksand';
|
||||
font-style: normal;
|
||||
font-weight: 300 700;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/quicksand/v37/6xKtdSZaM9iE8KbpRA_hJFQNcOM.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301,
|
||||
U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Quicksand';
|
||||
font-style: normal;
|
||||
font-weight: 300 700;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/quicksand/v37/6xKtdSZaM9iE8KbpRA_hJVQNcOM.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329,
|
||||
U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F,
|
||||
U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Quicksand';
|
||||
font-style: normal;
|
||||
font-weight: 300 700;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/quicksand/v37/6xKtdSZaM9iE8KbpRA_hK1QN.woff2) format('woff2');
|
||||
unicode-range:
|
||||
U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329,
|
||||
U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Silkscreen';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/silkscreen/v6/m8JXjfVPf62XiF7kO-i9YL1la1OD.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329,
|
||||
U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F,
|
||||
U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Silkscreen';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/silkscreen/v6/m8JXjfVPf62XiF7kO-i9YLNlaw.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329,
|
||||
U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Silkscreen';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/silkscreen/v6/m8JUjfVPf62XiF7kO-i9aAhAfmKi2Oud.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329,
|
||||
U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F,
|
||||
U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Silkscreen';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/silkscreen/v6/m8JUjfVPf62XiF7kO-i9aAhAfmyi2A.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329,
|
||||
U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Smooch Sans';
|
||||
font-style: normal;
|
||||
font-weight: 100 900;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/smoochsans/v15/c4mk1n5uGsXss2LJh1QH6Zd13KeHWA.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301,
|
||||
U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Smooch Sans';
|
||||
font-style: normal;
|
||||
font-weight: 100 900;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/smoochsans/v15/c4mk1n5uGsXss2LJh1QH6Zd03KeHWA.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329,
|
||||
U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F,
|
||||
U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Smooch Sans';
|
||||
font-style: normal;
|
||||
font-weight: 100 900;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/smoochsans/v15/c4mk1n5uGsXss2LJh1QH6Zd63Kc.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329,
|
||||
U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Tsukimi Rounded';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/tsukimirounded/v14/sJoc3LJNksWZO0LvnZwkF3HtoB7tOBsePYl9tE4unb6zDxeJE5P6vlm-9aQ.3.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+fa10, U+fa12-fa6d, U+fb00-fb04, U+fe10-fe19, U+fe30-fe42, U+fe44-fe52, U+fe54-fe66,
|
||||
U+fe68-fe6b, U+ff02, U+ff04, U+ff07, U+ff51, U+ff5b, U+ff5d, U+ff5f-ff60, U+ff66, U+ff69,
|
||||
U+ff87, U+ffa1-ffbe, U+ffc2-ffc7, U+ffca-ffcf, U+ffd2-ffd6;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Tsukimi Rounded';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/tsukimirounded/v14/sJoc3LJNksWZO0LvnZwkF3HtoB7tOBsePYl9tE4unb6zDxeJE5P6vlm-9aQ.54.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+3028-303f, U+3094-3096, U+309f-30a0, U+30ee, U+30f7-30fa, U+30ff, U+3105-312f, U+3131-3163,
|
||||
U+3165-318e, U+3190-31bb, U+31c0-31c7;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Tsukimi Rounded';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/tsukimirounded/v14/sJoc3LJNksWZO0LvnZwkF3HtoB7tOBsePYl9tE4unb6zDxeJE5P6vlm-9aQ.58.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+2105, U+2109-210a, U+210f, U+2116, U+2121, U+2126-2127, U+212b, U+212e, U+2135, U+213b,
|
||||
U+2194-2199, U+21b8-21b9, U+21c4-21c6, U+21cb-21cc, U+21d0, U+21e6-21e9, U+21f5, U+2202-2203,
|
||||
U+2205-2206, U+2208-220b, U+220f, U+2211, U+2213, U+2215, U+221a, U+221d, U+2220, U+2223,
|
||||
U+2225-2226, U+2228, U+222a-222e, U+2234-2237, U+223d, U+2243, U+2245, U+2248, U+224c, U+2260,
|
||||
U+2262, U+2264-2265, U+226e-226f, U+2272-2273, U+2276-2277, U+2283-2287, U+228a-228b,
|
||||
U+2295-2299, U+22a0, U+22a5, U+22bf, U+22da-22db, U+22ef, U+2305-2307, U+2318, U+2329-232a,
|
||||
U+23b0-23b1, U+23be-23cc, U+23ce, U+23da-23db, U+2423, U+2469-24d0;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Tsukimi Rounded';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/tsukimirounded/v14/sJoc3LJNksWZO0LvnZwkF3HtoB7tOBsePYl9tE4unb6zDxeJE5P6vlm-9aQ.59.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+a1-a4, U+a6-a7, U+aa, U+ac-ad, U+b5-b6, U+b8-ba, U+bc-c8, U+ca-cc, U+ce-d5, U+d9-db, U+dd-df,
|
||||
U+e6, U+ee, U+f0, U+f5, U+f7, U+f9, U+fb, U+fe-102, U+110-113, U+11a-11b, U+128-12b, U+143-144,
|
||||
U+147-148, U+14c, U+14e-14f, U+152-153, U+168-16d, U+192, U+1a0-1a1, U+1af, U+1cd-1dc,
|
||||
U+1f8-1f9, U+251, U+261, U+2bb, U+2c7, U+2c9, U+2ea-2eb, U+304, U+307, U+30c, U+1e3e-1e3f,
|
||||
U+1ea0-1ebe, U+1ec0-1ec6, U+1ec8-1ef9, U+2011-2012, U+2016, U+2018-201a, U+201e, U+2021, U+2030,
|
||||
U+2033, U+2035, U+2042, U+2047, U+2051, U+2074, U+20a9, U+20ab-20ac, U+20dd-20de, U+2100;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Tsukimi Rounded';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/tsukimirounded/v14/sJoc3LJNksWZO0LvnZwkF3HtoB7tOBsePYl9tE4unb6zDxeJE5P6vlm-9aQ.61.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+a8, U+2032, U+2261, U+2282, U+3090, U+30f1, U+339c, U+535c, U+53d9, U+56a2, U+56c1, U+5806,
|
||||
U+589f, U+59d0, U+5a7f, U+60e0, U+639f, U+65af, U+68fa, U+69ae, U+6d1b, U+6ef2, U+71fb, U+725d,
|
||||
U+7262, U+75bc, U+7768, U+7940, U+79bf, U+7bed, U+7d68, U+7dfb, U+814b, U+8207, U+83e9, U+8494,
|
||||
U+8526, U+8568, U+85ea, U+86d9, U+87ba, U+8861, U+887f, U+8fe6, U+9059, U+9061, U+916a, U+976d,
|
||||
U+97ad, U+9ece;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Tsukimi Rounded';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/tsukimirounded/v14/sJoc3LJNksWZO0LvnZwkF3HtoB7tOBsePYl9tE4unb6zDxeJE5P6vlm-9aQ.65.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+b1, U+309b, U+4e5e, U+51f1, U+5506, U+55c5, U+58cc, U+59d1, U+5c51, U+5ef7, U+6284, U+62d7,
|
||||
U+6689, U+673d, U+6a2b, U+6a8e, U+6a9c, U+6d63, U+6dd1, U+70b8, U+7235, U+72db, U+72f8, U+7560,
|
||||
U+7c9b, U+7ce7, U+7e1e, U+80af, U+82eb, U+8463, U+8499, U+85dd, U+86ee, U+8a60, U+8a6e, U+8c79,
|
||||
U+8e87, U+8e8a, U+8f5f, U+9010, U+918d, U+9190, U+965b, U+97fb, U+9ab8, U+9bad, U+9d3b, U+9d5c,
|
||||
U+9dfa, U+9e93;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Tsukimi Rounded';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/tsukimirounded/v14/sJoc3LJNksWZO0LvnZwkF3HtoB7tOBsePYl9tE4unb6zDxeJE5P6vlm-9aQ.70.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+266b, U+3006, U+5176, U+5197, U+51a8, U+51c6, U+52f2, U+5614, U+5875, U+5a2f, U+5b54, U+5ce0,
|
||||
U+5dba, U+5deb, U+5e63, U+5f59, U+5fcc, U+6068, U+6367, U+68b6, U+6a0b, U+6b64, U+6e15, U+6eba,
|
||||
U+7272, U+72a0, U+7947, U+7985, U+79e6, U+79e9, U+7a3d, U+7a9f, U+7aaf, U+7b95, U+7f60, U+7f9e,
|
||||
U+7fe0, U+8098, U+80ba, U+8106, U+82d4, U+831c, U+87f9, U+8a1f, U+8acf, U+90c1, U+920d, U+9756,
|
||||
U+fe43, U+ff94;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Tsukimi Rounded';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/tsukimirounded/v14/sJoc3LJNksWZO0LvnZwkF3HtoB7tOBsePYl9tE4unb6zDxeJE5P6vlm-9aQ.71.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+af, U+2465, U+2517, U+33a1, U+4f10, U+50c5, U+51b4, U+5384, U+5606, U+5bb0, U+5cac, U+5ee3,
|
||||
U+618e, U+61f2, U+62c9, U+66ab, U+66f9, U+6816, U+6960, U+6b3e, U+6f20, U+7078, U+72d0, U+73ed,
|
||||
U+7ad9, U+7b1b, U+7be4, U+7d62, U+7f51, U+80b4, U+80f4, U+8154, U+85fb, U+865c, U+8702, U+895f,
|
||||
U+8aed, U+8b90, U+8ced, U+8fbf, U+91d8, U+9418, U+9583, U+9591, U+9813, U+982c, U+9bd6, U+ff46,
|
||||
U+ff7f, U+ff88;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Tsukimi Rounded';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/tsukimirounded/v14/sJoc3LJNksWZO0LvnZwkF3HtoB7tOBsePYl9tE4unb6zDxeJE5P6vlm-9aQ.79.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+25b3, U+30f5, U+4eae, U+4f46, U+4f51, U+5203, U+52ff, U+55a7, U+564c, U+565b, U+57f9, U+5805,
|
||||
U+5b64, U+5e06, U+5f70, U+5f90, U+60e8, U+6182, U+62f3, U+62fe, U+63aa, U+64a4, U+65d7, U+673a,
|
||||
U+6851, U+68cb, U+68df, U+6d1e, U+6e58, U+6e9d, U+77b3, U+7832, U+7c3f, U+7db4, U+7f70, U+80aa,
|
||||
U+80c6, U+8105, U+819d, U+8276, U+8679, U+8986, U+8c9d, U+8fc5, U+916c, U+9665, U+9699, U+96c0,
|
||||
U+9a19, U+ff8b;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Tsukimi Rounded';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/tsukimirounded/v14/sJoc3LJNksWZO0LvnZwkF3HtoB7tOBsePYl9tE4unb6zDxeJE5P6vlm-9aQ.82.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+2103, U+5049, U+52b1, U+5320, U+5553, U+572d, U+58c7, U+5b5d, U+5bc2, U+5de3, U+5e61, U+5f80,
|
||||
U+61a9, U+67d0, U+67f4, U+6c88, U+6ca1, U+6ce5, U+6d78, U+6e9c, U+6f54, U+731b, U+73b2, U+74a7,
|
||||
U+74f6, U+75e9, U+7b20, U+7c8b, U+7f72, U+809d, U+8108, U+82b3, U+82bd, U+84b8, U+84c4, U+88c2,
|
||||
U+8ae6, U+8ef8, U+902e, U+9065, U+9326, U+935b, U+938c, U+9676, U+9694, U+96f7, U+9ed9, U+ff48,
|
||||
U+ff4c, U+ff81;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Tsukimi Rounded';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/tsukimirounded/v14/sJoc3LJNksWZO0LvnZwkF3HtoB7tOBsePYl9tE4unb6zDxeJE5P6vlm-9aQ.83.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+2500, U+3008-3009, U+4ead, U+4f0f, U+4fca, U+53eb, U+543e, U+57a2, U+5cf0, U+5e8f, U+5fe0,
|
||||
U+61b2, U+62d8, U+6442, U+64b2, U+6589, U+659c, U+67f1, U+68c4, U+6cb8, U+6d12, U+6de1, U+6fe1,
|
||||
U+70c8, U+723d, U+73e0, U+7656, U+773a, U+7948, U+7b87, U+7c92, U+7d3a, U+7e1b, U+7e4a, U+819a,
|
||||
U+8358, U+83c5, U+84bc, U+864e, U+8912, U+8c9e, U+8d05, U+92fc, U+9396, U+98fd, U+99d2, U+ff64,
|
||||
U+ff7a, U+ff83;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Tsukimi Rounded';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/tsukimirounded/v14/sJoc3LJNksWZO0LvnZwkF3HtoB7tOBsePYl9tE4unb6zDxeJE5P6vlm-9aQ.84.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+3014-3015, U+4e3c, U+5036, U+5075, U+533f, U+53e9, U+5531, U+5642, U+5984, U+59e6, U+5a01,
|
||||
U+5b6b, U+5c0b, U+5f25, U+6069, U+60a0, U+614e, U+62b5, U+62d2-62d3, U+6597, U+660c, U+674f,
|
||||
U+67cf, U+6841, U+6905, U+6cf3, U+6d32, U+6d69, U+6f64, U+716e, U+7761, U+7b52, U+7be0, U+7dbf,
|
||||
U+7de9, U+7f36, U+81d3, U+8302, U+8389, U+846c, U+84ee, U+8a69, U+9038, U+9d8f, U+ff47, U+ff4b,
|
||||
U+ff76, U+ff9b;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Tsukimi Rounded';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/tsukimirounded/v14/sJoc3LJNksWZO0LvnZwkF3HtoB7tOBsePYl9tE4unb6zDxeJE5P6vlm-9aQ.86.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+24, U+2022, U+2212, U+221f, U+2665, U+4ecf, U+5100, U+51cd, U+52d8, U+5378, U+53f6, U+574a,
|
||||
U+5982, U+5996, U+5c1a, U+5e1d, U+5f84, U+609f, U+61a7, U+61f8, U+6398, U+63ee, U+6676, U+6691,
|
||||
U+6eb6, U+7126, U+71e5, U+7687, U+7965, U+7d17, U+80a1, U+8107, U+8266, U+85a6, U+8987, U+8ca2,
|
||||
U+8cab, U+8e0a, U+9042, U+95c7, U+9810, U+9867, U+98fc, U+ff52-ff54, U+ff61, U+ff77, U+ff98-ff99;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Tsukimi Rounded';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/tsukimirounded/v14/sJoc3LJNksWZO0LvnZwkF3HtoB7tOBsePYl9tE4unb6zDxeJE5P6vlm-9aQ.87.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+b0, U+226a, U+2462, U+4e39, U+4fc3, U+4fd7, U+50be, U+50da, U+5200, U+5211, U+54f2, U+5618,
|
||||
U+596a, U+5b22, U+5bb4, U+5d50, U+60a3, U+63fa, U+658e, U+65e8, U+6669, U+6795, U+679d, U+67a0,
|
||||
U+6b3a, U+6e09, U+757f, U+7cd6, U+7dbe, U+7ffb, U+83cc, U+83f1, U+840c, U+845b, U+8846, U+8972,
|
||||
U+8a34, U+8a50, U+8a87, U+8edf, U+8ff0, U+90a6, U+9154, U+95a3, U+9663, U+9686, U+96c7, U+ff3c,
|
||||
U+ff7c, U+ff8a;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Tsukimi Rounded';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/tsukimirounded/v14/sJoc3LJNksWZO0LvnZwkF3HtoB7tOBsePYl9tE4unb6zDxeJE5P6vlm-9aQ.89.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+a5, U+4e80, U+4f34, U+4f73, U+4f75, U+511f, U+5192, U+52aa, U+53c8, U+570f, U+57cb, U+596e,
|
||||
U+5d8b, U+5f66, U+5fd9, U+62db, U+62f6, U+6328, U+633f, U+63a7, U+6469, U+6bbf, U+6c41, U+6c57,
|
||||
U+6d44, U+6dbc, U+706f, U+72c2, U+72ed, U+7551, U+75f4, U+7949, U+7e26, U+7fd4, U+8150, U+8af8,
|
||||
U+8b0e, U+8b72, U+8ca7, U+934b, U+9a0e, U+9a12, U+9b42, U+ff41, U+ff43, U+ff45, U+ff49, U+ff4f,
|
||||
U+ff62-ff63;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Tsukimi Rounded';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/tsukimirounded/v14/sJoc3LJNksWZO0LvnZwkF3HtoB7tOBsePYl9tE4unb6zDxeJE5P6vlm-9aQ.91.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+60, U+2200, U+226b, U+2461, U+517c, U+526f, U+5800, U+5b97, U+5bf8, U+5c01, U+5d29, U+5e4c,
|
||||
U+5e81, U+6065, U+61d0, U+667a, U+6696, U+6843, U+6c99, U+6d99, U+6ec5, U+6f22, U+6f6e, U+6fa4,
|
||||
U+6fef, U+71c3, U+72d9, U+7384, U+78e8, U+7a1a, U+7a32, U+7a3c, U+7adc, U+7ca7, U+7d2b, U+7dad,
|
||||
U+7e4b, U+80a9, U+8170, U+81ed, U+820e, U+8a17, U+8afe, U+90aa, U+914e, U+963f, U+99c4, U+9eba,
|
||||
U+9f3b, U+ff38;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Tsukimi Rounded';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/tsukimirounded/v14/sJoc3LJNksWZO0LvnZwkF3HtoB7tOBsePYl9tE4unb6zDxeJE5P6vlm-9aQ.93.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+21d2, U+25ce, U+300a-300b, U+4e89, U+4e9c, U+4ea1, U+5263, U+53cc, U+5426, U+5869, U+5947,
|
||||
U+598a, U+5999, U+5e55, U+5e72, U+5e79, U+5fae, U+5fb9, U+602a, U+6163, U+624d, U+6749, U+6c5a,
|
||||
U+6cbf, U+6d45, U+6dfb, U+6e7e, U+708e, U+725b, U+7763, U+79c0, U+7bc4, U+7c89, U+7e01, U+7e2e,
|
||||
U+8010, U+8033, U+8c6a, U+8cc3, U+8f1d, U+8f9b, U+8fb2, U+907f, U+90f7, U+9707, U+9818, U+9b3c,
|
||||
U+ff0a, U+ff4d;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Tsukimi Rounded';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/tsukimirounded/v14/sJoc3LJNksWZO0LvnZwkF3HtoB7tOBsePYl9tE4unb6zDxeJE5P6vlm-9aQ.95.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+2193, U+25b2, U+4e4b, U+516d, U+51c4, U+529f, U+52c9, U+5360, U+5442, U+5857, U+5915, U+59eb,
|
||||
U+5a9b, U+5c3b, U+6012, U+61b6, U+62b1, U+6311, U+6577, U+65e2, U+65ec, U+6613, U+6790, U+6cb9,
|
||||
U+7372, U+76ae, U+7d5e, U+7fcc, U+88ab, U+88d5, U+8caf, U+8ddd, U+8ecd, U+8f38, U+8f9e, U+8feb,
|
||||
U+9063, U+90f5, U+93e1, U+968a, U+968f, U+98fe, U+9ec4, U+ff1d, U+ff27, U+ff2a, U+ff36, U+ff3b,
|
||||
U+ff3d, U+ffe5;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Tsukimi Rounded';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/tsukimirounded/v14/sJoc3LJNksWZO0LvnZwkF3HtoB7tOBsePYl9tE4unb6zDxeJE5P6vlm-9aQ.97.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+7e, U+b4, U+25c6, U+2661, U+4e92, U+4eee, U+4ffa, U+5144, U+5237, U+5287, U+52b4, U+58c1,
|
||||
U+5bff, U+5c04, U+5c06, U+5e95, U+5f31, U+5f93, U+63c3, U+640d, U+6557, U+6614, U+662f, U+67d3,
|
||||
U+690d, U+6bba, U+6e6f, U+72af, U+732b, U+7518, U+7ae0, U+7ae5, U+7af6, U+822a, U+89e6, U+8a3a,
|
||||
U+8a98, U+8cb8, U+8de1, U+8e8d, U+95d8, U+961c, U+96a3, U+96ea, U+9bae, U+ff20, U+ff22, U+ff29,
|
||||
U+ff2b-ff2c;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Tsukimi Rounded';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/tsukimirounded/v14/sJoc3LJNksWZO0LvnZwkF3HtoB7tOBsePYl9tE4unb6zDxeJE5P6vlm-9aQ.99.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+2191, U+505c, U+52e4, U+5305, U+535a, U+56e0, U+59bb, U+5acc, U+5b09, U+5b87, U+5c90, U+5df1,
|
||||
U+5e2d, U+5e33, U+5f3e, U+6298, U+6383, U+653b, U+6697, U+6804, U+6a39, U+6cca, U+6e90, U+6f2b,
|
||||
U+702c, U+7206, U+7236, U+7559, U+7565, U+7591, U+75c7, U+75db, U+7b4b, U+7bb1, U+7d99, U+7fbd,
|
||||
U+8131, U+885b, U+8b1d, U+8ff7, U+9003, U+9045, U+96a0, U+9732, U+990a, U+99d0, U+9e97, U+9f62,
|
||||
U+ff25, U+ff2d;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Tsukimi Rounded';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/tsukimirounded/v14/sJoc3LJNksWZO0LvnZwkF3HtoB7tOBsePYl9tE4unb6zDxeJE5P6vlm-9aQ.102.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+3d, U+5e, U+25cf, U+4e0e, U+4e5d, U+4e73, U+4e94, U+4f3c, U+5009, U+5145, U+51ac, U+5238,
|
||||
U+524a, U+53f3, U+547c, U+5802, U+5922, U+5a66, U+5c0e, U+5de6, U+5fd8, U+5feb, U+6797, U+685c,
|
||||
U+6b7b, U+6c5f-6c60, U+6cc9, U+6ce2, U+6d17, U+6e21, U+7167, U+7642, U+76db, U+8001, U+821e,
|
||||
U+8857, U+89d2, U+8b1b, U+8b70, U+8cb4, U+8cde, U+8f03, U+8f2a, U+968e, U+9b54, U+9e7f, U+9ebb,
|
||||
U+ff05, U+ff33;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Tsukimi Rounded';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/tsukimirounded/v14/sJoc3LJNksWZO0LvnZwkF3HtoB7tOBsePYl9tE4unb6zDxeJE5P6vlm-9aQ.103.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+500d, U+5074, U+50cd, U+5175, U+52e2, U+5352, U+5354, U+53f2, U+5409, U+56fa, U+5a18, U+5b88,
|
||||
U+5bdd, U+5ca9, U+5f92, U+5fa9, U+60a9, U+623f, U+6483, U+653f, U+666f, U+66ae, U+66f2, U+6a21,
|
||||
U+6b66, U+6bcd, U+6d5c, U+796d, U+7a4d, U+7aef, U+7b56, U+7b97, U+7c4d, U+7e04, U+7fa9, U+8377,
|
||||
U+83dc, U+83ef, U+8535, U+8863, U+88cf, U+88dc, U+8907, U+8acb, U+90ce, U+91dd, U+ff0b, U+ff0d,
|
||||
U+ff19, U+ff65;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Tsukimi Rounded';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/tsukimirounded/v14/sJoc3LJNksWZO0LvnZwkF3HtoB7tOBsePYl9tE4unb6zDxeJE5P6vlm-9aQ.104.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+4e01, U+4e21, U+4e38, U+52a9, U+547d, U+592e, U+5931, U+5b63, U+5c40, U+5dde, U+5e78, U+5efa,
|
||||
U+5fa1, U+604b, U+6075, U+62c5, U+632f, U+6a19, U+6c0f, U+6c11, U+6c96, U+6e05, U+70ba, U+71b1,
|
||||
U+7387, U+7403, U+75c5, U+77ed, U+795d, U+7b54, U+7cbe, U+7d19, U+7fa4, U+8089, U+81f4, U+8208,
|
||||
U+8336, U+8457, U+8a33, U+8c4a, U+8ca0, U+8ca8, U+8cc0, U+9014, U+964d, U+9803, U+983c, U+98db,
|
||||
U+ff17, U+ff21;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Tsukimi Rounded';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/tsukimirounded/v14/sJoc3LJNksWZO0LvnZwkF3HtoB7tOBsePYl9tE4unb6zDxeJE5P6vlm-9aQ.105.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+25, U+25a0, U+4e26, U+4f4e, U+5341, U+56f2, U+5bbf, U+5c45, U+5c55, U+5c5e, U+5dee, U+5e9c,
|
||||
U+5f7c, U+6255, U+627f, U+62bc, U+65cf, U+661f, U+666e, U+66dc, U+67fb, U+6975, U+6a4b, U+6b32,
|
||||
U+6df1, U+6e29, U+6fc0, U+738b, U+7686, U+7a76, U+7a81, U+7c73, U+7d75, U+7dd2, U+82e5, U+82f1,
|
||||
U+85ac, U+888b, U+899a, U+8a31, U+8a8c, U+8ab0, U+8b58, U+904a, U+9060, U+9280, U+95b2, U+984d,
|
||||
U+9ce5, U+ff18;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Tsukimi Rounded';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/tsukimirounded/v14/sJoc3LJNksWZO0LvnZwkF3HtoB7tOBsePYl9tE4unb6zDxeJE5P6vlm-9aQ.106.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+30f6, U+50ac, U+5178, U+51e6, U+5224, U+52dd, U+5883, U+5897, U+590f, U+5a5a, U+5bb3, U+5c65,
|
||||
U+5e03, U+5e2b, U+5e30, U+5eb7, U+6271, U+63f4, U+64ae, U+6574, U+672b, U+679a, U+6a29-6a2a,
|
||||
U+6ca2, U+6cc1, U+6d0b, U+713c, U+74b0, U+7981, U+7a0b, U+7bc0, U+7d1a, U+7d61, U+7fd2, U+822c,
|
||||
U+8996, U+89aa, U+8cac, U+8cbb, U+8d77, U+8def, U+9020, U+9152, U+9244, U+9662, U+967a, U+96e3,
|
||||
U+9759, U+ff16;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Tsukimi Rounded';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/tsukimirounded/v14/sJoc3LJNksWZO0LvnZwkF3HtoB7tOBsePYl9tE4unb6zDxeJE5P6vlm-9aQ.107.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+23, U+3c, U+2192, U+4e45, U+4efb, U+4f50, U+4f8b, U+4fc2, U+5024, U+5150, U+5272, U+5370,
|
||||
U+53bb, U+542b, U+56db, U+56e3, U+57ce, U+5bc4, U+5bcc, U+5f71, U+60aa, U+6238, U+6280, U+629c,
|
||||
U+6539, U+66ff, U+670d, U+677e-677f, U+6839, U+69cb, U+6b4c, U+6bb5, U+6e96, U+6f14, U+72ec,
|
||||
U+7389, U+7814, U+79cb, U+79d1, U+79fb, U+7a0e, U+7d0d, U+85e4, U+8d64, U+9632, U+96e2, U+9805,
|
||||
U+99ac, U+ff1e;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Tsukimi Rounded';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/tsukimirounded/v14/sJoc3LJNksWZO0LvnZwkF3HtoB7tOBsePYl9tE4unb6zDxeJE5P6vlm-9aQ.109.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+266a, U+4f11, U+533a, U+5343, U+534a, U+53cd, U+5404, U+56f3, U+5b57-5b58, U+5bae, U+5c4a,
|
||||
U+5e0c, U+5e2f, U+5eab, U+5f35, U+5f79, U+614b, U+6226, U+629e, U+65c5, U+6625, U+6751, U+6821,
|
||||
U+6b69, U+6b8b, U+6bce, U+6c42, U+706b, U+7c21, U+7cfb, U+805e, U+80b2, U+82b8, U+843d, U+8853,
|
||||
U+88c5, U+8a3c, U+8a66, U+8d8a, U+8fba, U+9069, U+91cf, U+9752, U+975e, U+9999, U+ff0f-ff10,
|
||||
U+ff14-ff15;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Tsukimi Rounded';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/tsukimirounded/v14/sJoc3LJNksWZO0LvnZwkF3HtoB7tOBsePYl9tE4unb6zDxeJE5P6vlm-9aQ.110.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+40, U+4e86, U+4e95, U+4f01, U+4f1d, U+4fbf, U+5099, U+5171, U+5177, U+53cb, U+53ce, U+53f0,
|
||||
U+5668, U+5712, U+5ba4, U+5ca1, U+5f85, U+60f3, U+653e, U+65ad, U+65e9, U+6620, U+6750, U+6761,
|
||||
U+6b62, U+6b74, U+6e08, U+6e80, U+7248, U+7531, U+7533, U+753a, U+77f3, U+798f, U+7f6e, U+8449,
|
||||
U+88fd, U+89b3, U+8a55, U+8ac7, U+8b77, U+8db3, U+8efd, U+8fd4, U+9031-9032, U+9580, U+9589,
|
||||
U+96d1, U+985e;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Tsukimi Rounded';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/tsukimirounded/v14/sJoc3LJNksWZO0LvnZwkF3HtoB7tOBsePYl9tE4unb6zDxeJE5P6vlm-9aQ.111.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+2b, U+d7, U+300e-300f, U+4e07, U+4e8c, U+512a, U+5149, U+518d, U+5236, U+52b9, U+52d9, U+5468,
|
||||
U+578b, U+57fa, U+5b8c, U+5ba2, U+5c02, U+5de5, U+5f37, U+5f62, U+623b, U+63d0, U+652f, U+672a,
|
||||
U+6848, U+6d41, U+7136, U+7537, U+754c, U+76f4, U+79c1, U+7ba1, U+7d44, U+7d4c, U+7dcf, U+7dda,
|
||||
U+7de8, U+82b1, U+897f, U+8ca9, U+8cfc, U+904e, U+9664, U+982d, U+9858, U+98a8, U+9a13, U+ff13,
|
||||
U+ff5c;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Tsukimi Rounded';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/tsukimirounded/v14/sJoc3LJNksWZO0LvnZwkF3HtoB7tOBsePYl9tE4unb6zDxeJE5P6vlm-9aQ.113.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+26, U+5f, U+2026, U+203b, U+4e09, U+4eac, U+4ed5, U+4fa1, U+5143, U+5199, U+5207, U+539f,
|
||||
U+53e3, U+53f7, U+5411, U+5473, U+5546, U+55b6, U+5929, U+597d, U+5bb9, U+5c11, U+5c4b, U+5ddd,
|
||||
U+5f97, U+5fc5, U+6295, U+6301, U+6307, U+671b, U+76f8, U+78ba, U+795e, U+7d30, U+7d39, U+7d9a,
|
||||
U+89e3, U+8a00, U+8a73, U+8a8d, U+8a9e, U+8aad, U+8abf, U+8cea, U+8eca, U+8ffd, U+904b, U+9650,
|
||||
U+ff11-ff12;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Tsukimi Rounded';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/tsukimirounded/v14/sJoc3LJNksWZO0LvnZwkF3HtoB7tOBsePYl9tE4unb6zDxeJE5P6vlm-9aQ.114.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+3e, U+3005, U+4e0d, U+4e88, U+4ecb, U+4ee3, U+4ef6, U+4fdd, U+4fe1, U+500b, U+50cf, U+5186,
|
||||
U+5316, U+53d7, U+540c, U+544a, U+54e1, U+5728, U+58f2, U+5973, U+5b89, U+5c71, U+5e02, U+5e97,
|
||||
U+5f15, U+5fc3, U+5fdc, U+601d, U+611b, U+611f, U+671f, U+6728, U+6765, U+683c, U+6b21, U+6ce8,
|
||||
U+6d3b, U+6d77, U+7530, U+7740, U+7acb, U+7d50, U+826f, U+8f09, U+8fbc, U+9001, U+9053, U+91ce,
|
||||
U+9762, U+98df;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Tsukimi Rounded';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/tsukimirounded/v14/sJoc3LJNksWZO0LvnZwkF3HtoB7tOBsePYl9tE4unb6zDxeJE5P6vlm-9aQ.115.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+7c, U+3080, U+4ee5, U+5148, U+516c, U+521d, U+5225, U+529b, U+52a0, U+53ef, U+56de, U+56fd,
|
||||
U+5909, U+591a, U+5b66, U+5b9f, U+5bb6, U+5bfe, U+5e73, U+5e83, U+5ea6, U+5f53, U+6027, U+610f,
|
||||
U+6210, U+6240, U+660e, U+66f4, U+66f8, U+6709, U+6771, U+697d, U+69d8, U+6a5f, U+6c34, U+6cbb,
|
||||
U+73fe, U+756a, U+7684, U+771f, U+793a, U+7f8e, U+898f, U+8a2d, U+8a71, U+8fd1, U+9078, U+9577,
|
||||
U+96fb, U+ff5e;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Tsukimi Rounded';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/tsukimirounded/v14/sJoc3LJNksWZO0LvnZwkF3HtoB7tOBsePYl9tE4unb6zDxeJE5P6vlm-9aQ.116.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+a9, U+3010-3011, U+30e2, U+4e0b, U+4eca, U+4ed6, U+4ed8, U+4f53, U+4f5c, U+4f7f, U+53d6,
|
||||
U+540d, U+54c1, U+5730, U+5916, U+5b50, U+5c0f, U+5f8c, U+624b, U+6570, U+6587, U+6599, U+691c,
|
||||
U+696d, U+6cd5, U+7269, U+7279, U+7406, U+767a-767b, U+77e5, U+7d04, U+7d22, U+8005, U+80fd,
|
||||
U+81ea, U+8868, U+8981, U+89a7, U+901a, U+9023, U+90e8, U+91d1, U+9332, U+958b, U+96c6, U+9ad8,
|
||||
U+ff1a, U+ff1f;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Tsukimi Rounded';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/tsukimirounded/v14/sJoc3LJNksWZO0LvnZwkF3HtoB7tOBsePYl9tE4unb6zDxeJE5P6vlm-9aQ.117.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+4e, U+a0, U+3000, U+300c-300d, U+4e00, U+4e0a, U+4e2d, U+4e8b, U+4eba, U+4f1a, U+5165, U+5168,
|
||||
U+5185, U+51fa, U+5206, U+5229, U+524d, U+52d5, U+5408, U+554f, U+5831, U+5834, U+5927, U+5b9a,
|
||||
U+5e74, U+5f0f, U+60c5, U+65b0, U+65b9, U+6642, U+6700, U+672c, U+682a, U+6b63, U+6c17, U+7121,
|
||||
U+751f, U+7528, U+753b, U+76ee, U+793e, U+884c, U+898b, U+8a18, U+9593, U+95a2, U+ff01,
|
||||
U+ff08-ff09;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Tsukimi Rounded';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/tsukimirounded/v14/sJoc3LJNksWZO0LvnZwkF3HtoB7tOBsePYl9tE4unb6zDxeJE5P6vlm-9aQ.118.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+21-22, U+27-2a, U+2c-3b, U+3f, U+41-4d, U+4f-5d, U+61-7b, U+7d, U+ab, U+ae, U+b2-b3, U+b7,
|
||||
U+bb, U+c9, U+cd, U+d6, U+d8, U+dc, U+e0-e5, U+e7-ed, U+ef, U+f1-f4, U+f6, U+f8, U+fa, U+fc-fd,
|
||||
U+103, U+14d, U+1b0, U+300-301, U+1ebf, U+1ec7, U+2013-2014, U+201c-201d, U+2039-203a, U+203c,
|
||||
U+2048-2049, U+2113, U+2122, U+65e5, U+6708, U+70b9;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Tsukimi Rounded';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/tsukimirounded/v14/sJoc3LJNksWZO0LvnZwkF3HtoB7tOBsePYl9tE4unb6zDxeJE5P6vlm-9aQ.119.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+20, U+2027, U+3001-3002, U+3041-307f, U+3081-308f, U+3091-3093, U+3099-309a, U+309d-309e,
|
||||
U+30a1-30e1, U+30e3-30ed, U+30ef-30f0, U+30f2-30f4, U+30fb-30fe, U+ff0c, U+ff0e;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Tsukimi Rounded';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/tsukimirounded/v14/sJoc3LJNksWZO0LvnZwkF3HtoB7dNHkPD5g.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329,
|
||||
U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F,
|
||||
U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Tsukimi Rounded';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/tsukimirounded/v14/sJoc3LJNksWZO0LvnZwkF3HtoB7dOnkP.woff2)
|
||||
format('woff2');
|
||||
unicode-range:
|
||||
U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329,
|
||||
U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
167
src/style/global.css
Normal file
167
src/style/global.css
Normal file
@@ -0,0 +1,167 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:ital,wght@0,100..800;1,100..800&family=Space+Mono:ital,wght@0,400;0,700;1,400;1,700&display=swap');
|
||||
@import 'tailwindcss';
|
||||
@import './animations.css';
|
||||
@custom-variant light (&:where(.light, .light *));
|
||||
|
||||
* {
|
||||
transition: all 0.2s ease-in allow-discrete;
|
||||
}
|
||||
|
||||
@utility scroll-hidden {
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@theme {
|
||||
/* Shadow variables (unchanged) */
|
||||
--shadow-terminal:
|
||||
rgba(0, 0, 0, 0.4) 0px 2px 4px, rgba(0, 0, 0, 0.3) 0px 7px 13px -3px,
|
||||
rgba(0, 0, 02, 0.5) 0px -4px 0px inset;
|
||||
|
||||
--shadow-button:
|
||||
rgba(0, 0, 0, 0.4) 0px 2px 4px, rgba(0, 0, 0, 0.3) 0px 7px 13px -3px,
|
||||
rgba(0, 0, 02, 0.4) 0px -3px 0px inset;
|
||||
|
||||
--shadow-bg: rgba(0, 0, 0, 0.25) 0px 14px 28px, rgba(0, 0, 0, 0.22) 0px 10px 10px;
|
||||
|
||||
--shadow-subtle:
|
||||
rgba(0, 0, 0, 0.4) 0px 2px 4px, rgba(0, 0, 0, 0.3) 0px 3px 4px -3px,
|
||||
rgba(0, 0, 0, 0.2) 0px -3px 0px inset;
|
||||
|
||||
--background-image-dots-bg: radial-gradient(
|
||||
circle at center,
|
||||
var(--dots-bg-2) 1px,
|
||||
var(--dots-bg-1) 1px
|
||||
);
|
||||
|
||||
/* Background colors */
|
||||
--color-bg-dark-dark: oklch(0.1 0.01 292);
|
||||
--color-bg-dark: oklch(0.15 0.03 292);
|
||||
--color-bg-mid-dark: oklch(0.175 0.03 292);
|
||||
--color-bg-light-dark: oklch(0.2 0.035 292);
|
||||
--color-bg-lighter-dark: oklch(0.25 0.04 292);
|
||||
--color-bg-dark-light: oklch(0.8 0.15 280);
|
||||
--color-bg-light: oklch(0.85 0.2 280);
|
||||
--color-bg-light-light: oklch(0.9 0.3 280);
|
||||
--color-bg-lighter-light: oklch(1 0.3 280);
|
||||
|
||||
/* Text colors */
|
||||
--color-text-dark: oklch(0.96 0.01 292);
|
||||
--color-text-muted-dark: oklch(0.76 0.01 292);
|
||||
--color-text-light: oklch(0.15 0.02 292);
|
||||
--color-text-muted-light: oklch(0.4 0.02 292);
|
||||
|
||||
/* Highlight and border colors */
|
||||
--color-highlight-dark: oklch(0.6 0.02 292);
|
||||
--color-border-dark: oklch(0.3 0.02 292);
|
||||
--color-border-muted-dark: oklch(0.2 0.02 292);
|
||||
--color-highlight-light: oklch(1 0.02 280);
|
||||
--color-border-light: oklch(0.6 0.02 280);
|
||||
--color-border-muted-light: oklch(0.7 0.02 280);
|
||||
|
||||
/* Primary and secondary colors */
|
||||
--color-primary-dark: oklch(0.55 0.15 292);
|
||||
--color-primary-hover-dark: oklch(0.65 0.18 292);
|
||||
--color-secondary-dark: oklch(0.7 0.12 120);
|
||||
--color-primary-light: oklch(0.6 0.1 292);
|
||||
--color-primary-hover-light: oklch(0.65 0.1 292);
|
||||
--color-secondary-light: oklch(0.4 0.1 112);
|
||||
|
||||
/* Loader colors (no light equivalent, using dark values) */
|
||||
--color-loader-primary-dark: oklch(0.55 0.15 292);
|
||||
--color-loader-primary-hl-dark: oklch(0.8 0.15 292);
|
||||
--color-loader-primary-light: oklch(0.55 0.15 292);
|
||||
--color-loader-primary-hl-light: oklch(0.8 0.15 292);
|
||||
|
||||
/* Status colors */
|
||||
--color-danger-dark: oklch(0.7 0.05 30);
|
||||
--color-warning-dark: oklch(0.7 0.05 100);
|
||||
--color-success-dark: oklch(0.7 0.05 160);
|
||||
--color-info-dark: oklch(0.7 0.05 260);
|
||||
--color-danger-light: oklch(0.5 0.05 30);
|
||||
--color-warning-light: oklch(0.5 0.05 100);
|
||||
--color-success-light: oklch(0.5 0.05 160);
|
||||
--color-info-light: oklch(0.5 0.05 260);
|
||||
|
||||
/* Font variables (unchanged) */
|
||||
--font-primary: 'Space Mono', monospace;
|
||||
--font-terminal: 'JetBrains Mono', monospace;
|
||||
}
|
||||
|
||||
:root {
|
||||
transition: all 0.2s ease-in;
|
||||
--dots-bg-1: oklch(0.1 0.01 292);
|
||||
--dots-bg-2: oklch(0.2 0.01 292);
|
||||
|
||||
--border-cart: solid 1px var(--border);
|
||||
--shadow:
|
||||
rgba(0, 0, 0, 0.4) 0px 2px 4px, rgba(0, 0, 0, 0.3) 0px 7px 13px -3px,
|
||||
rgba(0, 0, 0, 0.2) 0px -3px 0px inset;
|
||||
|
||||
--gradient-bg-direction: 0deg;
|
||||
--gradient-bg: linear-gradient(var(--gradient-bg-direction), var(--bg-dark) 80%, transparent);
|
||||
|
||||
--gradient: linear-gradient(0deg, var(--bg) 95%, var(--bg-light));
|
||||
--gradient-hover: linear-gradient(0deg, var(--bg), var(--bg-light));
|
||||
|
||||
--transition-standard: all 0.2s ease-in;
|
||||
--transition-theme: all 0.2s ease;
|
||||
--lang-html: oklch(0.6 0.08207 195.16);
|
||||
--lang-css: oklch(0.5 0.16037 303.368);
|
||||
--lang-js: oklch(0.8 0.18261 102.094);
|
||||
--lang-php: oklch(0.5923 0.09155 272.038);
|
||||
--lang-cs: oklch(0.55 0.27927 141.446);
|
||||
--lang-cpp: oklch(0.6 0.15576 6.88);
|
||||
|
||||
/*
|
||||
|
||||
#underline-bg
|
||||
|
||||
/single side/ linear-gradient(90deg, var(--text) 85%, transparent 100%);
|
||||
/both sides/ radial-gradient(circle, var(--text) 85%, transparent 100%);
|
||||
|
||||
#misc
|
||||
|
||||
background-image: radial-gradient(circle at center, #9f7aea20 1px, transparent 1px); background-size: 20px 20px;
|
||||
(dotted background)
|
||||
|
||||
*/
|
||||
|
||||
main.light {
|
||||
--dots-bg-1: oklch(0.9 0.01 292);
|
||||
--dots-bg-2: oklch(0.8 0.01 292);
|
||||
|
||||
--bg-dark: oklch(0.92 0.2 280);
|
||||
--bg: oklch(0.94 0.3 280);
|
||||
--bg-light: oklch(0.96 0.3 280);
|
||||
|
||||
--text: oklch(0.15 0.02 292);
|
||||
--text-muted: oklch(0.4 0.02 292);
|
||||
|
||||
--highlight: oklch(1 0.02 280);
|
||||
--border: oklch(0.6 0.02 280);
|
||||
--border-muted: oklch(0.7 0.02 280);
|
||||
|
||||
--border-cart: solid 1px var(--bg);
|
||||
--shadow:
|
||||
oklch(0.72 0.01 292 / 0.4) 0px 2px 4px, oklch(0.62 0.01 292 / 0.3) 0px 7px 13px -3px,
|
||||
oklch(0.52 0.01 292 / 0.2) 0px -3px 0px inset;
|
||||
|
||||
--primary: oklch(0.6 0.1 292);
|
||||
--primary-hover: oklch(0.65 0.1 292);
|
||||
--secondary: oklch(0.4 0.1 112);
|
||||
|
||||
--gradient: linear-gradient(0deg, var(--bg), var(--bg-light) 95%);
|
||||
--gradient-hover: linear-gradient(0deg, var(--bg), var(--bg-light));
|
||||
--box-shadow-muted:
|
||||
rgba(14, 30, 37, 0.12) 0px 2px 4px 0px, rgba(14, 30, 37, 0.32) 0px 2px 16px 0px;
|
||||
|
||||
--danger: oklch(0.5 0.05 30);
|
||||
--warning: oklch(0.5 0.05 100);
|
||||
--success: oklch(0.5 0.05 160);
|
||||
--info: oklch(0.5 0.05 260);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user