diff --git a/.env.example b/.env.example index 398bfb3..86a6b69 100644 --- a/.env.example +++ b/.env.example @@ -1,20 +1,23 @@ -#webserver config +# node server config HOST=0.0.0.0 PORT=5178 ORIGIN=http://localhost:5178 -PUBLIC_ORIGIN=https://myweb.cz -BODY_SIZE_LIMIT=16777216 # max upload size in bytes -#database config +# database config DATABASE_IP=10.10.10.223 DATABASE_PORT=3306 DATABASE_USER=superclovek DATABASE_PASSWORD=tajnyheslo123456 -DATABASE_NAME=website -DATABASE_URL=mysql://superclovek:tajnyheslo123456@10.10.10.223:3306/website -#secret pro JWT (tím se bude podepisovat JWT token - https://jwt.io/) +DATABASE_NAME=db +DATABASE_URL=mysql://superclovek:tajnyheslo123456@10.10.10.223:3306/db +# jwt settings JWT_SECRET=text -#v sekundách (10 min = 10 * 60) -#expiruje pouze pokud uživatel danou dobu nic nedělá (neprochází stránky) +# marks how many seconds until session cookie expire COOKIE_EXPIRE=1200 -#v sekundách (5 minut = 5 * 60) -PUBLIC_CHECK_COOKIE_INTERVAL=300 \ No newline at end of file +# default account which will be created in migrations +DEFAULT_USERNAME=patrick115 +DEFAULT_PASSWORD=test123 +HASH_ROUNDS=12 +# folder for uploaded images +FILE_FOLDER=./uploads +MAX_FILE_SIZE=104857600 +BODY_SIZE_LIMIT=104857600 diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..e86fb73 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,319 @@ +## 🧠 Svelte 5 + TailwindCSS 4: Copilot Coding Guide + +Use the following patterns to ensure consistency across components. These examples define the **expected structure**, especially for `$props()`, `twMerge`, runes, and children rendering. + +--- + +### 🟣 Runes Instead of Legacy Syntax + +Always use Svelte 5 runes: + +```ts +// ✅ Access props +const { label }: InputProps = $props(); + +// ✅ Local state +const count = $state(0); + +// ✅ Reactivity +$effect(() => { + console.log(count); +}); + +// ✅ Derived value +const doubled = $derived(() => count * 2); + +// ✅ Bindable value +const { value = $bindable('') }: { value: string } = $props(); +``` + +Avoid legacy syntax: + +```ts +// ❌ Don't use export let +export let label: string; + +// ❌ Don't use $: +$: something = ... +``` + +--- + +### 🔡 Prop Typing and Usage + +Always define a named prop type. Avoid inline types inside `$props()`: + +```ts +type ButtonProps = { + label: string; + onClick?: () => void; +}; + +const { label, onClick }: ButtonProps = $props(); +``` + +--- + +### 🧩 Extending HTML Elements + +When wrapping native elements like ``, use `SvelteHTMLElements`: + +```ts +import type { SvelteHTMLElements } from 'svelte/elements'; + +type InputProps = { + label: string; +} & SvelteHTMLElements['input']; + +const { label, class: cls = '', ...props }: InputProps = $props(); +``` + +Don’t re-declare built-in HTML attributes (like `value`, `name`, or `children`). They're already included. + +```ts +// ❌ Don't do this: +type Bad = { + value?: string; + name?: string; +} & SvelteHTMLElements['input']; +``` + +--- + +### 🔄 Bindable Props + +For bindable values (e.g. `bind:value`), use `$bindable()`: + +```ts +const { value = $bindable('') }: { value: string } = $props(); +``` + +Or when combining with other props: + +```ts +const { value = $bindable(''), class: cls = '', ...props }: InputProps = $props(); +``` + +--- + +### 🧱 Tailwind Class Merging + +Use `twMerge` to merge Tailwind classes: + +```ts +import { twMerge } from "tailwind-merge"; + +const { class: cls = "" } = $props(); + + +``` + +Avoid manual concatenation: + +```ts +// ❌ Don't do this: +class={"base " + cls} +``` + +--- + +### 🧒 Children Rendering and Snippets + +Use `@render` for rendering children: + +```svelte +{@render children?.()} +``` + +Children are either passed inline: + +```svelte + +``` + +Or wrapped in a named snippet: + +```svelte + +``` + +Never use unnamed snippet blocks: + +```svelte +// ❌ Invalid +{#snippet} + +{/snippet} +``` + +Do not declare `children?: Snippet` if it is already part of the extended HTML element type. + +--- + +### ✅ Summary Cheat Sheet + +- Always use `$props()`, `$bindable()`, `$state()`, `$effect()`, `$derived()` +- Avoid `export let`, `$:`, and `` +- Type props with `ComponentNameProps` +- Extend HTML props with `SvelteHTMLElements['tag']` +- Don’t re-declare built-in HTML attributes like `value`, `name`, or `children` +- Use `twMerge()` for class merging +- Render children using `@render children?.()` +- Never generate `{#snippet}` without a name + +--- + +# Project Architecture + +## Overview + +This project is a web application built using **SvelteKit** (Svelte 5) with the **Node.js adapter**. It uses **MySQL** as the database, managed by **Kysely** query builder. The frontend is styled with **TailwindCSS 4**. + +## Tech Stack + +### Core Framework + +- **SvelteKit**: Full-stack framework using File-System Based Routing. +- **Svelte 5**: Utilizing Runes system (`$state`, `$props`, `$derived`, `$effect`) for reactivity. +- **Vite**: Build tool for fast HMR and bundling. +- **TypeScript**: Strictly typed development. + +### Database & Backend + +- **MySQL**: Relational database (driver: `mysql2`). +- **Kysely**: Type-safe SQL query builder. +- **Kysely Codegen**: Generates TypeScript interfaces from the live database schema (DB First approach). +- **Core Library**: `@patrick115/sveltekitapi` (Custom wrapper for type-safe API, Router, and Procedure handling). +- **Auth**: `jsonwebtoken` (JWT) for session management & `bcrypt` for password hasing. +- **Zod**: Schema validation for API inputs and environment variables. + +### Styling & UI + +- **TailwindCSS 4**: Utility-first CSS framework. +- **Bootstrap Icons**: Iconography. +- **SweetAlert2**: Modal popups and alerts. +- **Chart.js**: Data visualization. + +### Utilities + +- **Marked**: Markdown parsing for rendering articles. +- **Sharp**: High-performance image processing (resizing, format conversion). +- **Dotenv**: Environment variable management. + +## Project Structure + +### `/src` + +The source code and logic of the application. + +- **`lib/`**: Shared library code, accesible via import alias `$lib/` or `$/`. + + - **`server/`**: **Server-side only** code. Can strictly be imported in `+page.server.ts`, `+server.ts`, etc. + - `api.ts`: Helper to create type-safe API definitions. + - `context.ts`: Defines the request context (authentication state, database connection, etc.). + - `cookies/`: Helper class `JWTCookies` for managing secure HTTP-only cookies. + - `routes.ts`: Main API router definition/aggregate. + - `server.ts`: Initializes the `APIServer` instance with the router and context. + - `variables.ts`: **Source of Truth** for server configuration. Initializes: + - `conn`: The Kysely MySQL connection instance. + - `jwt`: JWT configuration. + - Loads environment variables (`$env/static/private`). + - **`components/`**: Reusable Svelte UI components. + - **`lang/`**: Internationalization (i18n) logic. + - `index.ts`: Exports available languages and helper functions for translation resolution alongside `getPath` for localized routing. + - `state.svelte.ts`: **Global Client State**. Uses Svelte 5 runes (`$state`) to manage: + - `lang`: Current language dictionary. + - `selectedLang`: Current language key (e.g., 'en', 'cs'). + - `userState`: Logged-in user information. + - **`types/`**: Application-wide TypeScript definitions. + - `database.ts`: **Autogenerated** Kysely database types. Reflects the actual DB schema handles by `pnpm genDatabaseSchema`. + - `schemes.ts`: Zod schemas for validation. + +- **`routes/`**: File-system based routing. + - **`[[lang=lang]]/`**: Main application routes wrapped in an optional language parameter. + - Allows routes like `/about` (default lang) and `/cs/about` (explicit lang). + - Contains sub-routes: `admin/`, `gallery/`, `about/`, `contact/`. + - **`api/[...data]/`**: Catch-all API endpoint. + - Takes all requests starting with `/api/`. + - Delegates processing to `Server.handler` defined in `src/lib/server/server.ts`. + - This creates a centralized entry point for the type-safe API. + - **`image/[name]/`**: Image serving endpoint. + - Likely processes/serves images dynamically (e.g., resizing or retrieving from storage). + +### `/migrations` + +Contains database migration files written in TypeScript. + +- Using a custom migration runner (`script/runMigration.ts`). +- Migrations are timestamped (e.g., `20250504223140_logging.ts`). +- Tracks history to ensure the database schema is consistent across environments. + +### `/script` + +Utility scripts for maintenance. + +- `createMigration.sh`: Helper to scaffold a new migration file. +- `runMigration.ts`: The runner script for executing migrations (up/down). + +### `/uploads` + +Storage directory for user-uploaded content (images, scripts, etc.), often excluded from version control. + +## Implementation Details + +### State Management (Client) + +State is managed globally via `src/lib/state.svelte.ts`. It exports a reactive `state` object created with `$state()` rune. Components can import getters/setters to react to changes in: + +- Language (`lang`) +- User Authentication (`userState`) + +### API Architecture + +The project uses a TRPC-like pattern via `@patrick115/sveltekitapi`: + +1. **Context**: Created in `context.ts`, checking headers/cookies for JWT to determine `logged` status. +2. **Procedures**: Defined in `api.ts`. + - `procedure`: Public endpoint. + - `loggedProcedure`: Protected endpoint (throws 401 if not logged in). +3. **Router**: Multiple procedures are bundled into a router (`routes.ts`). +4. **Handling**: `src/routes/api/[...data]/+server.ts` maps HTTP methods (GET, POST, etc.) to the `Server.handler`. + +### Internationalization (i18n) + +- **URL Strategy**: Uses the `[[lang=lang]]` param matcher. +- **Dictionary**: Loaded from `src/lib/lang/`. +- **Resolution**: `resolveTranslation` function recursively looks up keys in the language object. + +## Useful Commands + +These commands are defined in `package.json` for development and maintenance. + +### Development + +- `pnpm dev`: Start the development server. +- `pnpm build`: Build the application for production. +- `pnpm preview`: Preview the production build locally. +- `pnpm check`: Run Svelte-check (TypeScript validation). +- `pnpm lint`: Run ESLint and Prettier checks. +- `pnpm format`: Automatically format code with Prettier. + +### Database + +- `pnpm genDatabaseSchema`: Introspect the database and regenerate TypeScript definitions in `src/types/database.ts`. **Run this after applying migrations.** +- `pnpm migration:create`: Create a new empty migration file in `migrations/` (prompts for name). +- `pnpm migrate:up`: Apply pending migrations. +- `pnpm migrate:down`: Revert the last batch of migrations. +- `pnpm migrate`: Run migrations (alias for checking status/running). + +### Deployment + +- `pnpm start`: Start the built Node.js server (requires `pnpm build` first). diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..db03656 --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,127 @@ +name: CI + +on: + push: + +jobs: + lint: + name: 'Lint code' + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install NodeJS + uses: actions/setup-node@v4 + with: + node-version: 20 + + - name: Install pnpm + run: npm install -g pnpm + + - name: Cache node_modules + uses: actions/cache@v3 + with: + path: node_modules + key: ${{ runner.os }}-pnpm-store-${{ hashFiles('pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-store- + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Copy env + run: cp .env.example .env + + - name: Build + run: pnpm run build + + - name: Check lint + run: pnpm run lint + + - name: TypeScript check + run: pnpm run check + + deploy: + name: Deploy + runs-on: ubuntu-latest + needs: lint + if: github.ref == 'refs/heads/main' + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Deploy via SSH + uses: appleboy/ssh-action@v1.2.0 + with: + host: ${{ secrets.HOST }} + username: ${{ secrets.USERNAME }} + port: ${{ secrets.PORT }} + key: ${{ secrets.SSH_KEY }} + script: | + cd /opt/NodeApps/Web + + echo "Setting up nvm" + export NVM_DIR="$HOME/.nvm" + source "$NVM_DIR/nvm.sh" + nvm use + + echo "Pulling latest changes from git" + git pull + + echo "Installing dependencies" + npm install -g pnpm + + echo "Installing dependencies" + pnpm install --frozen-lockfile + + echo "Building the app" + pnpm run build + + echo "Run migrations" + pnpm run migrate + + echo "Restarting the app" + sudo systemctl restart web + echo "Deployment completed" + deploybeta: + name: DeployBeta + runs-on: ubuntu-latest + needs: lint + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Deploy via SSH + uses: appleboy/ssh-action@v1.2.0 + with: + host: ${{ secrets.HOST }} + username: ${{ secrets.USERNAME }} + port: ${{ secrets.PORT }} + key: ${{ secrets.SSH_KEY }} + script: | + cd /opt/NodeApps/WebBeta + + echo "Setting up nvm" + export NVM_DIR="$HOME/.nvm" + source "$NVM_DIR/nvm.sh" + nvm use + + echo "Pulling latest changes from git" + git pull + + echo "Installing dependencies" + npm install -g pnpm + + echo "Installing dependencies" + pnpm install --frozen-lockfile + + echo "Building the app" + pnpm run build + + echo "Run migrations" + pnpm run migrate + + echo "Restarting the app" + sudo systemctl restart web-beta + echo "Deployment completed" diff --git a/.gitignore b/.gitignore index 2c3b871..b4eac77 100644 --- a/.gitignore +++ b/.gitignore @@ -1,19 +1,30 @@ -.DS_Store node_modules -/build + +# Output +.output +.vercel +.netlify +.wrangler /.svelte-kit -/package +/build + +# OS +.DS_Store +Thumbs.db + +# Env .env .env.* !.env.example +!.env.test + +# Vite vite.config.js.timestamp-* vite.config.ts.timestamp-* -#custom folders -/projects -.cache +# logs +logs/* -#lock files -pnpm-lock.yaml -package-lock.json -yarn.lock \ No newline at end of file +# image storage +.cache +uploads diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 0000000..09dd6bf --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1 @@ +pnpx lint-staged diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..517f386 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +v22.14.0 diff --git a/.prettierignore b/.prettierignore index cc41cea..6562bcb 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,4 +1,6 @@ -# Ignore files for PNPM, NPM and YARN -pnpm-lock.yaml +# Package Managers package-lock.json +pnpm-lock.yaml yarn.lock +bun.lock +bun.lockb diff --git a/.prettierrc b/.prettierrc index f7c913d..aae4c79 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,10 +1,16 @@ { - "useTabs": false, - "tabWidth": 4, - "singleQuote": true, - "trailingComma": "none", - "printWidth": 180, - "semi": true, - "plugins": ["prettier-plugin-svelte", "prettier-plugin-tailwindcss"], - "overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }] + "useTabs": false, + "singleQuote": true, + "trailingComma": "none", + "printWidth": 90, + "plugins": ["prettier-plugin-svelte", "prettier-plugin-tailwindcss"], + "overrides": [ + { + "files": "*.svelte", + "options": { + "parser": "svelte" + } + } + ], + "tabWidth": 2 } diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 72446f4..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "typescript.tsdk": "node_modules/typescript/lib" -} diff --git a/README.md b/README.md index 0d33873..76ea6fc 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Info -Source code of my main website [patrick115.eu](https://patrick115.eu) +TODO ## Dev mode @@ -8,6 +8,7 @@ Source code of my main website [patrick115.eu](https://patrick115.eu) npm run dev # or start the server and open the app in a new browser tab + npm run dev -- --open ``` @@ -19,26 +20,21 @@ Building app: npm run build ``` -Show builded preview: `npm run preview`. +Show builded preview: \`npm run preview\`. Start builded app using `npm run start` with [Node Adapter](https://kit.svelte.dev/docs/adapter-node) or config command in package.json using your own [Adapter](https://kit.svelte.dev/docs/adapters) ## Example ENV file (.env.example) ```YAML -#webserver config HOST=0.0.0.0 PORT=5178 ORIGIN=http://localhost:5178 -PUBLIC_ORIGIN=https://myweb.cz -BODY_SIZE_LIMIT=16777216 # max upload size in bytes -#database config DATABASE_IP=10.10.10.223 DATABASE_PORT=3306 DATABASE_USER=superclovek DATABASE_PASSWORD=tajnyheslo123456 -DATABASE_NAME=website -DATABASE_URL=mysql://superclovek:tajnyheslo123456@10.10.10.223:3306/website -#secret pro JWT (tím se bude podepisovat JWT token - https://jwt.io/) +DATABASE_NAME=db +DATABASE_URL=mysql://superclovek:tajnyheslo123456@10.10.10.223:3306/db JWT_SECRET=text #v sekundách (10 min = 10 * 60) #expiruje pouze pokud uživatel danou dobu nic nedělá (neprochází stránky) diff --git a/config/patrick115.eu b/config/patrick115.eu new file mode 100755 index 0000000..81c9c40 --- /dev/null +++ b/config/patrick115.eu @@ -0,0 +1,25 @@ +server { + listen 80; + listen [::]:80; + + server_name beta.patrick115.eu; + + return 301 https://$host$request_uri; + +} + +server { + + listen [::]:443 ssl http2; + listen 443 ssl http2; + + server_name beta.patrick115.eu; + + access_log /opt/NodeApps/WebBeta/logs/access.log; + error_log /opt/NodeApps/WebBeta/logs/error.log; + + location / { + proxy_set_header X-Real-IP $remote_addr; + proxy_pass "http://127.0.0.1:5188"; + } +} diff --git a/config/web.service b/config/web.service new file mode 120000 index 0000000..708300e --- /dev/null +++ b/config/web.service @@ -0,0 +1 @@ +/etc/systemd/system/web.service \ No newline at end of file diff --git a/eslint.config.js b/eslint.config.js index 28faeea..0ba7d1c 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,36 +1,48 @@ import prettier from 'eslint-config-prettier'; import js from '@eslint/js'; +import { includeIgnoreFile } from '@eslint/compat'; import svelte from 'eslint-plugin-svelte'; import globals from 'globals'; +import { fileURLToPath } from 'node:url'; import ts from 'typescript-eslint'; +import svelteConfig from './svelte.config.js'; + +const gitignorePath = fileURLToPath(new URL('./.gitignore', import.meta.url)); export default ts.config( + includeIgnoreFile(gitignorePath), js.configs.recommended, ...ts.configs.recommended, - ...svelte.configs['flat/recommended'], + ...svelte.configs.recommended, prettier, - ...svelte.configs['flat/prettier'], + ...svelte.configs.prettier, { languageOptions: { - globals: { - ...globals.browser, - ...globals.node - } + globals: { ...globals.browser, ...globals.node } + }, + rules: { 'no-undef': 'off' } + }, + { + files: ['./src/lib/server/_routes/**/*.ts'], + rules: { + 'no-console': ['error', { allow: ['error'] }] } }, { - files: ['**/*.svelte'], + ignores: ['./src/lib/server/_routes/**/*.ts'], rules: { - 'svelte/no-at-html-tags': 'off' - }, - + 'no-console': 'error' + } + }, + { + files: ['**/*.svelte', '**/*.svelte.ts', '**/*.svelte.js'], languageOptions: { parserOptions: { - parser: ts.parser + projectService: true, + extraFileExtensions: ['.svelte'], + parser: ts.parser, + svelteConfig } } - }, - { - ignores: ['build/', '.svelte-kit/', 'dist/'] } ); diff --git a/kysely.config.json b/kysely.config.json new file mode 100644 index 0000000..5400a6d --- /dev/null +++ b/kysely.config.json @@ -0,0 +1,16 @@ +{ + "outFile": "./src/types/database.ts", + "overrides": { + "columns": { + "article.id": "Generated", + "article.title": "string", + "article.description": "string", + "article.content_md": "string", + "article_equipment.article_id": "string", + "exposure.article_id": "string", + "gallery_image.article_id": "string", + "translations.key": "string", + "gallery_image.alt_text": "string" + } + } +} diff --git a/logs/.gitkeep b/logs/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/migrations/20250504223140_logging.ts b/migrations/20250504223140_logging.ts new file mode 100644 index 0000000..711b982 --- /dev/null +++ b/migrations/20250504223140_logging.ts @@ -0,0 +1,18 @@ +/*eslint-disable @typescript-eslint/no-explicit-any*/ + +import { Kysely, sql } from 'kysely'; + +export const up = async (conn: Kysely) => { + await conn.schema + .createTable('visitors') + .addColumn('id', 'integer', (col) => col.autoIncrement().primaryKey()) + .addColumn('ip', 'varchar(255)', (col) => col.notNull()) + .addColumn('user_agent', 'varchar(255)', (col) => col.notNull()) + .addColumn('date', 'timestamp', (col) => col.notNull().defaultTo(sql`now()`)) + .addColumn('page', 'varchar(255)', (col) => col.notNull()) + .execute(); +}; + +export const down = async (conn: Kysely) => { + await conn.schema.dropTable('visitors').execute(); +}; diff --git a/migrations/20250505162104_account.ts b/migrations/20250505162104_account.ts new file mode 100644 index 0000000..25efb9f --- /dev/null +++ b/migrations/20250505162104_account.ts @@ -0,0 +1,28 @@ +/*eslint-disable @typescript-eslint/no-explicit-any*/ + +import * as bcrypt from 'bcrypt'; +import { Kysely } from 'kysely'; + +export const up = async (conn: Kysely) => { + await conn.schema + .createTable('account') + .addColumn('id', 'integer', (col) => col.autoIncrement().primaryKey()) + .addColumn('username', 'varchar(64)', (col) => col.notNull()) + .addColumn('password', 'varchar(60)', (col) => col.notNull()) + .execute(); + + await conn + .insertInto('account') + .values({ + username: process.env.DEFAULT_USERNAME!, + password: bcrypt.hashSync( + process.env.DEFAULT_PASSWORD!, + parseInt(process.env.HASH_ROUNDS!) + ) + }) + .execute(); +}; + +export const down = async (conn: Kysely) => { + await conn.schema.dropTable('account').execute(); +}; diff --git a/migrations/20250509192723_gallery.ts b/migrations/20250509192723_gallery.ts new file mode 100644 index 0000000..c033563 --- /dev/null +++ b/migrations/20250509192723_gallery.ts @@ -0,0 +1,68 @@ +/*eslint-disable @typescript-eslint/no-explicit-any*/ + +import { Kysely, sql } from 'kysely'; + +export const up = async (conn: Kysely) => { + await conn.schema + .createTable('equipment_type') + .addColumn('id', 'integer', (col) => col.primaryKey().autoIncrement()) + .addColumn('lang_key', 'varchar(64)', (col) => col.notNull()) + .addColumn('priority', 'integer', (col) => col.notNull().defaultTo(0)) + .execute(); + + await conn.schema + .createTable('equipment') + .addColumn('id', 'integer', (col) => col.primaryKey().autoIncrement()) + .addColumn('type_id', 'integer', (col) => + col.notNull().references('equipment_type.id') + ) + .addColumn('name', 'varchar(128)', (col) => col.notNull()) + .addColumn('link', 'varchar(512)', (col) => col.notNull()) + .execute(); + + await conn.schema + .createTable('article') + .addColumn('id', 'uuid', (col) => col.primaryKey()) + .addColumn('title', 'varchar(56)', (col) => col.notNull()) + .addColumn('description', 'varchar(128)', (col) => col.notNull()) + .addColumn('content_md', 'text', (col) => col.notNull()) + .addColumn('created_at', 'timestamp', (col) => col.defaultTo(sql`now()`).notNull()) + .addColumn('updated_at', 'timestamp', (col) => col.defaultTo(sql`now()`).notNull()) + .execute(); + + await conn.schema + .createTable('article_equipment') + .addColumn('id', 'integer', (col) => col.primaryKey().autoIncrement()) + .addColumn('article_id', 'uuid', (col) => col.notNull().references('article.id')) + .addColumn('equipment_id', 'integer', (col) => + col.notNull().references('equipment.id') + ) + .execute(); + + await conn.schema + .createTable('gallery_image') + .addColumn('id', 'integer', (col) => col.primaryKey().autoIncrement()) + .addColumn('name', 'varchar(50)', (col) => col.notNull()) + .addColumn('article_id', 'uuid', (col) => col.notNull().references('article.id')) + .addColumn('alt_text', 'varchar(256)', (col) => col.notNull()) + .execute(); + + await conn.schema + .createTable('exposure') + .addColumn('id', 'integer', (col) => col.primaryKey().autoIncrement()) + .addColumn('article_id', 'uuid', (col) => col.notNull().references('article.id')) + .addColumn('date', 'date', (col) => col.notNull()) + .addColumn('type', 'varchar(16)', (col) => col.notNull()) // e.g. 'light', 'dark', 'flat' + .addColumn('count', 'integer', (col) => col.notNull()) + .addColumn('exposure_time_s', 'real', (col) => col.notNull()) + .execute(); +}; + +export const down = async (conn: Kysely) => { + await conn.schema.dropTable('exposure').execute(); + await conn.schema.dropTable('gallery_image').execute(); + await conn.schema.dropTable('article_equipment').execute(); + await conn.schema.dropTable('article').execute(); + await conn.schema.dropTable('equipment').execute(); + await conn.schema.dropTable('equipment_type').execute(); +}; diff --git a/migrations/20250706142207_translations.ts b/migrations/20250706142207_translations.ts new file mode 100644 index 0000000..c615965 --- /dev/null +++ b/migrations/20250706142207_translations.ts @@ -0,0 +1,192 @@ +/*eslint-disable @typescript-eslint/no-explicit-any*/ + +import { Kysely, sql } from 'kysely'; +import { v4 } from 'uuid'; + +export const up = async (conn: Kysely) => { + await conn.schema + .createTable('translations') + .addColumn('key', 'uuid') + .addColumn('lang', 'varchar(2)') + .addColumn('text', 'text', (col) => col.notNull()) + .addPrimaryKeyConstraint('pk_translations', ['key', 'lang']) + .execute(); + + //now we need to migrate posts texts into translations table + const posts = await conn.selectFrom('article').selectAll().execute(); + + //here we move the content_md of each post into translations table + for (const post of posts) { + const titleUuid = v4(); + await conn.transaction().execute(async (trx) => { + await trx + .insertInto('translations') + .values({ + key: titleUuid, + lang: 'cs', + text: post.title + }) + .execute(); + await trx + .updateTable('article') + .set({ + title: titleUuid + }) + .where('id', '=', post.id) + .execute(); + }); + + const descriptionUuid = v4(); + await conn.transaction().execute(async (trx) => { + await trx + .insertInto('translations') + .values({ + key: descriptionUuid, + lang: 'cs', + text: post.description + }) + .execute(); + await trx + .updateTable('article') + .set({ + description: descriptionUuid + }) + .where('id', '=', post.id) + .execute(); + }); + + const contentUuid = v4(); + await conn.transaction().execute(async (trx) => { + await trx + .insertInto('translations') + .values({ + key: contentUuid, + lang: 'cs', + text: post.content_md + }) + .execute(); + await trx + .updateTable('article') + .set({ + content_md: contentUuid + }) + .where('id', '=', post.id) + .execute(); + }); + } + + const images = await conn.selectFrom('gallery_image').selectAll().execute(); + + for (const image of images) { + const uuid = v4(); + await conn.transaction().execute(async (trx) => { + await trx + .insertInto('translations') + .values({ + key: uuid, + lang: 'cs', + text: image.alt_text + }) + .execute(); + await trx + .updateTable('gallery_image') + .set({ + alt_text: uuid + }) + .where('id', '=', image.id) + .execute(); + }); + } + + await sql`ALTER TABLE article CHANGE title title UUID NOT NULL`.execute(conn); + await sql`ALTER TABLE article CHANGE description description UUID NOT NULL`.execute( + conn + ); + await sql`ALTER TABLE article CHANGE content_md content_md UUID NOT NULL`.execute(conn); + await sql`ALTER TABLE gallery_image CHANGE alt_text alt_text UUID NOT NULL`.execute( + conn + ); + + await conn.schema + .alterTable('article') + .addForeignKeyConstraint('title_fk', ['title'], 'translations', ['key']) + .execute(); + await conn.schema + .alterTable('article') + .addForeignKeyConstraint('description_fk', ['description'], 'translations', ['key']) + .execute(); + await conn.schema + .alterTable('article') + .addForeignKeyConstraint('content_md_fk', ['content_md'], 'translations', ['key']) + .execute(); + await conn.schema + .alterTable('gallery_image') + .addForeignKeyConstraint('alt_text_fk', ['alt_text'], 'translations', ['key']) + .execute(); +}; + +export const down = async (conn: Kysely) => { + // first we need to remove the foreign key constraint + await sql`ALTER TABLE article DROP FOREIGN KEY title_fk`.execute(conn); + await sql`ALTER TABLE article DROP FOREIGN KEY description_fk`.execute(conn); + await sql`ALTER TABLE article DROP FOREIGN KEY content_md_fk`.execute(conn); + await sql`ALTER TABLE gallery_image DROP FOREIGN KEY alt_text_fk`.execute(conn); + // then we need to move the content_md back to article table + const articles = await conn.selectFrom('article').selectAll().execute(); + const translations = await conn + .selectFrom('translations') + .selectAll() + .where('lang', '=', 'cs') + .execute(); + + await sql`ALTER TABLE article CHANGE title title varchar(56) NOT NULL`.execute(conn); + await sql`ALTER TABLE article CHANGE description description varchar(128) NOT NULL`.execute( + conn + ); + await sql`ALTER TABLE article CHANGE content_md content_md text NOT NULL`.execute(conn); + await sql`ALTER TABLE gallery_image CHANGE alt_text alt_text varchar(256) NOT NULL`.execute( + conn + ); + + const updateCol = async ( + type: 'title' | 'description' | 'content_md', + id: string, + key: string + ) => { + const value = translations.find((t) => t.key === key); + if (value) { + await conn + .updateTable('article') + .set({ + [type]: value.text + }) + .where('id', '=', id) + .execute(); + } + }; + + //now update the articles with the content_md + for (const article of articles) { + await updateCol('title', article.id, article.title); + await updateCol('description', article.id, article.description); + await updateCol('content_md', article.id, article.content_md); + } + + //now update images + const images = await conn.selectFrom('gallery_image').selectAll().execute(); + for (const image of images) { + const value = translations.find((t) => t.key === image.alt_text); + if (value) { + await conn + .updateTable('gallery_image') + .set({ + alt_text: value.text + }) + .where('id', '=', image.id) + .execute(); + } + } + + // finally we can drop the translations table + await conn.schema.dropTable('translations').execute(); +}; diff --git a/package.json b/package.json index 3b5eb87..d3e4ffd 100644 --- a/package.json +++ b/package.json @@ -1,66 +1,80 @@ { - "name": "mainweb", - "version": "0.0.5", - "private": true, - "scripts": { - "dev": "vite dev", - "build": "vite build", - "preview": "vite preview", - "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", - "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", - "lint": "prettier --plugin prettier-plugin-svelte --plugin prettier-plugin-tailwindcss --check . && eslint .", - "format": "prettier --plugin prettier-plugin-svelte --plugin prettier-plugin-tailwindcss --write .", - "genDatabaseSchema": "kysely-codegen --out-file ./src/types/database.ts", - "start": "node -r dotenv/config build" - }, - "devDependencies": { - "@eslint/js": "^9.17.0", - "@sveltejs/adapter-node": "^5.2.11", - "@sveltejs/kit": "^2.13.0", - "@sveltejs/vite-plugin-svelte": "^5.0.3", - "@types/bcrypt": "^5.0.2", - "@types/eslint": "9.6.1", - "@types/jsonwebtoken": "^9.0.7", - "@types/path-browserify": "^1.0.3", - "@types/uuid": "^10.0.0", - "@typescript-eslint/eslint-plugin": "^8.18.1", - "@typescript-eslint/parser": "^8.18.1", - "autoprefixer": "^10.4.20", - "eslint": "^9.17.0", - "eslint-config-prettier": "^9.1.0", - "eslint-plugin-svelte": "^2.46.1", - "globals": "^15.14.0", - "kysely-codegen": "^0.17.0", - "postcss": "^8.4.49", - "prettier": "^3.4.2", - "prettier-plugin-svelte": "^3.3.2", - "prettier-plugin-tailwindcss": "^0.6.9", - "svelte": "^5.14.5", - "svelte-check": "^4.1.1", - "tailwindcss": "^3.4.17", - "tslib": "^2.8.1", - "typescript": "^5.7.2", - "typescript-eslint": "^8.18.1", - "vite": "^6.0.4" - }, - "type": "module", - "dependencies": { - "@patrick115/sveltekitapi": "^1.2.12", - "bcrypt": "^5.1.1", - "bootstrap-icons": "^1.11.3", - "chart.js": "^4.4.7", - "date-fns": "^4.1.0", - "dompurify": "^3.2.3", - "dotenv": "^16.4.7", - "jsonwebtoken": "^9.0.2", - "kysely": "^0.27.5", - "mysql2": "^3.11.5", - "path-browserify": "^1.0.1", - "sharp": "^0.33.5", - "simple-json-db": "^2.0.0", - "sweetalert2": "^11.15.2", - "tailwind-merge": "^2.5.5", - "uuid": "^11.0.3", - "zod": "^3.24.1" - } + "name": "web", + "private": true, + "version": "0.0.1", + "type": "module", + "scripts": { + "dev": "vite dev", + "build": "vite build", + "preview": "vite preview", + "prepare": "husky", + "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", + "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", + "format": "prettier --write src migrations", + "lint": "prettier --check src migrations && eslint src migrations", + "genDatabaseSchema": "kysely-codegen --config-file kysely.config.json", + "start": "node -r dotenv/config build", + "migration:create": "./script/createMigration.sh", + "migrate": "tsx ./script/runMigration.ts", + "migrate:up": "tsx ./script/runMigration.ts up", + "migrate:down": "tsx ./script/runMigration.ts down" + }, + "devDependencies": { + "@eslint/compat": "^1.2.9", + "@eslint/js": "^9.26.0", + "@sveltejs/adapter-node": "^5.2.12", + "@sveltejs/kit": "^2.31.1", + "@sveltejs/vite-plugin-svelte": "^5.0.3", + "@tailwindcss/typography": "^0.5.16", + "@tailwindcss/vite": "^4.1.5", + "@types/bcrypt": "^5.0.2", + "@types/jsonwebtoken": "^9.0.9", + "@types/uuid": "^10.0.0", + "eslint": "^9.26.0", + "eslint-config-prettier": "^10.1.3", + "eslint-plugin-svelte": "^3.5.1", + "globals": "^16.3.0", + "husky": "^9.1.7", + "kysely-codegen": "^0.18.5", + "lint-staged": "^16.1.2", + "prettier": "^3.5.3", + "prettier-plugin-svelte": "^3.3.3", + "prettier-plugin-tailwindcss": "^0.6.11", + "svelte": "^5.38.1", + "svelte-check": "^4.1.7", + "tailwindcss": "^4.1.5", + "tsx": "^4.20.3", + "typescript": "^5.8.3", + "typescript-eslint": "^8.32.0", + "vite": "^6.3.5" + }, + "dependencies": { + "@patrick115/sveltekitapi": "^1.3.1", + "bcrypt": "^5.1.1", + "bootstrap-icons": "^1.12.1", + "chart.js": "^4.4.9", + "clsx": "^2.1.1", + "dotenv": "^16.5.0", + "jsonwebtoken": "^9.0.2", + "kysely": "^0.28.2", + "marked": "^15.0.12", + "mysql2": "^3.14.1", + "sharp": "^0.34.2", + "simple-json-db": "^2.0.0", + "starback": "github:patrick11514/starback.js", + "sweetalert2": "^11.21.0", + "tailwind-merge": "^3.2.0", + "uuid": "^11.1.0", + "zod": "^4.0.17" + }, + "lint-staged": { + "src/**/*.{ts,svelte}": [ + "prettier --write", + "eslint --fix" + ], + "migrations/**/*.ts": [ + "prettier --write", + "eslint --fix" + ] + } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..4cab5ef --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,4790 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@patrick115/sveltekitapi': + specifier: ^1.3.1 + version: 1.3.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.38.1)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0)))(svelte@5.38.1)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0)) + bcrypt: + specifier: ^5.1.1 + version: 5.1.1 + bootstrap-icons: + specifier: ^1.12.1 + version: 1.12.1 + chart.js: + specifier: ^4.4.9 + version: 4.4.9 + clsx: + specifier: ^2.1.1 + version: 2.1.1 + dotenv: + specifier: ^16.5.0 + version: 16.5.0 + jsonwebtoken: + specifier: ^9.0.2 + version: 9.0.2 + kysely: + specifier: ^0.28.2 + version: 0.28.2 + marked: + specifier: ^15.0.12 + version: 15.0.12 + mysql2: + specifier: ^3.14.1 + version: 3.14.1 + sharp: + specifier: ^0.34.2 + version: 0.34.2 + simple-json-db: + specifier: ^2.0.0 + version: 2.0.0 + starback: + specifier: github:patrick11514/starback.js + version: https://codeload.github.com/patrick11514/starback.js/tar.gz/fe6f8be567f2e4c4d2eadb06c007544c58dd3a0d + sweetalert2: + specifier: ^11.21.0 + version: 11.21.0 + tailwind-merge: + specifier: ^3.2.0 + version: 3.2.0 + uuid: + specifier: ^11.1.0 + version: 11.1.0 + zod: + specifier: ^4.0.17 + version: 4.0.17 + devDependencies: + '@eslint/compat': + specifier: ^1.2.9 + version: 1.2.9(eslint@9.26.0(jiti@2.4.2)) + '@eslint/js': + specifier: ^9.26.0 + version: 9.26.0 + '@sveltejs/adapter-node': + specifier: ^5.2.12 + version: 5.2.12(@sveltejs/kit@2.31.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.38.1)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0)))(svelte@5.38.1)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0))) + '@sveltejs/kit': + specifier: ^2.31.1 + version: 2.31.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.38.1)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0)))(svelte@5.38.1)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0)) + '@sveltejs/vite-plugin-svelte': + specifier: ^5.0.3 + version: 5.0.3(svelte@5.38.1)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0)) + '@tailwindcss/typography': + specifier: ^0.5.16 + version: 0.5.16(tailwindcss@4.1.5) + '@tailwindcss/vite': + specifier: ^4.1.5 + version: 4.1.5(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0)) + '@types/bcrypt': + specifier: ^5.0.2 + version: 5.0.2 + '@types/jsonwebtoken': + specifier: ^9.0.9 + version: 9.0.9 + '@types/uuid': + specifier: ^10.0.0 + version: 10.0.0 + eslint: + specifier: ^9.26.0 + version: 9.26.0(jiti@2.4.2) + eslint-config-prettier: + specifier: ^10.1.3 + version: 10.1.3(eslint@9.26.0(jiti@2.4.2)) + eslint-plugin-svelte: + specifier: ^3.5.1 + version: 3.5.1(eslint@9.26.0(jiti@2.4.2))(svelte@5.38.1) + globals: + specifier: ^16.3.0 + version: 16.3.0 + husky: + specifier: ^9.1.7 + version: 9.1.7 + kysely-codegen: + specifier: ^0.18.5 + version: 0.18.5(kysely@0.28.2)(mysql2@3.14.1)(typescript@5.8.3) + lint-staged: + specifier: ^16.1.2 + version: 16.1.2 + prettier: + specifier: ^3.5.3 + version: 3.5.3 + prettier-plugin-svelte: + specifier: ^3.3.3 + version: 3.3.3(prettier@3.5.3)(svelte@5.38.1) + prettier-plugin-tailwindcss: + specifier: ^0.6.11 + version: 0.6.11(prettier-plugin-svelte@3.3.3(prettier@3.5.3)(svelte@5.38.1))(prettier@3.5.3) + svelte: + specifier: ^5.38.1 + version: 5.38.1 + svelte-check: + specifier: ^4.1.7 + version: 4.1.7(picomatch@4.0.2)(svelte@5.38.1)(typescript@5.8.3) + tailwindcss: + specifier: ^4.1.5 + version: 4.1.5 + tsx: + specifier: ^4.20.3 + version: 4.20.3 + typescript: + specifier: ^5.8.3 + version: 5.8.3 + typescript-eslint: + specifier: ^8.32.0 + version: 8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) + vite: + specifier: ^6.3.5 + version: 6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0) + +packages: + + '@babel/code-frame@7.27.1': + resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.27.1': + resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} + engines: {node: '>=6.9.0'} + + '@emnapi/runtime@1.4.3': + resolution: {integrity: sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==} + + '@esbuild/aix-ppc64@0.25.4': + resolution: {integrity: sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.25.4': + resolution: {integrity: sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.25.4': + resolution: {integrity: sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.25.4': + resolution: {integrity: sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.25.4': + resolution: {integrity: sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.25.4': + resolution: {integrity: sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.25.4': + resolution: {integrity: sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.25.4': + resolution: {integrity: sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.25.4': + resolution: {integrity: sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.25.4': + resolution: {integrity: sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.25.4': + resolution: {integrity: sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.25.4': + resolution: {integrity: sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.25.4': + resolution: {integrity: sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.25.4': + resolution: {integrity: sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.25.4': + resolution: {integrity: sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.25.4': + resolution: {integrity: sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.25.4': + resolution: {integrity: sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.4': + resolution: {integrity: sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.4': + resolution: {integrity: sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.25.4': + resolution: {integrity: sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.4': + resolution: {integrity: sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.25.4': + resolution: {integrity: sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.25.4': + resolution: {integrity: sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.25.4': + resolution: {integrity: sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.25.4': + resolution: {integrity: sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@eslint-community/eslint-utils@4.7.0': + resolution: {integrity: sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.1': + resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/compat@1.2.9': + resolution: {integrity: sha512-gCdSY54n7k+driCadyMNv8JSPzYLeDVM/ikZRtvtROBpRdFSkS8W9A82MqsaY7lZuwL0wiapgD0NT1xT0hyJsA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^9.10.0 + peerDependenciesMeta: + eslint: + optional: true + + '@eslint/config-array@0.20.0': + resolution: {integrity: sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/config-helpers@0.2.2': + resolution: {integrity: sha512-+GPzk8PlG0sPpzdU5ZvIRMPidzAnZDl/s9L+y13iodqvb8leL53bTannOrQ/Im7UkpsmFU5Ily5U60LWixnmLg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.13.0': + resolution: {integrity: sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/eslintrc@3.3.1': + resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.26.0': + resolution: {integrity: sha512-I9XlJawFdSMvWjDt6wksMCrgns5ggLNfFwFvnShsleWruvXM514Qxk8V246efTw+eo9JABvVz+u3q2RiAowKxQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/object-schema@2.1.6': + resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/plugin-kit@0.2.8': + resolution: {integrity: sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@humanfs/core@0.19.1': + resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.6': + resolution: {integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==} + engines: {node: '>=18.18.0'} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/retry@0.3.1': + resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==} + engines: {node: '>=18.18'} + + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} + + '@img/sharp-darwin-arm64@0.34.2': + resolution: {integrity: sha512-OfXHZPppddivUJnqyKoi5YVeHRkkNE2zUFT2gbpKxp/JZCFYEYubnMg+gOp6lWfasPrTS+KPosKqdI+ELYVDtg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [darwin] + + '@img/sharp-darwin-x64@0.34.2': + resolution: {integrity: sha512-dYvWqmjU9VxqXmjEtjmvHnGqF8GrVjM2Epj9rJ6BUIXvk8slvNDJbhGFvIoXzkDhrJC2jUxNLz/GUjjvSzfw+g==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-darwin-arm64@1.1.0': + resolution: {integrity: sha512-HZ/JUmPwrJSoM4DIQPv/BfNh9yrOA8tlBbqbLz4JZ5uew2+o22Ik+tHQJcih7QJuSa0zo5coHTfD5J8inqj9DA==} + cpu: [arm64] + os: [darwin] + + '@img/sharp-libvips-darwin-x64@1.1.0': + resolution: {integrity: sha512-Xzc2ToEmHN+hfvsl9wja0RlnXEgpKNmftriQp6XzY/RaSfwD9th+MSh0WQKzUreLKKINb3afirxW7A0fz2YWuQ==} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-linux-arm64@1.1.0': + resolution: {integrity: sha512-IVfGJa7gjChDET1dK9SekxFFdflarnUB8PwW8aGwEoF3oAsSDuNUTYS+SKDOyOJxQyDC1aPFMuRYLoDInyV9Ew==} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linux-arm@1.1.0': + resolution: {integrity: sha512-s8BAd0lwUIvYCJyRdFqvsj+BJIpDBSxs6ivrOPm/R7piTs5UIwY5OjXrP2bqXC9/moGsyRa37eYWYCOGVXxVrA==} + cpu: [arm] + os: [linux] + + '@img/sharp-libvips-linux-ppc64@1.1.0': + resolution: {integrity: sha512-tiXxFZFbhnkWE2LA8oQj7KYR+bWBkiV2nilRldT7bqoEZ4HiDOcePr9wVDAZPi/Id5fT1oY9iGnDq20cwUz8lQ==} + cpu: [ppc64] + os: [linux] + + '@img/sharp-libvips-linux-s390x@1.1.0': + resolution: {integrity: sha512-xukSwvhguw7COyzvmjydRb3x/09+21HykyapcZchiCUkTThEQEOMtBj9UhkaBRLuBrgLFzQ2wbxdeCCJW/jgJA==} + cpu: [s390x] + os: [linux] + + '@img/sharp-libvips-linux-x64@1.1.0': + resolution: {integrity: sha512-yRj2+reB8iMg9W5sULM3S74jVS7zqSzHG3Ol/twnAAkAhnGQnpjj6e4ayUz7V+FpKypwgs82xbRdYtchTTUB+Q==} + cpu: [x64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-arm64@1.1.0': + resolution: {integrity: sha512-jYZdG+whg0MDK+q2COKbYidaqW/WTz0cc1E+tMAusiDygrM4ypmSCjOJPmFTvHHJ8j/6cAGyeDWZOsK06tP33w==} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-x64@1.1.0': + resolution: {integrity: sha512-wK7SBdwrAiycjXdkPnGCPLjYb9lD4l6Ze2gSdAGVZrEL05AOUJESWU2lhlC+Ffn5/G+VKuSm6zzbQSzFX/P65A==} + cpu: [x64] + os: [linux] + + '@img/sharp-linux-arm64@0.34.2': + resolution: {integrity: sha512-D8n8wgWmPDakc83LORcfJepdOSN6MvWNzzz2ux0MnIbOqdieRZwVYY32zxVx+IFUT8er5KPcyU3XXsn+GzG/0Q==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + + '@img/sharp-linux-arm@0.34.2': + resolution: {integrity: sha512-0DZzkvuEOqQUP9mo2kjjKNok5AmnOr1jB2XYjkaoNRwpAYMDzRmAqUIa1nRi58S2WswqSfPOWLNOr0FDT3H5RQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm] + os: [linux] + + '@img/sharp-linux-s390x@0.34.2': + resolution: {integrity: sha512-EGZ1xwhBI7dNISwxjChqBGELCWMGDvmxZXKjQRuqMrakhO8QoMgqCrdjnAqJq/CScxfRn+Bb7suXBElKQpPDiw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [s390x] + os: [linux] + + '@img/sharp-linux-x64@0.34.2': + resolution: {integrity: sha512-sD7J+h5nFLMMmOXYH4DD9UtSNBD05tWSSdWAcEyzqW8Cn5UxXvsHAxmxSesYUsTOBmUnjtxghKDl15EvfqLFbQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + + '@img/sharp-linuxmusl-arm64@0.34.2': + resolution: {integrity: sha512-NEE2vQ6wcxYav1/A22OOxoSOGiKnNmDzCYFOZ949xFmrWZOVII1Bp3NqVVpvj+3UeHMFyN5eP/V5hzViQ5CZNA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + + '@img/sharp-linuxmusl-x64@0.34.2': + resolution: {integrity: sha512-DOYMrDm5E6/8bm/yQLCWyuDJwUnlevR8xtF8bs+gjZ7cyUNYXiSf/E8Kp0Ss5xasIaXSHzb888V1BE4i1hFhAA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + + '@img/sharp-wasm32@0.34.2': + resolution: {integrity: sha512-/VI4mdlJ9zkaq53MbIG6rZY+QRN3MLbR6usYlgITEzi4Rpx5S6LFKsycOQjkOGmqTNmkIdLjEvooFKwww6OpdQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [wasm32] + + '@img/sharp-win32-arm64@0.34.2': + resolution: {integrity: sha512-cfP/r9FdS63VA5k0xiqaNaEoGxBg9k7uE+RQGzuK9fHt7jib4zAVVseR9LsE4gJcNWgT6APKMNnCcnyOtmSEUQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [win32] + + '@img/sharp-win32-ia32@0.34.2': + resolution: {integrity: sha512-QLjGGvAbj0X/FXl8n1WbtQ6iVBpWU7JO94u/P2M4a8CFYsvQi4GW2mRy/JqkRx0qpBzaOdKJKw8uc930EX2AHw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ia32] + os: [win32] + + '@img/sharp-win32-x64@0.34.2': + resolution: {integrity: sha512-aUdT6zEYtDKCaxkofmmJDJYGCf0+pJg3eU9/oBuqvEeoB9dKI6ZLc/1iLJCTuJQDO4ptntAlkUmHgGjyuobZbw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [win32] + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.0': + resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.25': + resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + + '@jridgewell/trace-mapping@0.3.30': + resolution: {integrity: sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==} + + '@kurkle/color@0.3.4': + resolution: {integrity: sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==} + + '@mapbox/node-pre-gyp@1.0.11': + resolution: {integrity: sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==} + hasBin: true + + '@modelcontextprotocol/sdk@1.11.1': + resolution: {integrity: sha512-9LfmxKTb1v+vUS1/emSk1f5ePmTLkb9Le9AxOB5T0XM59EUumwcS45z05h7aiZx3GI0Bl7mjb3FMEglYj+acuQ==} + engines: {node: '>=18'} + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@patrick115/sveltekitapi@1.3.1': + resolution: {integrity: sha512-Tk50419up1pOWdm4LlXXgiO1tmfwWqqoDkZLOV4oyw9DdijDS71rr+ZRIuOMVSv8bQ4rvXEmMnrSa4hWLnjKPg==} + + '@polka/url@1.0.0-next.29': + resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} + + '@rollup/plugin-commonjs@28.0.3': + resolution: {integrity: sha512-pyltgilam1QPdn+Zd9gaCfOLcnjMEJ9gV+bTw6/r73INdvzf1ah9zLIJBm+kW7R6IUFIQ1YO+VqZtYxZNWFPEQ==} + engines: {node: '>=16.0.0 || 14 >= 14.17'} + peerDependencies: + rollup: ^2.68.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/plugin-json@6.1.0': + resolution: {integrity: sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/plugin-node-resolve@16.0.1': + resolution: {integrity: sha512-tk5YCxJWIG81umIvNkSod2qK5KyQW19qcBF/B78n1bjtOON6gzKoVeSzAE8yHCZEDmqkHKkxplExA8KzdJLJpA==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^2.78.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/pluginutils@5.1.4': + resolution: {integrity: sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/rollup-android-arm-eabi@4.40.2': + resolution: {integrity: sha512-JkdNEq+DFxZfUwxvB58tHMHBHVgX23ew41g1OQinthJ+ryhdRk67O31S7sYw8u2lTjHUPFxwar07BBt1KHp/hg==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.40.2': + resolution: {integrity: sha512-13unNoZ8NzUmnndhPTkWPWbX3vtHodYmy+I9kuLxN+F+l+x3LdVF7UCu8TWVMt1POHLh6oDHhnOA04n8oJZhBw==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.40.2': + resolution: {integrity: sha512-Gzf1Hn2Aoe8VZzevHostPX23U7N5+4D36WJNHK88NZHCJr7aVMG4fadqkIf72eqVPGjGc0HJHNuUaUcxiR+N/w==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.40.2': + resolution: {integrity: sha512-47N4hxa01a4x6XnJoskMKTS8XZ0CZMd8YTbINbi+w03A2w4j1RTlnGHOz/P0+Bg1LaVL6ufZyNprSg+fW5nYQQ==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.40.2': + resolution: {integrity: sha512-8t6aL4MD+rXSHHZUR1z19+9OFJ2rl1wGKvckN47XFRVO+QL/dUSpKA2SLRo4vMg7ELA8pzGpC+W9OEd1Z/ZqoQ==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.40.2': + resolution: {integrity: sha512-C+AyHBzfpsOEYRFjztcYUFsH4S7UsE9cDtHCtma5BK8+ydOZYgMmWg1d/4KBytQspJCld8ZIujFMAdKG1xyr4Q==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.40.2': + resolution: {integrity: sha512-de6TFZYIvJwRNjmW3+gaXiZ2DaWL5D5yGmSYzkdzjBDS3W+B9JQ48oZEsmMvemqjtAFzE16DIBLqd6IQQRuG9Q==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.40.2': + resolution: {integrity: sha512-urjaEZubdIkacKc930hUDOfQPysezKla/O9qV+O89enqsqUmQm8Xj8O/vh0gHg4LYfv7Y7UsE3QjzLQzDYN1qg==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.40.2': + resolution: {integrity: sha512-KlE8IC0HFOC33taNt1zR8qNlBYHj31qGT1UqWqtvR/+NuCVhfufAq9fxO8BMFC22Wu0rxOwGVWxtCMvZVLmhQg==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.40.2': + resolution: {integrity: sha512-j8CgxvfM0kbnhu4XgjnCWJQyyBOeBI1Zq91Z850aUddUmPeQvuAy6OiMdPS46gNFgy8gN1xkYyLgwLYZG3rBOg==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loongarch64-gnu@4.40.2': + resolution: {integrity: sha512-Ybc/1qUampKuRF4tQXc7G7QY9YRyeVSykfK36Y5Qc5dmrIxwFhrOzqaVTNoZygqZ1ZieSWTibfFhQ5qK8jpWxw==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-powerpc64le-gnu@4.40.2': + resolution: {integrity: sha512-3FCIrnrt03CCsZqSYAOW/k9n625pjpuMzVfeI+ZBUSDT3MVIFDSPfSUgIl9FqUftxcUXInvFah79hE1c9abD+Q==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.40.2': + resolution: {integrity: sha512-QNU7BFHEvHMp2ESSY3SozIkBPaPBDTsfVNGx3Xhv+TdvWXFGOSH2NJvhD1zKAT6AyuuErJgbdvaJhYVhVqrWTg==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.40.2': + resolution: {integrity: sha512-5W6vNYkhgfh7URiXTO1E9a0cy4fSgfE4+Hl5agb/U1sa0kjOLMLC1wObxwKxecE17j0URxuTrYZZME4/VH57Hg==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.40.2': + resolution: {integrity: sha512-B7LKIz+0+p348JoAL4X/YxGx9zOx3sR+o6Hj15Y3aaApNfAshK8+mWZEf759DXfRLeL2vg5LYJBB7DdcleYCoQ==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.40.2': + resolution: {integrity: sha512-lG7Xa+BmBNwpjmVUbmyKxdQJ3Q6whHjMjzQplOs5Z+Gj7mxPtWakGHqzMqNER68G67kmCX9qX57aRsW5V0VOng==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.40.2': + resolution: {integrity: sha512-tD46wKHd+KJvsmije4bUskNuvWKFcTOIM9tZ/RrmIvcXnbi0YK/cKS9FzFtAm7Oxi2EhV5N2OpfFB348vSQRXA==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-win32-arm64-msvc@4.40.2': + resolution: {integrity: sha512-Bjv/HG8RRWLNkXwQQemdsWw4Mg+IJ29LK+bJPW2SCzPKOUaMmPEppQlu/Fqk1d7+DX3V7JbFdbkh/NMmurT6Pg==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.40.2': + resolution: {integrity: sha512-dt1llVSGEsGKvzeIO76HToiYPNPYPkmjhMHhP00T9S4rDern8P2ZWvWAQUEJ+R1UdMWJ/42i/QqJ2WV765GZcA==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.40.2': + resolution: {integrity: sha512-bwspbWB04XJpeElvsp+DCylKfF4trJDa2Y9Go8O6A7YLX2LIKGcNK/CYImJN6ZP4DcuOHB4Utl3iCbnR62DudA==} + cpu: [x64] + os: [win32] + + '@standard-schema/spec@1.0.0': + resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} + + '@sveltejs/acorn-typescript@1.0.5': + resolution: {integrity: sha512-IwQk4yfwLdibDlrXVE04jTZYlLnwsTT2PIOQQGNLWfjavGifnk1JD1LcZjZaBTRcxZu2FfPfNLOE04DSu9lqtQ==} + peerDependencies: + acorn: ^8.9.0 + + '@sveltejs/adapter-node@5.2.12': + resolution: {integrity: sha512-0bp4Yb3jKIEcZWVcJC/L1xXp9zzJS4hDwfb4VITAkfT4OVdkspSHsx7YhqJDbb2hgLl6R9Vs7VQR+fqIVOxPUQ==} + peerDependencies: + '@sveltejs/kit': ^2.4.0 + + '@sveltejs/kit@2.31.1': + resolution: {integrity: sha512-Iv98PKh81amOjIWZ6Flqr6E7xYcrrYZ4mY9XwYUvaCDiQ4hYt5+jXR9CivcgLOTK+RcJ3K4guEYF4lFYQfTxaA==} + engines: {node: '>=18.13'} + hasBin: true + peerDependencies: + '@opentelemetry/api': ^1.0.0 + '@sveltejs/vite-plugin-svelte': ^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0 + svelte: ^4.0.0 || ^5.0.0-next.0 + vite: ^5.0.3 || ^6.0.0 || ^7.0.0-beta.0 + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + + '@sveltejs/vite-plugin-svelte-inspector@4.0.1': + resolution: {integrity: sha512-J/Nmb2Q2y7mck2hyCX4ckVHcR5tu2J+MtBEQqpDrrgELZ2uvraQcK/ioCV61AqkdXFgriksOKIceDcQmqnGhVw==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22} + peerDependencies: + '@sveltejs/vite-plugin-svelte': ^5.0.0 + svelte: ^5.0.0 + vite: ^6.0.0 + + '@sveltejs/vite-plugin-svelte@5.0.3': + resolution: {integrity: sha512-MCFS6CrQDu1yGwspm4qtli0e63vaPCehf6V7pIMP15AsWgMKrqDGCPFF/0kn4SP0ii4aySu4Pa62+fIRGFMjgw==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22} + peerDependencies: + svelte: ^5.0.0 + vite: ^6.0.0 + + '@tailwindcss/node@4.1.5': + resolution: {integrity: sha512-CBhSWo0vLnWhXIvpD0qsPephiaUYfHUX3U9anwDaHZAeuGpTiB3XmsxPAN6qX7bFhipyGBqOa1QYQVVhkOUGxg==} + + '@tailwindcss/oxide-android-arm64@4.1.5': + resolution: {integrity: sha512-LVvM0GirXHED02j7hSECm8l9GGJ1RfgpWCW+DRn5TvSaxVsv28gRtoL4aWKGnXqwvI3zu1GABeDNDVZeDPOQrw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [android] + + '@tailwindcss/oxide-darwin-arm64@4.1.5': + resolution: {integrity: sha512-//TfCA3pNrgnw4rRJOqavW7XUk8gsg9ddi8cwcsWXp99tzdBAZW0WXrD8wDyNbqjW316Pk2hiN/NJx/KWHl8oA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@tailwindcss/oxide-darwin-x64@4.1.5': + resolution: {integrity: sha512-XQorp3Q6/WzRd9OalgHgaqgEbjP3qjHrlSUb5k1EuS1Z9NE9+BbzSORraO+ecW432cbCN7RVGGL/lSnHxcd+7Q==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@tailwindcss/oxide-freebsd-x64@4.1.5': + resolution: {integrity: sha512-bPrLWbxo8gAo97ZmrCbOdtlz/Dkuy8NK97aFbVpkJ2nJ2Jo/rsCbu0TlGx8joCuA3q6vMWTSn01JY46iwG+clg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [freebsd] + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.5': + resolution: {integrity: sha512-1gtQJY9JzMAhgAfvd/ZaVOjh/Ju/nCoAsvOVJenWZfs05wb8zq+GOTnZALWGqKIYEtyNpCzvMk+ocGpxwdvaVg==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-gnu@4.1.5': + resolution: {integrity: sha512-dtlaHU2v7MtdxBXoqhxwsWjav7oim7Whc6S9wq/i/uUMTWAzq/gijq1InSgn2yTnh43kR+SFvcSyEF0GCNu1PQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-musl@4.1.5': + resolution: {integrity: sha512-fg0F6nAeYcJ3CriqDT1iVrqALMwD37+sLzXs8Rjy8Z1ZHshJoYceodfyUwGJEsQoTyWbliFNRs2wMQNXtT7MVA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-gnu@4.1.5': + resolution: {integrity: sha512-SO+F2YEIAHa1AITwc8oPwMOWhgorPzzcbhWEb+4oLi953h45FklDmM8dPSZ7hNHpIk9p/SCZKUYn35t5fjGtHA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-musl@4.1.5': + resolution: {integrity: sha512-6UbBBplywkk/R+PqqioskUeXfKcBht3KU7juTi1UszJLx0KPXUo10v2Ok04iBJIaDPkIFkUOVboXms5Yxvaz+g==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-wasm32-wasi@4.1.5': + resolution: {integrity: sha512-hwALf2K9FHuiXTPqmo1KeOb83fTRNbe9r/Ixv9ZNQ/R24yw8Ge1HOWDDgTdtzntIaIUJG5dfXCf4g9AD4RiyhQ==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + bundledDependencies: + - '@napi-rs/wasm-runtime' + - '@emnapi/core' + - '@emnapi/runtime' + - '@tybys/wasm-util' + - '@emnapi/wasi-threads' + - tslib + + '@tailwindcss/oxide-win32-arm64-msvc@4.1.5': + resolution: {integrity: sha512-oDKncffWzaovJbkuR7/OTNFRJQVdiw/n8HnzaCItrNQUeQgjy7oUiYpsm9HUBgpmvmDpSSbGaCa2Evzvk3eFmA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@tailwindcss/oxide-win32-x64-msvc@4.1.5': + resolution: {integrity: sha512-WiR4dtyrFdbb+ov0LK+7XsFOsG+0xs0PKZKkt41KDn9jYpO7baE3bXiudPVkTqUEwNfiglCygQHl2jklvSBi7Q==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@tailwindcss/oxide@4.1.5': + resolution: {integrity: sha512-1n4br1znquEvyW/QuqMKQZlBen+jxAbvyduU87RS8R3tUSvByAkcaMTkJepNIrTlYhD+U25K4iiCIxE6BGdRYA==} + engines: {node: '>= 10'} + + '@tailwindcss/typography@0.5.16': + resolution: {integrity: sha512-0wDLwCVF5V3x3b1SGXPCDcdsbDHMBe+lkFzBRaHeLvNi+nrrnZ1lA18u+OTWO8iSWU2GxUOCvlXtDuqftc1oiA==} + peerDependencies: + tailwindcss: '>=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1' + + '@tailwindcss/vite@4.1.5': + resolution: {integrity: sha512-FE1stRoqdHSb7RxesMfCXE8icwI1W6zGE/512ae3ZDrpkQYTTYeSyUJPRCjZd8CwVAhpDUbi1YR8pcZioFJQ/w==} + peerDependencies: + vite: ^5.2.0 || ^6 + + '@types/bcrypt@5.0.2': + resolution: {integrity: sha512-6atioO8Y75fNcbmj0G7UjI9lXN2pQ/IGJ2FWT4a/btd0Lk9lQalHLKhkgKVZ3r+spnmWUKfbMi1GEe9wyHQfNQ==} + + '@types/cookie@0.6.0': + resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} + + '@types/estree@1.0.7': + resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/jsonwebtoken@9.0.9': + resolution: {integrity: sha512-uoe+GxEuHbvy12OUQct2X9JenKM3qAscquYymuQN4fMWG9DBQtykrQEFcAbVACF7qaLw9BePSodUL0kquqBJpQ==} + + '@types/ms@2.1.0': + resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} + + '@types/node@22.15.17': + resolution: {integrity: sha512-wIX2aSZL5FE+MR0JlvF87BNVrtFWf6AE6rxSE9X7OwnVvoyCQjpzSRJ+M87se/4QCkCiebQAqrJ0y6fwIyi7nw==} + + '@types/resolve@1.20.2': + resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} + + '@types/uuid@10.0.0': + resolution: {integrity: sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==} + + '@typescript-eslint/eslint-plugin@8.32.0': + resolution: {integrity: sha512-/jU9ettcntkBFmWUzzGgsClEi2ZFiikMX5eEQsmxIAWMOn4H3D4rvHssstmAHGVvrYnaMqdWWWg0b5M6IN/MTQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/parser@8.32.0': + resolution: {integrity: sha512-B2MdzyWxCE2+SqiZHAjPphft+/2x2FlO9YBx7eKE1BCb+rqBlQdhtAEhzIEdozHd55DXPmxBdpMygFJjfjjA9A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/scope-manager@8.32.0': + resolution: {integrity: sha512-jc/4IxGNedXkmG4mx4nJTILb6TMjL66D41vyeaPWvDUmeYQzF3lKtN15WsAeTr65ce4mPxwopPSo1yUUAWw0hQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/type-utils@8.32.0': + resolution: {integrity: sha512-t2vouuYQKEKSLtJaa5bB4jHeha2HJczQ6E5IXPDPgIty9EqcJxpr1QHQ86YyIPwDwxvUmLfP2YADQ5ZY4qddZg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/types@8.32.0': + resolution: {integrity: sha512-O5Id6tGadAZEMThM6L9HmVf5hQUXNSxLVKeGJYWNhhVseps/0LddMkp7//VDkzwJ69lPL0UmZdcZwggj9akJaA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.32.0': + resolution: {integrity: sha512-pU9VD7anSCOIoBFnhTGfOzlVFQIA1XXiQpH/CezqOBaDppRwTglJzCC6fUQGpfwey4T183NKhF1/mfatYmjRqQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/utils@8.32.0': + resolution: {integrity: sha512-8S9hXau6nQ/sYVtC3D6ISIDoJzS1NsCK+gluVhLN2YkBPX+/1wkwyUiDKnxRh15579WoOIyVWnoyIf3yGI9REw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/visitor-keys@8.32.0': + resolution: {integrity: sha512-1rYQTCLFFzOI5Nl0c8LUpJT8HxpwVRn9E4CkMsYfuN6ctmQqExjSTzzSk0Tz2apmXy7WU6/6fyaZVVA/thPN+w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + abbrev@1.1.1: + resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} + + accepts@2.0.0: + resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} + engines: {node: '>= 0.6'} + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.14.1: + resolution: {integrity: sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==} + engines: {node: '>=0.4.0'} + hasBin: true + + acorn@8.15.0: + resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} + engines: {node: '>=0.4.0'} + hasBin: true + + agent-base@6.0.2: + resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} + engines: {node: '>= 6.0.0'} + + ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + + ansi-escapes@7.0.0: + resolution: {integrity: sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==} + engines: {node: '>=18'} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.1.0: + resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} + engines: {node: '>=12'} + + ansi-styles@3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@6.2.1: + resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + engines: {node: '>=12'} + + aproba@2.0.0: + resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==} + + are-we-there-yet@2.0.0: + resolution: {integrity: sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==} + engines: {node: '>=10'} + deprecated: This package is no longer supported. + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + aria-query@5.3.2: + resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} + engines: {node: '>= 0.4'} + + aws-ssl-profiles@1.1.2: + resolution: {integrity: sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==} + engines: {node: '>= 6.0.0'} + + axobject-query@4.1.0: + resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} + engines: {node: '>= 0.4'} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + bcrypt@5.1.1: + resolution: {integrity: sha512-AGBHOG5hPYZ5Xl9KXzU5iKq9516yEmvCKDg3ecP5kX2aB6UqTeXZxk2ELnDgDm6BQSMlLt9rDB4LoSMx0rYwww==} + engines: {node: '>= 10.0.0'} + + body-parser@2.2.0: + resolution: {integrity: sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==} + engines: {node: '>=18'} + + bootstrap-icons@1.12.1: + resolution: {integrity: sha512-ekwupjsteHQmgGV+haQ0nNMoSyKCbJj5ou+06vFzb9uR2/bwN9isNEgXBaQzcT+fLzhKS3OaBNpwz8XdZlIgYQ==} + + brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + + brace-expansion@2.0.1: + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + buffer-equal-constant-time@1.0.1: + resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} + + bytes@3.1.2: + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + chalk@2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + chalk@5.4.1: + resolution: {integrity: sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + + chart.js@4.4.9: + resolution: {integrity: sha512-EyZ9wWKgpAU0fLJ43YAEIF8sr5F2W3LqbS40ZJyHIner2lY14ufqv2VMp69MAiZ2rpwxEUxEhIH/0U3xyRynxg==} + engines: {pnpm: '>=8'} + + chokidar@4.0.3: + resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} + engines: {node: '>= 14.16.0'} + + chownr@2.0.0: + resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} + engines: {node: '>=10'} + + cli-cursor@5.0.0: + resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} + engines: {node: '>=18'} + + cli-truncate@4.0.0: + resolution: {integrity: sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==} + engines: {node: '>=18'} + + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + + color-convert@1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + color-string@1.9.1: + resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} + + color-support@1.1.3: + resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==} + hasBin: true + + color@4.2.3: + resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==} + engines: {node: '>=12.5.0'} + + colorette@2.0.20: + resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} + + commander@14.0.0: + resolution: {integrity: sha512-2uM9rYjPvyq39NwLRqaiLtWHyDC1FvryJDa2ATTVims5YAS4PupsEQsDvP14FqhFr0P49CYDugi59xaxJlTXRA==} + engines: {node: '>=20'} + + commondir@1.0.1: + resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + console-control-strings@1.1.0: + resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==} + + content-disposition@1.0.0: + resolution: {integrity: sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==} + engines: {node: '>= 0.6'} + + content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + + cookie-signature@1.2.2: + resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} + engines: {node: '>=6.6.0'} + + cookie@0.6.0: + resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} + engines: {node: '>= 0.6'} + + cookie@0.7.2: + resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} + engines: {node: '>= 0.6'} + + cors@2.8.5: + resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} + engines: {node: '>= 0.10'} + + cosmiconfig@9.0.0: + resolution: {integrity: sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + + debug@4.4.0: + resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.4.1: + resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + + delegates@1.0.0: + resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} + + denque@2.1.0: + resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==} + engines: {node: '>=0.10'} + + depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + + detect-libc@2.0.4: + resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==} + engines: {node: '>=8'} + + devalue@5.1.1: + resolution: {integrity: sha512-maua5KUiapvEwiEAe+XnlZ3Rh0GD+qI1J/nb9vrJc3muPXvcF/8gXYTWF76+5DAqHyDUtOIImEuo0YKE9mshVw==} + + diff@3.5.0: + resolution: {integrity: sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==} + engines: {node: '>=0.3.1'} + + dotenv-expand@12.0.2: + resolution: {integrity: sha512-lXpXz2ZE1cea1gL4sz2Ipj8y4PiVjytYr3Ij0SWoms1PGxIv7m2CRKuRuCRtHdVuvM/hNJPMxt5PbhboNC4dPQ==} + engines: {node: '>=12'} + + dotenv@16.5.0: + resolution: {integrity: sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==} + engines: {node: '>=12'} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + ecdsa-sig-formatter@1.0.11: + resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} + + ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + + emoji-regex@10.4.0: + resolution: {integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + encodeurl@2.0.0: + resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} + engines: {node: '>= 0.8'} + + enhanced-resolve@5.18.1: + resolution: {integrity: sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==} + engines: {node: '>=10.13.0'} + + env-paths@2.2.1: + resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} + engines: {node: '>=6'} + + environment@1.1.0: + resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} + engines: {node: '>=18'} + + error-ex@1.3.2: + resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + esbuild@0.25.4: + resolution: {integrity: sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q==} + engines: {node: '>=18'} + hasBin: true + + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + + escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + eslint-config-prettier@10.1.3: + resolution: {integrity: sha512-vDo4d9yQE+cS2tdIT4J02H/16veRvkHgiLDRpej+WL67oCfbOb97itZXn8wMPJ/GsiEBVjrjs//AVNw2Cp1EcA==} + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + + eslint-plugin-svelte@3.5.1: + resolution: {integrity: sha512-Qn1slddZHfqYiDO6IN8/iN3YL+VuHlgYjm30FT+hh0Jf/TX0jeZMTJXQMajFm5f6f6hURi+XO8P+NPYD+T4jkg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.1 || ^9.0.0 + svelte: ^3.37.0 || ^4.0.0 || ^5.0.0 + peerDependenciesMeta: + svelte: + optional: true + + eslint-scope@8.3.0: + resolution: {integrity: sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@4.2.0: + resolution: {integrity: sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint@9.26.0: + resolution: {integrity: sha512-Hx0MOjPh6uK9oq9nVsATZKE/Wlbai7KFjfCuw9UHaguDW3x+HF0O5nIi3ud39TWgrTjTO5nHxmL3R1eANinWHQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + esm-env@1.2.2: + resolution: {integrity: sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==} + + espree@10.3.0: + resolution: {integrity: sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + esquery@1.6.0: + resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} + engines: {node: '>=0.10'} + + esrap@2.1.0: + resolution: {integrity: sha512-yzmPNpl7TBbMRC5Lj2JlJZNPml0tzqoqP5B1JXycNUwtqma9AKCO0M2wHrdgsHcy1WRW7S9rJknAMtByg3usgA==} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + + eventemitter3@5.0.1: + resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + + eventsource-parser@3.0.1: + resolution: {integrity: sha512-VARTJ9CYeuQYb0pZEPbzi740OWFgpHe7AYJ2WFZVnUDUQp5Dk2yJUgF36YsZ81cOyxT0QxmXD2EQpapAouzWVA==} + engines: {node: '>=18.0.0'} + + eventsource@3.0.6: + resolution: {integrity: sha512-l19WpE2m9hSuyP06+FbuUUf1G+R0SFLrtQfbRb9PRr+oimOfxQhgGCbVaXg5IvZyyTThJsxh6L/srkMiCeBPDA==} + engines: {node: '>=18.0.0'} + + express-rate-limit@7.5.0: + resolution: {integrity: sha512-eB5zbQh5h+VenMPM3fh+nw1YExi5nMr6HUCR62ELSP11huvxm/Uir1H1QEyTkk5QX6A58pX6NmaTMceKZ0Eodg==} + engines: {node: '>= 16'} + peerDependencies: + express: ^4.11 || 5 || ^5.0.0-beta.1 + + express@5.1.0: + resolution: {integrity: sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==} + engines: {node: '>= 18'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fastq@1.19.1: + resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} + + fdir@6.4.4: + resolution: {integrity: sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + finalhandler@2.1.0: + resolution: {integrity: sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==} + engines: {node: '>= 0.8'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + + flatted@3.3.3: + resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + + forwarded@0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} + + fresh@2.0.0: + resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} + engines: {node: '>= 0.8'} + + fs-minipass@2.1.0: + resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} + engines: {node: '>= 8'} + + fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + gauge@3.0.2: + resolution: {integrity: sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==} + engines: {node: '>=10'} + deprecated: This package is no longer supported. + + generate-function@2.3.1: + resolution: {integrity: sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==} + + get-east-asian-width@1.3.0: + resolution: {integrity: sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==} + engines: {node: '>=18'} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + get-tsconfig@4.10.1: + resolution: {integrity: sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==} + + git-diff@2.0.6: + resolution: {integrity: sha512-/Iu4prUrydE3Pb3lCBMbcSNIf81tgGt0W1ZwknnyF62t3tHmtiJTRj0f+1ZIhp3+Rh0ktz1pJVoa7ZXUCskivA==} + engines: {node: '>= 4.8.0'} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + deprecated: Glob versions prior to v9 are no longer supported + + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + + globals@16.3.0: + resolution: {integrity: sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ==} + engines: {node: '>=18'} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + + has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-unicode@2.0.1: + resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + http-errors@2.0.0: + resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} + engines: {node: '>= 0.8'} + + https-proxy-agent@5.0.1: + resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} + engines: {node: '>= 6'} + + husky@9.1.7: + resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==} + engines: {node: '>=18'} + hasBin: true + + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + interpret@1.4.0: + resolution: {integrity: sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==} + engines: {node: '>= 0.10'} + + ipaddr.js@1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} + + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + + is-arrayish@0.3.2: + resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} + + is-core-module@2.16.1: + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + engines: {node: '>= 0.4'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-fullwidth-code-point@4.0.0: + resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==} + engines: {node: '>=12'} + + is-fullwidth-code-point@5.0.0: + resolution: {integrity: sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==} + engines: {node: '>=18'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-module@1.0.0: + resolution: {integrity: sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-promise@4.0.0: + resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} + + is-property@1.0.2: + resolution: {integrity: sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==} + + is-reference@1.2.1: + resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==} + + is-reference@3.0.3: + resolution: {integrity: sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + jiti@2.4.2: + resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==} + hasBin: true + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + jsonwebtoken@9.0.2: + resolution: {integrity: sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==} + engines: {node: '>=12', npm: '>=6'} + + jwa@1.4.2: + resolution: {integrity: sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==} + + jws@3.2.2: + resolution: {integrity: sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + kleur@4.1.5: + resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} + engines: {node: '>=6'} + + known-css-properties@0.35.0: + resolution: {integrity: sha512-a/RAk2BfKk+WFGhhOCAYqSiFLc34k8Mt/6NWRI4joER0EYUzXIcFivjjnoD3+XU1DggLn/tZc3DOAgke7l8a4A==} + + kysely-codegen@0.18.5: + resolution: {integrity: sha512-bj6DMsXcKo0PrrXUk/fdjFgNC6Pwq+HPBCqhNGuD57gwUJZdci2s2OqhNneQeYpAIWGot7/481WdzTyXrClY2Q==} + engines: {node: '>=20.0.0'} + hasBin: true + peerDependencies: + '@libsql/kysely-libsql': '>=0.3.0 <0.5.0' + '@tediousjs/connection-string': '>=0.5.0 <0.6.0' + better-sqlite3: '>=7.6.2 <8.0.0' + kysely: '>=0.27.0 <1.0.0' + kysely-bun-sqlite: '>=0.3.2 <1.0.0' + kysely-bun-worker: '>=1.2.0 <2.0.0' + mysql2: '>=2.3.3 <4.0.0' + pg: '>=8.8.0 <9.0.0' + tarn: '>=3.0.0 <4.0.0' + tedious: '>=18.0.0 <20.0.0' + peerDependenciesMeta: + '@libsql/kysely-libsql': + optional: true + '@tediousjs/connection-string': + optional: true + better-sqlite3: + optional: true + kysely-bun-sqlite: + optional: true + kysely-bun-worker: + optional: true + mysql2: + optional: true + pg: + optional: true + tarn: + optional: true + tedious: + optional: true + + kysely@0.28.2: + resolution: {integrity: sha512-4YAVLoF0Sf0UTqlhgQMFU9iQECdah7n+13ANkiuVfRvlK+uI0Etbgd7bVP36dKlG+NXWbhGua8vnGt+sdhvT7A==} + engines: {node: '>=18.0.0'} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + lightningcss-darwin-arm64@1.29.2: + resolution: {integrity: sha512-cK/eMabSViKn/PG8U/a7aCorpeKLMlK0bQeNHmdb7qUnBkNPnL+oV5DjJUo0kqWsJUapZsM4jCfYItbqBDvlcA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.29.2: + resolution: {integrity: sha512-j5qYxamyQw4kDXX5hnnCKMf3mLlHvG44f24Qyi2965/Ycz829MYqjrVg2H8BidybHBp9kom4D7DR5VqCKDXS0w==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.29.2: + resolution: {integrity: sha512-wDk7M2tM78Ii8ek9YjnY8MjV5f5JN2qNVO+/0BAGZRvXKtQrBC4/cn4ssQIpKIPP44YXw6gFdpUF+Ps+RGsCwg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.29.2: + resolution: {integrity: sha512-IRUrOrAF2Z+KExdExe3Rz7NSTuuJ2HvCGlMKoquK5pjvo2JY4Rybr+NrKnq0U0hZnx5AnGsuFHjGnNT14w26sg==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.29.2: + resolution: {integrity: sha512-KKCpOlmhdjvUTX/mBuaKemp0oeDIBBLFiU5Fnqxh1/DZ4JPZi4evEH7TKoSBFOSOV3J7iEmmBaw/8dpiUvRKlQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-arm64-musl@1.29.2: + resolution: {integrity: sha512-Q64eM1bPlOOUgxFmoPUefqzY1yV3ctFPE6d/Vt7WzLW4rKTv7MyYNky+FWxRpLkNASTnKQUaiMJ87zNODIrrKQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-x64-gnu@1.29.2: + resolution: {integrity: sha512-0v6idDCPG6epLXtBH/RPkHvYx74CVziHo6TMYga8O2EiQApnUPZsbR9nFNrg2cgBzk1AYqEd95TlrsL7nYABQg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-linux-x64-musl@1.29.2: + resolution: {integrity: sha512-rMpz2yawkgGT8RULc5S4WiZopVMOFWjiItBT7aSfDX4NQav6M44rhn5hjtkKzB+wMTRlLLqxkeYEtQ3dd9696w==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-win32-arm64-msvc@1.29.2: + resolution: {integrity: sha512-nL7zRW6evGQqYVu/bKGK+zShyz8OVzsCotFgc7judbt6wnB2KbiKKJwBE4SGoDBQ1O94RjW4asrCjQL4i8Fhbw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.29.2: + resolution: {integrity: sha512-EdIUW3B2vLuHmv7urfzMI/h2fmlnOQBk1xlsDxkN1tCWKjNFjfLhGxYk8C8mzpSfr+A6jFFIi8fU6LbQGsRWjA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.29.2: + resolution: {integrity: sha512-6b6gd/RUXKaw5keVdSEtqFVdzWnU5jMxTUjA2bVcMNPLwSQ08Sv/UodBVtETLCn7k4S1Ibxwh7k68IwLZPgKaA==} + engines: {node: '>= 12.0.0'} + + lilconfig@2.1.0: + resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} + engines: {node: '>=10'} + + lilconfig@3.1.3: + resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} + engines: {node: '>=14'} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + lint-staged@16.1.2: + resolution: {integrity: sha512-sQKw2Si2g9KUZNY3XNvRuDq4UJqpHwF0/FQzZR2M7I5MvtpWvibikCjUVJzZdGE0ByurEl3KQNvsGetd1ty1/Q==} + engines: {node: '>=20.17'} + hasBin: true + + listr2@8.3.3: + resolution: {integrity: sha512-LWzX2KsqcB1wqQ4AHgYb4RsDXauQiqhjLk+6hjbaeHG4zpjjVAB6wC/gz6X0l+Du1cN3pUB5ZlrvTbhGSNnUQQ==} + engines: {node: '>=18.0.0'} + + locate-character@3.0.0: + resolution: {integrity: sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lodash.castarray@4.4.0: + resolution: {integrity: sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==} + + lodash.includes@4.3.0: + resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==} + + lodash.isboolean@3.0.3: + resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==} + + lodash.isinteger@4.0.4: + resolution: {integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==} + + lodash.isnumber@3.0.3: + resolution: {integrity: sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==} + + lodash.isplainobject@4.0.6: + resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} + + lodash.isstring@4.0.1: + resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + lodash.once@4.1.1: + resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==} + + log-update@6.1.0: + resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} + engines: {node: '>=18'} + + loglevel@1.9.2: + resolution: {integrity: sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==} + engines: {node: '>= 0.6.0'} + + long@5.3.2: + resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==} + + lru-cache@7.18.3: + resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} + engines: {node: '>=12'} + + lru.min@1.1.2: + resolution: {integrity: sha512-Nv9KddBcQSlQopmBHXSsZVY5xsdlZkdH/Iey0BlcBYggMd4two7cZnKOK9vmy3nY0O5RGH99z1PCeTpPqszUYg==} + engines: {bun: '>=1.0.0', deno: '>=1.30.0', node: '>=8.0.0'} + + magic-string@0.30.17: + resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} + + make-dir@3.1.0: + resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} + engines: {node: '>=8'} + + marked@15.0.12: + resolution: {integrity: sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==} + engines: {node: '>= 18'} + hasBin: true + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + media-typer@1.1.0: + resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} + engines: {node: '>= 0.8'} + + merge-descriptors@2.0.0: + resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} + engines: {node: '>=18'} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + mime-db@1.54.0: + resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} + engines: {node: '>= 0.6'} + + mime-types@3.0.1: + resolution: {integrity: sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==} + engines: {node: '>= 0.6'} + + mimic-function@5.0.1: + resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} + engines: {node: '>=18'} + + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + minipass@3.3.6: + resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} + engines: {node: '>=8'} + + minipass@5.0.0: + resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} + engines: {node: '>=8'} + + minizlib@2.1.2: + resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} + engines: {node: '>= 8'} + + mkdirp@1.0.4: + resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} + engines: {node: '>=10'} + hasBin: true + + mri@1.2.0: + resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} + engines: {node: '>=4'} + + mrmime@2.0.1: + resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} + engines: {node: '>=10'} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + mysql2@3.14.1: + resolution: {integrity: sha512-7ytuPQJjQB8TNAYX/H2yhL+iQOnIBjAMam361R7UAL0lOVXWjtdrmoL9HYKqKoLp/8UUTRcvo1QPvK9KL7wA8w==} + engines: {node: '>= 8.0'} + + named-placeholders@1.1.3: + resolution: {integrity: sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==} + engines: {node: '>=12.0.0'} + + nano-spawn@1.0.2: + resolution: {integrity: sha512-21t+ozMQDAL/UGgQVBbZ/xXvNO10++ZPuTmKRO8k9V3AClVRht49ahtDjfY8l1q6nSHOrE5ASfthzH3ol6R/hg==} + engines: {node: '>=20.17'} + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + negotiator@1.0.0: + resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} + engines: {node: '>= 0.6'} + + node-addon-api@5.1.0: + resolution: {integrity: sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==} + + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + + nopt@5.0.0: + resolution: {integrity: sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==} + engines: {node: '>=6'} + hasBin: true + + npmlog@5.0.1: + resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==} + deprecated: This package is no longer supported. + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + + on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + onetime@7.0.0: + resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} + engines: {node: '>=18'} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + + parseurl@1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-to-regexp@8.2.0: + resolution: {integrity: sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==} + engines: {node: '>=16'} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + picomatch@4.0.2: + resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} + engines: {node: '>=12'} + + pidtree@0.6.0: + resolution: {integrity: sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==} + engines: {node: '>=0.10'} + hasBin: true + + pkce-challenge@5.0.0: + resolution: {integrity: sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ==} + engines: {node: '>=16.20.0'} + + pluralize@8.0.0: + resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} + engines: {node: '>=4'} + + postcss-load-config@3.1.4: + resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==} + engines: {node: '>= 10'} + peerDependencies: + postcss: '>=8.0.9' + ts-node: '>=9.0.0' + peerDependenciesMeta: + postcss: + optional: true + ts-node: + optional: true + + postcss-safe-parser@7.0.1: + resolution: {integrity: sha512-0AioNCJZ2DPYz5ABT6bddIqlhgwhpHZ/l65YAYo0BCIn0xiDpsnTHz0gnoTGk0OXZW0JRs+cDwL8u/teRdz+8A==} + engines: {node: '>=18.0'} + peerDependencies: + postcss: ^8.4.31 + + postcss-scss@4.0.9: + resolution: {integrity: sha512-AjKOeiwAitL/MXxQW2DliT28EKukvvbEWx3LBmJIRN8KfBGZbRTxNYW0kSqi1COiTZ57nZ9NW06S6ux//N1c9A==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.4.29 + + postcss-selector-parser@6.0.10: + resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==} + engines: {node: '>=4'} + + postcss-selector-parser@7.1.0: + resolution: {integrity: sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==} + engines: {node: '>=4'} + + postcss@8.5.3: + resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==} + engines: {node: ^10 || ^12 || >=14} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + prettier-plugin-svelte@3.3.3: + resolution: {integrity: sha512-yViK9zqQ+H2qZD1w/bH7W8i+bVfKrD8GIFjkFe4Thl6kCT9SlAsXVNmt3jCvQOCsnOhcvYgsoVlRV/Eu6x5nNw==} + peerDependencies: + prettier: ^3.0.0 + svelte: ^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0 + + prettier-plugin-tailwindcss@0.6.11: + resolution: {integrity: sha512-YxaYSIvZPAqhrrEpRtonnrXdghZg1irNg4qrjboCXrpybLWVs55cW2N3juhspVJiO0JBvYJT8SYsJpc8OQSnsA==} + engines: {node: '>=14.21.3'} + peerDependencies: + '@ianvs/prettier-plugin-sort-imports': '*' + '@prettier/plugin-pug': '*' + '@shopify/prettier-plugin-liquid': '*' + '@trivago/prettier-plugin-sort-imports': '*' + '@zackad/prettier-plugin-twig': '*' + prettier: ^3.0 + prettier-plugin-astro: '*' + prettier-plugin-css-order: '*' + prettier-plugin-import-sort: '*' + prettier-plugin-jsdoc: '*' + prettier-plugin-marko: '*' + prettier-plugin-multiline-arrays: '*' + prettier-plugin-organize-attributes: '*' + prettier-plugin-organize-imports: '*' + prettier-plugin-sort-imports: '*' + prettier-plugin-style-order: '*' + prettier-plugin-svelte: '*' + peerDependenciesMeta: + '@ianvs/prettier-plugin-sort-imports': + optional: true + '@prettier/plugin-pug': + optional: true + '@shopify/prettier-plugin-liquid': + optional: true + '@trivago/prettier-plugin-sort-imports': + optional: true + '@zackad/prettier-plugin-twig': + optional: true + prettier-plugin-astro: + optional: true + prettier-plugin-css-order: + optional: true + prettier-plugin-import-sort: + optional: true + prettier-plugin-jsdoc: + optional: true + prettier-plugin-marko: + optional: true + prettier-plugin-multiline-arrays: + optional: true + prettier-plugin-organize-attributes: + optional: true + prettier-plugin-organize-imports: + optional: true + prettier-plugin-sort-imports: + optional: true + prettier-plugin-style-order: + optional: true + prettier-plugin-svelte: + optional: true + + prettier@3.5.3: + resolution: {integrity: sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==} + engines: {node: '>=14'} + hasBin: true + + proxy-addr@2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + qs@6.14.0: + resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} + engines: {node: '>=0.6'} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + range-parser@1.2.1: + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} + + raw-body@3.0.0: + resolution: {integrity: sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==} + engines: {node: '>= 0.8'} + + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + + readdirp@4.1.2: + resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} + engines: {node: '>= 14.18.0'} + + rechoir@0.6.2: + resolution: {integrity: sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==} + engines: {node: '>= 0.10'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + + resolve@1.22.10: + resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==} + engines: {node: '>= 0.4'} + hasBin: true + + restore-cursor@5.1.0: + resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} + engines: {node: '>=18'} + + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rfdc@1.4.1: + resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} + + rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + + rollup@4.40.2: + resolution: {integrity: sha512-tfUOg6DTP4rhQ3VjOO6B4wyrJnGOX85requAXvqYTHsOgb2TFJdZ3aWpT8W2kPoypSGP7dZUyzxJ9ee4buM5Fg==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + router@2.2.0: + resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} + engines: {node: '>= 18'} + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + sade@1.8.1: + resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} + engines: {node: '>=6'} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.7.1: + resolution: {integrity: sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==} + engines: {node: '>=10'} + hasBin: true + + semver@7.7.2: + resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} + engines: {node: '>=10'} + hasBin: true + + send@1.2.0: + resolution: {integrity: sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==} + engines: {node: '>= 18'} + + seq-queue@0.0.5: + resolution: {integrity: sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==} + + serve-static@2.2.0: + resolution: {integrity: sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==} + engines: {node: '>= 18'} + + set-blocking@2.0.0: + resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} + + set-cookie-parser@2.7.1: + resolution: {integrity: sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==} + + setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + + sharp@0.34.2: + resolution: {integrity: sha512-lszvBmB9QURERtyKT2bNmsgxXK0ShJrL/fvqlonCo7e6xBF8nT8xU6pW+PMIbLsz0RxQk3rgH9kd8UmvOzlMJg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + shelljs.exec@1.1.8: + resolution: {integrity: sha512-vFILCw+lzUtiwBAHV8/Ex8JsFjelFMdhONIsgKNLgTzeRckp2AOYRQtHJE/9LhNvdMmE27AGtzWx0+DHpwIwSw==} + engines: {node: '>= 4.0.0'} + + shelljs@0.8.5: + resolution: {integrity: sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==} + engines: {node: '>=4'} + hasBin: true + + side-channel-list@1.0.0: + resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + + side-channel@1.1.0: + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} + engines: {node: '>= 0.4'} + + signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + simple-json-db@2.0.0: + resolution: {integrity: sha512-oTh7gFQzqAe0E8RN3EkisPo0CojkzcKCKibTcJncg0yt47hWTaNwwjX/FsxfXSTDxfMjBFXFVnZe/EskAlJr7w==} + engines: {node: '>=10.0'} + + simple-swizzle@0.2.2: + resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} + + sirv@3.0.1: + resolution: {integrity: sha512-FoqMu0NCGBLCcAkS1qA+XJIQTR6/JHfQXl+uGteNCQ76T91DMUjPa9xfmeqMY3z80nLSg9yQmNjK0Px6RWsH/A==} + engines: {node: '>=18'} + + slice-ansi@5.0.0: + resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==} + engines: {node: '>=12'} + + slice-ansi@7.1.0: + resolution: {integrity: sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==} + engines: {node: '>=18'} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + sqlstring@2.3.3: + resolution: {integrity: sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==} + engines: {node: '>= 0.6'} + + starback@https://codeload.github.com/patrick11514/starback.js/tar.gz/fe6f8be567f2e4c4d2eadb06c007544c58dd3a0d: + resolution: {tarball: https://codeload.github.com/patrick11514/starback.js/tar.gz/fe6f8be567f2e4c4d2eadb06c007544c58dd3a0d} + version: 2.1.7 + + statuses@2.0.1: + resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} + engines: {node: '>= 0.8'} + + string-argv@0.3.2: + resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} + engines: {node: '>=0.6.19'} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@7.2.0: + resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} + engines: {node: '>=18'} + + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + svelte-check@4.1.7: + resolution: {integrity: sha512-1jX4BzXrQJhC/Jt3SqYf6Ntu//vmfc6VWp07JkRfK2nn+22yIblspVUo96gzMkg0Zov8lQicxhxsMzOctwcMQQ==} + engines: {node: '>= 18.0.0'} + hasBin: true + peerDependencies: + svelte: ^4.0.0 || ^5.0.0-next.0 + typescript: '>=5.0.0' + + svelte-eslint-parser@1.1.3: + resolution: {integrity: sha512-DUc/z/vk+AFVoxGv54+BOBFqUrmUgNg2gSO2YqrE3OL6ro19/0azPmQj/4wN3s9RxuF5l7G0162q/Ddk4LJhZA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + svelte: ^3.37.0 || ^4.0.0 || ^5.0.0 + peerDependenciesMeta: + svelte: + optional: true + + svelte@5.38.1: + resolution: {integrity: sha512-fO6CLDfJYWHgfo6lQwkQU2vhCiHc2MBl6s3vEhK+sSZru17YL4R5s1v14ndRpqKAIkq8nCz6MTk1yZbESZWeyQ==} + engines: {node: '>=18'} + + sweetalert2@11.21.0: + resolution: {integrity: sha512-fiEK7SqRY/QD/wC2uqEHlfYGZ7qe2UcyQbJpbpj4YRVqplBgcI+euPZLZL+evLINcvbtXmL1SFUdZHKqBHGAAQ==} + + tailwind-merge@3.2.0: + resolution: {integrity: sha512-FQT/OVqCD+7edmmJpsgCsY820RTD5AkBryuG5IUqR5YQZSdj5xlH5nLgH7YPths7WsLPSpSBNneJdM8aS8aeFA==} + + tailwindcss@4.1.5: + resolution: {integrity: sha512-nYtSPfWGDiWgCkwQG/m+aX83XCwf62sBgg3bIlNiiOcggnS1x3uVRDAuyelBFL+vJdOPPCGElxv9DjHJjRHiVA==} + + tapable@2.2.1: + resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} + engines: {node: '>=6'} + + tar@6.2.1: + resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} + engines: {node: '>=10'} + + tinyglobby@0.2.13: + resolution: {integrity: sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==} + engines: {node: '>=12.0.0'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + + totalist@3.0.1: + resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} + engines: {node: '>=6'} + + tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + + ts-api-utils@2.1.0: + resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + tsx@4.20.3: + resolution: {integrity: sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ==} + engines: {node: '>=18.0.0'} + hasBin: true + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + type-is@2.0.1: + resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} + engines: {node: '>= 0.6'} + + typescript-eslint@8.32.0: + resolution: {integrity: sha512-UMq2kxdXCzinFFPsXc9o2ozIpYCCOiEC46MG3yEh5Vipq6BO27otTtEBZA1fQ66DulEUgE97ucQ/3YY66CPg0A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + + typescript@5.8.3: + resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + + unpipe@1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + uuid@11.1.0: + resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==} + hasBin: true + + vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + + vite@6.3.5: + resolution: {integrity: sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + jiti: '>=1.21.0' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vitefu@1.0.6: + resolution: {integrity: sha512-+Rex1GlappUyNN6UfwbVZne/9cYC4+R2XDk9xkNXBKMw6HQagdX9PgZ8V2v1WUSK1wfBLp7qbI1+XSNIlB1xmA==} + peerDependencies: + vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 + peerDependenciesMeta: + vite: + optional: true + + webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + + whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + wide-align@1.1.5: + resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==} + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + wrap-ansi@9.0.0: + resolution: {integrity: sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==} + engines: {node: '>=18'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + + yaml@1.10.2: + resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} + engines: {node: '>= 6'} + + yaml@2.8.0: + resolution: {integrity: sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==} + engines: {node: '>= 14.6'} + hasBin: true + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + zimmerframe@1.1.2: + resolution: {integrity: sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==} + + zod-to-json-schema@3.24.5: + resolution: {integrity: sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g==} + peerDependencies: + zod: ^3.24.1 + + zod@3.25.76: + resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} + + zod@4.0.17: + resolution: {integrity: sha512-1PHjlYRevNxxdy2JZ8JcNAw7rX8V9P1AKkP+x/xZfxB0K5FYfuV+Ug6P/6NVSR2jHQ+FzDDoDHS04nYUsOIyLQ==} + +snapshots: + + '@babel/code-frame@7.27.1': + dependencies: + '@babel/helper-validator-identifier': 7.27.1 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/helper-validator-identifier@7.27.1': {} + + '@emnapi/runtime@1.4.3': + dependencies: + tslib: 2.8.1 + optional: true + + '@esbuild/aix-ppc64@0.25.4': + optional: true + + '@esbuild/android-arm64@0.25.4': + optional: true + + '@esbuild/android-arm@0.25.4': + optional: true + + '@esbuild/android-x64@0.25.4': + optional: true + + '@esbuild/darwin-arm64@0.25.4': + optional: true + + '@esbuild/darwin-x64@0.25.4': + optional: true + + '@esbuild/freebsd-arm64@0.25.4': + optional: true + + '@esbuild/freebsd-x64@0.25.4': + optional: true + + '@esbuild/linux-arm64@0.25.4': + optional: true + + '@esbuild/linux-arm@0.25.4': + optional: true + + '@esbuild/linux-ia32@0.25.4': + optional: true + + '@esbuild/linux-loong64@0.25.4': + optional: true + + '@esbuild/linux-mips64el@0.25.4': + optional: true + + '@esbuild/linux-ppc64@0.25.4': + optional: true + + '@esbuild/linux-riscv64@0.25.4': + optional: true + + '@esbuild/linux-s390x@0.25.4': + optional: true + + '@esbuild/linux-x64@0.25.4': + optional: true + + '@esbuild/netbsd-arm64@0.25.4': + optional: true + + '@esbuild/netbsd-x64@0.25.4': + optional: true + + '@esbuild/openbsd-arm64@0.25.4': + optional: true + + '@esbuild/openbsd-x64@0.25.4': + optional: true + + '@esbuild/sunos-x64@0.25.4': + optional: true + + '@esbuild/win32-arm64@0.25.4': + optional: true + + '@esbuild/win32-ia32@0.25.4': + optional: true + + '@esbuild/win32-x64@0.25.4': + optional: true + + '@eslint-community/eslint-utils@4.7.0(eslint@9.26.0(jiti@2.4.2))': + dependencies: + eslint: 9.26.0(jiti@2.4.2) + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.1': {} + + '@eslint/compat@1.2.9(eslint@9.26.0(jiti@2.4.2))': + optionalDependencies: + eslint: 9.26.0(jiti@2.4.2) + + '@eslint/config-array@0.20.0': + dependencies: + '@eslint/object-schema': 2.1.6 + debug: 4.4.0 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.2.2': {} + + '@eslint/core@0.13.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/eslintrc@3.3.1': + dependencies: + ajv: 6.12.6 + debug: 4.4.0 + espree: 10.3.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@9.26.0': {} + + '@eslint/object-schema@2.1.6': {} + + '@eslint/plugin-kit@0.2.8': + dependencies: + '@eslint/core': 0.13.0 + levn: 0.4.1 + + '@humanfs/core@0.19.1': {} + + '@humanfs/node@0.16.6': + dependencies: + '@humanfs/core': 0.19.1 + '@humanwhocodes/retry': 0.3.1 + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.3.1': {} + + '@humanwhocodes/retry@0.4.3': {} + + '@img/sharp-darwin-arm64@0.34.2': + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.1.0 + optional: true + + '@img/sharp-darwin-x64@0.34.2': + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.1.0 + optional: true + + '@img/sharp-libvips-darwin-arm64@1.1.0': + optional: true + + '@img/sharp-libvips-darwin-x64@1.1.0': + optional: true + + '@img/sharp-libvips-linux-arm64@1.1.0': + optional: true + + '@img/sharp-libvips-linux-arm@1.1.0': + optional: true + + '@img/sharp-libvips-linux-ppc64@1.1.0': + optional: true + + '@img/sharp-libvips-linux-s390x@1.1.0': + optional: true + + '@img/sharp-libvips-linux-x64@1.1.0': + optional: true + + '@img/sharp-libvips-linuxmusl-arm64@1.1.0': + optional: true + + '@img/sharp-libvips-linuxmusl-x64@1.1.0': + optional: true + + '@img/sharp-linux-arm64@0.34.2': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.1.0 + optional: true + + '@img/sharp-linux-arm@0.34.2': + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.1.0 + optional: true + + '@img/sharp-linux-s390x@0.34.2': + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.1.0 + optional: true + + '@img/sharp-linux-x64@0.34.2': + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.1.0 + optional: true + + '@img/sharp-linuxmusl-arm64@0.34.2': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.1.0 + optional: true + + '@img/sharp-linuxmusl-x64@0.34.2': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.1.0 + optional: true + + '@img/sharp-wasm32@0.34.2': + dependencies: + '@emnapi/runtime': 1.4.3 + optional: true + + '@img/sharp-win32-arm64@0.34.2': + optional: true + + '@img/sharp-win32-ia32@0.34.2': + optional: true + + '@img/sharp-win32-x64@0.34.2': + optional: true + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.30 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.30 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.0': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.25': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.0 + + '@jridgewell/trace-mapping@0.3.30': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@kurkle/color@0.3.4': {} + + '@mapbox/node-pre-gyp@1.0.11': + dependencies: + detect-libc: 2.0.4 + https-proxy-agent: 5.0.1 + make-dir: 3.1.0 + node-fetch: 2.7.0 + nopt: 5.0.0 + npmlog: 5.0.1 + rimraf: 3.0.2 + semver: 7.7.1 + tar: 6.2.1 + transitivePeerDependencies: + - encoding + - supports-color + + '@modelcontextprotocol/sdk@1.11.1': + dependencies: + content-type: 1.0.5 + cors: 2.8.5 + cross-spawn: 7.0.6 + eventsource: 3.0.6 + express: 5.1.0 + express-rate-limit: 7.5.0(express@5.1.0) + pkce-challenge: 5.0.0 + raw-body: 3.0.0 + zod: 3.25.76 + zod-to-json-schema: 3.24.5(zod@3.25.76) + transitivePeerDependencies: + - supports-color + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.19.1 + + '@patrick115/sveltekitapi@1.3.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.38.1)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0)))(svelte@5.38.1)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0))': + dependencies: + '@sveltejs/kit': 2.31.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.38.1)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0)))(svelte@5.38.1)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0)) + zod: 4.0.17 + transitivePeerDependencies: + - '@opentelemetry/api' + - '@sveltejs/vite-plugin-svelte' + - svelte + - vite + + '@polka/url@1.0.0-next.29': {} + + '@rollup/plugin-commonjs@28.0.3(rollup@4.40.2)': + dependencies: + '@rollup/pluginutils': 5.1.4(rollup@4.40.2) + commondir: 1.0.1 + estree-walker: 2.0.2 + fdir: 6.4.4(picomatch@4.0.2) + is-reference: 1.2.1 + magic-string: 0.30.17 + picomatch: 4.0.2 + optionalDependencies: + rollup: 4.40.2 + + '@rollup/plugin-json@6.1.0(rollup@4.40.2)': + dependencies: + '@rollup/pluginutils': 5.1.4(rollup@4.40.2) + optionalDependencies: + rollup: 4.40.2 + + '@rollup/plugin-node-resolve@16.0.1(rollup@4.40.2)': + dependencies: + '@rollup/pluginutils': 5.1.4(rollup@4.40.2) + '@types/resolve': 1.20.2 + deepmerge: 4.3.1 + is-module: 1.0.0 + resolve: 1.22.10 + optionalDependencies: + rollup: 4.40.2 + + '@rollup/pluginutils@5.1.4(rollup@4.40.2)': + dependencies: + '@types/estree': 1.0.7 + estree-walker: 2.0.2 + picomatch: 4.0.2 + optionalDependencies: + rollup: 4.40.2 + + '@rollup/rollup-android-arm-eabi@4.40.2': + optional: true + + '@rollup/rollup-android-arm64@4.40.2': + optional: true + + '@rollup/rollup-darwin-arm64@4.40.2': + optional: true + + '@rollup/rollup-darwin-x64@4.40.2': + optional: true + + '@rollup/rollup-freebsd-arm64@4.40.2': + optional: true + + '@rollup/rollup-freebsd-x64@4.40.2': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.40.2': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.40.2': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.40.2': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.40.2': + optional: true + + '@rollup/rollup-linux-loongarch64-gnu@4.40.2': + optional: true + + '@rollup/rollup-linux-powerpc64le-gnu@4.40.2': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.40.2': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.40.2': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.40.2': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.40.2': + optional: true + + '@rollup/rollup-linux-x64-musl@4.40.2': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.40.2': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.40.2': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.40.2': + optional: true + + '@standard-schema/spec@1.0.0': {} + + '@sveltejs/acorn-typescript@1.0.5(acorn@8.15.0)': + dependencies: + acorn: 8.15.0 + + '@sveltejs/adapter-node@5.2.12(@sveltejs/kit@2.31.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.38.1)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0)))(svelte@5.38.1)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0)))': + dependencies: + '@rollup/plugin-commonjs': 28.0.3(rollup@4.40.2) + '@rollup/plugin-json': 6.1.0(rollup@4.40.2) + '@rollup/plugin-node-resolve': 16.0.1(rollup@4.40.2) + '@sveltejs/kit': 2.31.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.38.1)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0)))(svelte@5.38.1)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0)) + rollup: 4.40.2 + + '@sveltejs/kit@2.31.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.38.1)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0)))(svelte@5.38.1)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0))': + dependencies: + '@standard-schema/spec': 1.0.0 + '@sveltejs/acorn-typescript': 1.0.5(acorn@8.15.0) + '@sveltejs/vite-plugin-svelte': 5.0.3(svelte@5.38.1)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0)) + '@types/cookie': 0.6.0 + acorn: 8.15.0 + cookie: 0.6.0 + devalue: 5.1.1 + esm-env: 1.2.2 + kleur: 4.1.5 + magic-string: 0.30.17 + mrmime: 2.0.1 + sade: 1.8.1 + set-cookie-parser: 2.7.1 + sirv: 3.0.1 + svelte: 5.38.1 + vite: 6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0) + + '@sveltejs/vite-plugin-svelte-inspector@4.0.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.38.1)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0)))(svelte@5.38.1)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0))': + dependencies: + '@sveltejs/vite-plugin-svelte': 5.0.3(svelte@5.38.1)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0)) + debug: 4.4.0 + svelte: 5.38.1 + vite: 6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0) + transitivePeerDependencies: + - supports-color + + '@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.38.1)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0))': + dependencies: + '@sveltejs/vite-plugin-svelte-inspector': 4.0.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.38.1)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0)))(svelte@5.38.1)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0)) + debug: 4.4.0 + deepmerge: 4.3.1 + kleur: 4.1.5 + magic-string: 0.30.17 + svelte: 5.38.1 + vite: 6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0) + vitefu: 1.0.6(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0)) + transitivePeerDependencies: + - supports-color + + '@tailwindcss/node@4.1.5': + dependencies: + enhanced-resolve: 5.18.1 + jiti: 2.4.2 + lightningcss: 1.29.2 + tailwindcss: 4.1.5 + + '@tailwindcss/oxide-android-arm64@4.1.5': + optional: true + + '@tailwindcss/oxide-darwin-arm64@4.1.5': + optional: true + + '@tailwindcss/oxide-darwin-x64@4.1.5': + optional: true + + '@tailwindcss/oxide-freebsd-x64@4.1.5': + optional: true + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.5': + optional: true + + '@tailwindcss/oxide-linux-arm64-gnu@4.1.5': + optional: true + + '@tailwindcss/oxide-linux-arm64-musl@4.1.5': + optional: true + + '@tailwindcss/oxide-linux-x64-gnu@4.1.5': + optional: true + + '@tailwindcss/oxide-linux-x64-musl@4.1.5': + optional: true + + '@tailwindcss/oxide-wasm32-wasi@4.1.5': + optional: true + + '@tailwindcss/oxide-win32-arm64-msvc@4.1.5': + optional: true + + '@tailwindcss/oxide-win32-x64-msvc@4.1.5': + optional: true + + '@tailwindcss/oxide@4.1.5': + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.1.5 + '@tailwindcss/oxide-darwin-arm64': 4.1.5 + '@tailwindcss/oxide-darwin-x64': 4.1.5 + '@tailwindcss/oxide-freebsd-x64': 4.1.5 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.5 + '@tailwindcss/oxide-linux-arm64-gnu': 4.1.5 + '@tailwindcss/oxide-linux-arm64-musl': 4.1.5 + '@tailwindcss/oxide-linux-x64-gnu': 4.1.5 + '@tailwindcss/oxide-linux-x64-musl': 4.1.5 + '@tailwindcss/oxide-wasm32-wasi': 4.1.5 + '@tailwindcss/oxide-win32-arm64-msvc': 4.1.5 + '@tailwindcss/oxide-win32-x64-msvc': 4.1.5 + + '@tailwindcss/typography@0.5.16(tailwindcss@4.1.5)': + dependencies: + lodash.castarray: 4.4.0 + lodash.isplainobject: 4.0.6 + lodash.merge: 4.6.2 + postcss-selector-parser: 6.0.10 + tailwindcss: 4.1.5 + + '@tailwindcss/vite@4.1.5(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0))': + dependencies: + '@tailwindcss/node': 4.1.5 + '@tailwindcss/oxide': 4.1.5 + tailwindcss: 4.1.5 + vite: 6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0) + + '@types/bcrypt@5.0.2': + dependencies: + '@types/node': 22.15.17 + + '@types/cookie@0.6.0': {} + + '@types/estree@1.0.7': {} + + '@types/estree@1.0.8': {} + + '@types/json-schema@7.0.15': {} + + '@types/jsonwebtoken@9.0.9': + dependencies: + '@types/ms': 2.1.0 + '@types/node': 22.15.17 + + '@types/ms@2.1.0': {} + + '@types/node@22.15.17': + dependencies: + undici-types: 6.21.0 + + '@types/resolve@1.20.2': {} + + '@types/uuid@10.0.0': {} + + '@typescript-eslint/eslint-plugin@8.32.0(@typescript-eslint/parser@8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)': + dependencies: + '@eslint-community/regexpp': 4.12.1 + '@typescript-eslint/parser': 8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) + '@typescript-eslint/scope-manager': 8.32.0 + '@typescript-eslint/type-utils': 8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) + '@typescript-eslint/utils': 8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) + '@typescript-eslint/visitor-keys': 8.32.0 + eslint: 9.26.0(jiti@2.4.2) + graphemer: 1.4.0 + ignore: 5.3.2 + natural-compare: 1.4.0 + ts-api-utils: 2.1.0(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.32.0 + '@typescript-eslint/types': 8.32.0 + '@typescript-eslint/typescript-estree': 8.32.0(typescript@5.8.3) + '@typescript-eslint/visitor-keys': 8.32.0 + debug: 4.4.0 + eslint: 9.26.0(jiti@2.4.2) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@8.32.0': + dependencies: + '@typescript-eslint/types': 8.32.0 + '@typescript-eslint/visitor-keys': 8.32.0 + + '@typescript-eslint/type-utils@8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)': + dependencies: + '@typescript-eslint/typescript-estree': 8.32.0(typescript@5.8.3) + '@typescript-eslint/utils': 8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) + debug: 4.4.0 + eslint: 9.26.0(jiti@2.4.2) + ts-api-utils: 2.1.0(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@8.32.0': {} + + '@typescript-eslint/typescript-estree@8.32.0(typescript@5.8.3)': + dependencies: + '@typescript-eslint/types': 8.32.0 + '@typescript-eslint/visitor-keys': 8.32.0 + debug: 4.4.0 + fast-glob: 3.3.3 + is-glob: 4.0.3 + minimatch: 9.0.5 + semver: 7.7.1 + ts-api-utils: 2.1.0(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)': + dependencies: + '@eslint-community/eslint-utils': 4.7.0(eslint@9.26.0(jiti@2.4.2)) + '@typescript-eslint/scope-manager': 8.32.0 + '@typescript-eslint/types': 8.32.0 + '@typescript-eslint/typescript-estree': 8.32.0(typescript@5.8.3) + eslint: 9.26.0(jiti@2.4.2) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@8.32.0': + dependencies: + '@typescript-eslint/types': 8.32.0 + eslint-visitor-keys: 4.2.0 + + abbrev@1.1.1: {} + + accepts@2.0.0: + dependencies: + mime-types: 3.0.1 + negotiator: 1.0.0 + + acorn-jsx@5.3.2(acorn@8.14.1): + dependencies: + acorn: 8.14.1 + + acorn@8.14.1: {} + + acorn@8.15.0: {} + + agent-base@6.0.2: + dependencies: + debug: 4.4.0 + transitivePeerDependencies: + - supports-color + + ajv@6.12.6: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ansi-escapes@7.0.0: + dependencies: + environment: 1.1.0 + + ansi-regex@5.0.1: {} + + ansi-regex@6.1.0: {} + + ansi-styles@3.2.1: + dependencies: + color-convert: 1.9.3 + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@6.2.1: {} + + aproba@2.0.0: {} + + are-we-there-yet@2.0.0: + dependencies: + delegates: 1.0.0 + readable-stream: 3.6.2 + + argparse@2.0.1: {} + + aria-query@5.3.2: {} + + aws-ssl-profiles@1.1.2: {} + + axobject-query@4.1.0: {} + + balanced-match@1.0.2: {} + + bcrypt@5.1.1: + dependencies: + '@mapbox/node-pre-gyp': 1.0.11 + node-addon-api: 5.1.0 + transitivePeerDependencies: + - encoding + - supports-color + + body-parser@2.2.0: + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 4.4.0 + http-errors: 2.0.0 + iconv-lite: 0.6.3 + on-finished: 2.4.1 + qs: 6.14.0 + raw-body: 3.0.0 + type-is: 2.0.1 + transitivePeerDependencies: + - supports-color + + bootstrap-icons@1.12.1: {} + + brace-expansion@1.1.11: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@2.0.1: + dependencies: + balanced-match: 1.0.2 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + buffer-equal-constant-time@1.0.1: {} + + bytes@3.1.2: {} + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + callsites@3.1.0: {} + + chalk@2.4.2: + dependencies: + ansi-styles: 3.2.1 + escape-string-regexp: 1.0.5 + supports-color: 5.5.0 + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + chalk@5.4.1: {} + + chart.js@4.4.9: + dependencies: + '@kurkle/color': 0.3.4 + + chokidar@4.0.3: + dependencies: + readdirp: 4.1.2 + + chownr@2.0.0: {} + + cli-cursor@5.0.0: + dependencies: + restore-cursor: 5.1.0 + + cli-truncate@4.0.0: + dependencies: + slice-ansi: 5.0.0 + string-width: 7.2.0 + + clsx@2.1.1: {} + + color-convert@1.9.3: + dependencies: + color-name: 1.1.3 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.3: {} + + color-name@1.1.4: {} + + color-string@1.9.1: + dependencies: + color-name: 1.1.4 + simple-swizzle: 0.2.2 + + color-support@1.1.3: {} + + color@4.2.3: + dependencies: + color-convert: 2.0.1 + color-string: 1.9.1 + + colorette@2.0.20: {} + + commander@14.0.0: {} + + commondir@1.0.1: {} + + concat-map@0.0.1: {} + + console-control-strings@1.1.0: {} + + content-disposition@1.0.0: + dependencies: + safe-buffer: 5.2.1 + + content-type@1.0.5: {} + + cookie-signature@1.2.2: {} + + cookie@0.6.0: {} + + cookie@0.7.2: {} + + cors@2.8.5: + dependencies: + object-assign: 4.1.1 + vary: 1.1.2 + + cosmiconfig@9.0.0(typescript@5.8.3): + dependencies: + env-paths: 2.2.1 + import-fresh: 3.3.1 + js-yaml: 4.1.0 + parse-json: 5.2.0 + optionalDependencies: + typescript: 5.8.3 + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + cssesc@3.0.0: {} + + debug@4.4.0: + dependencies: + ms: 2.1.3 + + debug@4.4.1: + dependencies: + ms: 2.1.3 + + deep-is@0.1.4: {} + + deepmerge@4.3.1: {} + + delegates@1.0.0: {} + + denque@2.1.0: {} + + depd@2.0.0: {} + + detect-libc@2.0.4: {} + + devalue@5.1.1: {} + + diff@3.5.0: {} + + dotenv-expand@12.0.2: + dependencies: + dotenv: 16.5.0 + + dotenv@16.5.0: {} + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + ecdsa-sig-formatter@1.0.11: + dependencies: + safe-buffer: 5.2.1 + + ee-first@1.1.1: {} + + emoji-regex@10.4.0: {} + + emoji-regex@8.0.0: {} + + encodeurl@2.0.0: {} + + enhanced-resolve@5.18.1: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.2.1 + + env-paths@2.2.1: {} + + environment@1.1.0: {} + + error-ex@1.3.2: + dependencies: + is-arrayish: 0.2.1 + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + esbuild@0.25.4: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.4 + '@esbuild/android-arm': 0.25.4 + '@esbuild/android-arm64': 0.25.4 + '@esbuild/android-x64': 0.25.4 + '@esbuild/darwin-arm64': 0.25.4 + '@esbuild/darwin-x64': 0.25.4 + '@esbuild/freebsd-arm64': 0.25.4 + '@esbuild/freebsd-x64': 0.25.4 + '@esbuild/linux-arm': 0.25.4 + '@esbuild/linux-arm64': 0.25.4 + '@esbuild/linux-ia32': 0.25.4 + '@esbuild/linux-loong64': 0.25.4 + '@esbuild/linux-mips64el': 0.25.4 + '@esbuild/linux-ppc64': 0.25.4 + '@esbuild/linux-riscv64': 0.25.4 + '@esbuild/linux-s390x': 0.25.4 + '@esbuild/linux-x64': 0.25.4 + '@esbuild/netbsd-arm64': 0.25.4 + '@esbuild/netbsd-x64': 0.25.4 + '@esbuild/openbsd-arm64': 0.25.4 + '@esbuild/openbsd-x64': 0.25.4 + '@esbuild/sunos-x64': 0.25.4 + '@esbuild/win32-arm64': 0.25.4 + '@esbuild/win32-ia32': 0.25.4 + '@esbuild/win32-x64': 0.25.4 + + escape-html@1.0.3: {} + + escape-string-regexp@1.0.5: {} + + escape-string-regexp@4.0.0: {} + + eslint-config-prettier@10.1.3(eslint@9.26.0(jiti@2.4.2)): + dependencies: + eslint: 9.26.0(jiti@2.4.2) + + eslint-plugin-svelte@3.5.1(eslint@9.26.0(jiti@2.4.2))(svelte@5.38.1): + dependencies: + '@eslint-community/eslint-utils': 4.7.0(eslint@9.26.0(jiti@2.4.2)) + '@jridgewell/sourcemap-codec': 1.5.0 + eslint: 9.26.0(jiti@2.4.2) + esutils: 2.0.3 + known-css-properties: 0.35.0 + postcss: 8.5.3 + postcss-load-config: 3.1.4(postcss@8.5.3) + postcss-safe-parser: 7.0.1(postcss@8.5.3) + semver: 7.7.1 + svelte-eslint-parser: 1.1.3(svelte@5.38.1) + optionalDependencies: + svelte: 5.38.1 + transitivePeerDependencies: + - ts-node + + eslint-scope@8.3.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@4.2.0: {} + + eslint@9.26.0(jiti@2.4.2): + dependencies: + '@eslint-community/eslint-utils': 4.7.0(eslint@9.26.0(jiti@2.4.2)) + '@eslint-community/regexpp': 4.12.1 + '@eslint/config-array': 0.20.0 + '@eslint/config-helpers': 0.2.2 + '@eslint/core': 0.13.0 + '@eslint/eslintrc': 3.3.1 + '@eslint/js': 9.26.0 + '@eslint/plugin-kit': 0.2.8 + '@humanfs/node': 0.16.6 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@modelcontextprotocol/sdk': 1.11.1 + '@types/estree': 1.0.7 + '@types/json-schema': 7.0.15 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.0 + escape-string-regexp: 4.0.0 + eslint-scope: 8.3.0 + eslint-visitor-keys: 4.2.0 + espree: 10.3.0 + esquery: 1.6.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.4 + zod: 3.25.76 + optionalDependencies: + jiti: 2.4.2 + transitivePeerDependencies: + - supports-color + + esm-env@1.2.2: {} + + espree@10.3.0: + dependencies: + acorn: 8.14.1 + acorn-jsx: 5.3.2(acorn@8.14.1) + eslint-visitor-keys: 4.2.0 + + esquery@1.6.0: + dependencies: + estraverse: 5.3.0 + + esrap@2.1.0: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + estree-walker@2.0.2: {} + + esutils@2.0.3: {} + + etag@1.8.1: {} + + eventemitter3@5.0.1: {} + + eventsource-parser@3.0.1: {} + + eventsource@3.0.6: + dependencies: + eventsource-parser: 3.0.1 + + express-rate-limit@7.5.0(express@5.1.0): + dependencies: + express: 5.1.0 + + express@5.1.0: + dependencies: + accepts: 2.0.0 + body-parser: 2.2.0 + content-disposition: 1.0.0 + content-type: 1.0.5 + cookie: 0.7.2 + cookie-signature: 1.2.2 + debug: 4.4.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 2.1.0 + fresh: 2.0.0 + http-errors: 2.0.0 + merge-descriptors: 2.0.0 + mime-types: 3.0.1 + on-finished: 2.4.1 + once: 1.4.0 + parseurl: 1.3.3 + proxy-addr: 2.0.7 + qs: 6.14.0 + range-parser: 1.2.1 + router: 2.2.0 + send: 1.2.0 + serve-static: 2.2.0 + statuses: 2.0.1 + type-is: 2.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + + fast-deep-equal@3.1.3: {} + + fast-glob@3.3.3: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fastq@1.19.1: + dependencies: + reusify: 1.1.0 + + fdir@6.4.4(picomatch@4.0.2): + optionalDependencies: + picomatch: 4.0.2 + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + finalhandler@2.1.0: + dependencies: + debug: 4.4.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.1 + transitivePeerDependencies: + - supports-color + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@4.0.1: + dependencies: + flatted: 3.3.3 + keyv: 4.5.4 + + flatted@3.3.3: {} + + forwarded@0.2.0: {} + + fresh@2.0.0: {} + + fs-minipass@2.1.0: + dependencies: + minipass: 3.3.6 + + fs.realpath@1.0.0: {} + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + gauge@3.0.2: + dependencies: + aproba: 2.0.0 + color-support: 1.1.3 + console-control-strings: 1.1.0 + has-unicode: 2.0.1 + object-assign: 4.1.1 + signal-exit: 3.0.7 + string-width: 4.2.3 + strip-ansi: 6.0.1 + wide-align: 1.1.5 + + generate-function@2.3.1: + dependencies: + is-property: 1.0.2 + + get-east-asian-width@1.3.0: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + math-intrinsics: 1.1.0 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + + get-tsconfig@4.10.1: + dependencies: + resolve-pkg-maps: 1.0.0 + + git-diff@2.0.6: + dependencies: + chalk: 2.4.2 + diff: 3.5.0 + loglevel: 1.9.2 + shelljs: 0.8.5 + shelljs.exec: 1.1.8 + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + glob@7.2.3: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + + globals@14.0.0: {} + + globals@16.3.0: {} + + gopd@1.2.0: {} + + graceful-fs@4.2.11: {} + + graphemer@1.4.0: {} + + has-flag@3.0.0: {} + + has-flag@4.0.0: {} + + has-symbols@1.1.0: {} + + has-unicode@2.0.1: {} + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + http-errors@2.0.0: + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.1 + toidentifier: 1.0.1 + + https-proxy-agent@5.0.1: + dependencies: + agent-base: 6.0.2 + debug: 4.4.0 + transitivePeerDependencies: + - supports-color + + husky@9.1.7: {} + + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + + ignore@5.3.2: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + imurmurhash@0.1.4: {} + + inflight@1.0.6: + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + + inherits@2.0.4: {} + + interpret@1.4.0: {} + + ipaddr.js@1.9.1: {} + + is-arrayish@0.2.1: {} + + is-arrayish@0.3.2: {} + + is-core-module@2.16.1: + dependencies: + hasown: 2.0.2 + + is-extglob@2.1.1: {} + + is-fullwidth-code-point@3.0.0: {} + + is-fullwidth-code-point@4.0.0: {} + + is-fullwidth-code-point@5.0.0: + dependencies: + get-east-asian-width: 1.3.0 + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-module@1.0.0: {} + + is-number@7.0.0: {} + + is-promise@4.0.0: {} + + is-property@1.0.2: {} + + is-reference@1.2.1: + dependencies: + '@types/estree': 1.0.7 + + is-reference@3.0.3: + dependencies: + '@types/estree': 1.0.8 + + isexe@2.0.0: {} + + jiti@2.4.2: {} + + js-tokens@4.0.0: {} + + js-yaml@4.1.0: + dependencies: + argparse: 2.0.1 + + json-buffer@3.0.1: {} + + json-parse-even-better-errors@2.3.1: {} + + json-schema-traverse@0.4.1: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + jsonwebtoken@9.0.2: + dependencies: + jws: 3.2.2 + lodash.includes: 4.3.0 + lodash.isboolean: 3.0.3 + lodash.isinteger: 4.0.4 + lodash.isnumber: 3.0.3 + lodash.isplainobject: 4.0.6 + lodash.isstring: 4.0.1 + lodash.once: 4.1.1 + ms: 2.1.3 + semver: 7.7.1 + + jwa@1.4.2: + dependencies: + buffer-equal-constant-time: 1.0.1 + ecdsa-sig-formatter: 1.0.11 + safe-buffer: 5.2.1 + + jws@3.2.2: + dependencies: + jwa: 1.4.2 + safe-buffer: 5.2.1 + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + kleur@4.1.5: {} + + known-css-properties@0.35.0: {} + + kysely-codegen@0.18.5(kysely@0.28.2)(mysql2@3.14.1)(typescript@5.8.3): + dependencies: + chalk: 4.1.2 + cosmiconfig: 9.0.0(typescript@5.8.3) + dotenv: 16.5.0 + dotenv-expand: 12.0.2 + git-diff: 2.0.6 + kysely: 0.28.2 + micromatch: 4.0.8 + minimist: 1.2.8 + pluralize: 8.0.0 + zod: 3.25.76 + optionalDependencies: + mysql2: 3.14.1 + transitivePeerDependencies: + - typescript + + kysely@0.28.2: {} + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + lightningcss-darwin-arm64@1.29.2: + optional: true + + lightningcss-darwin-x64@1.29.2: + optional: true + + lightningcss-freebsd-x64@1.29.2: + optional: true + + lightningcss-linux-arm-gnueabihf@1.29.2: + optional: true + + lightningcss-linux-arm64-gnu@1.29.2: + optional: true + + lightningcss-linux-arm64-musl@1.29.2: + optional: true + + lightningcss-linux-x64-gnu@1.29.2: + optional: true + + lightningcss-linux-x64-musl@1.29.2: + optional: true + + lightningcss-win32-arm64-msvc@1.29.2: + optional: true + + lightningcss-win32-x64-msvc@1.29.2: + optional: true + + lightningcss@1.29.2: + dependencies: + detect-libc: 2.0.4 + optionalDependencies: + lightningcss-darwin-arm64: 1.29.2 + lightningcss-darwin-x64: 1.29.2 + lightningcss-freebsd-x64: 1.29.2 + lightningcss-linux-arm-gnueabihf: 1.29.2 + lightningcss-linux-arm64-gnu: 1.29.2 + lightningcss-linux-arm64-musl: 1.29.2 + lightningcss-linux-x64-gnu: 1.29.2 + lightningcss-linux-x64-musl: 1.29.2 + lightningcss-win32-arm64-msvc: 1.29.2 + lightningcss-win32-x64-msvc: 1.29.2 + + lilconfig@2.1.0: {} + + lilconfig@3.1.3: {} + + lines-and-columns@1.2.4: {} + + lint-staged@16.1.2: + dependencies: + chalk: 5.4.1 + commander: 14.0.0 + debug: 4.4.1 + lilconfig: 3.1.3 + listr2: 8.3.3 + micromatch: 4.0.8 + nano-spawn: 1.0.2 + pidtree: 0.6.0 + string-argv: 0.3.2 + yaml: 2.8.0 + transitivePeerDependencies: + - supports-color + + listr2@8.3.3: + dependencies: + cli-truncate: 4.0.0 + colorette: 2.0.20 + eventemitter3: 5.0.1 + log-update: 6.1.0 + rfdc: 1.4.1 + wrap-ansi: 9.0.0 + + locate-character@3.0.0: {} + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash.castarray@4.4.0: {} + + lodash.includes@4.3.0: {} + + lodash.isboolean@3.0.3: {} + + lodash.isinteger@4.0.4: {} + + lodash.isnumber@3.0.3: {} + + lodash.isplainobject@4.0.6: {} + + lodash.isstring@4.0.1: {} + + lodash.merge@4.6.2: {} + + lodash.once@4.1.1: {} + + log-update@6.1.0: + dependencies: + ansi-escapes: 7.0.0 + cli-cursor: 5.0.0 + slice-ansi: 7.1.0 + strip-ansi: 7.1.0 + wrap-ansi: 9.0.0 + + loglevel@1.9.2: {} + + long@5.3.2: {} + + lru-cache@7.18.3: {} + + lru.min@1.1.2: {} + + magic-string@0.30.17: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.0 + + make-dir@3.1.0: + dependencies: + semver: 6.3.1 + + marked@15.0.12: {} + + math-intrinsics@1.1.0: {} + + media-typer@1.1.0: {} + + merge-descriptors@2.0.0: {} + + merge2@1.4.1: {} + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + + mime-db@1.54.0: {} + + mime-types@3.0.1: + dependencies: + mime-db: 1.54.0 + + mimic-function@5.0.1: {} + + minimatch@3.1.2: + dependencies: + brace-expansion: 1.1.11 + + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.1 + + minimist@1.2.8: {} + + minipass@3.3.6: + dependencies: + yallist: 4.0.0 + + minipass@5.0.0: {} + + minizlib@2.1.2: + dependencies: + minipass: 3.3.6 + yallist: 4.0.0 + + mkdirp@1.0.4: {} + + mri@1.2.0: {} + + mrmime@2.0.1: {} + + ms@2.1.3: {} + + mysql2@3.14.1: + dependencies: + aws-ssl-profiles: 1.1.2 + denque: 2.1.0 + generate-function: 2.3.1 + iconv-lite: 0.6.3 + long: 5.3.2 + lru.min: 1.1.2 + named-placeholders: 1.1.3 + seq-queue: 0.0.5 + sqlstring: 2.3.3 + + named-placeholders@1.1.3: + dependencies: + lru-cache: 7.18.3 + + nano-spawn@1.0.2: {} + + nanoid@3.3.11: {} + + natural-compare@1.4.0: {} + + negotiator@1.0.0: {} + + node-addon-api@5.1.0: {} + + node-fetch@2.7.0: + dependencies: + whatwg-url: 5.0.0 + + nopt@5.0.0: + dependencies: + abbrev: 1.1.1 + + npmlog@5.0.1: + dependencies: + are-we-there-yet: 2.0.0 + console-control-strings: 1.1.0 + gauge: 3.0.2 + set-blocking: 2.0.0 + + object-assign@4.1.1: {} + + object-inspect@1.13.4: {} + + on-finished@2.4.1: + dependencies: + ee-first: 1.1.1 + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + onetime@7.0.0: + dependencies: + mimic-function: 5.0.1 + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parse-json@5.2.0: + dependencies: + '@babel/code-frame': 7.27.1 + error-ex: 1.3.2 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + + parseurl@1.3.3: {} + + path-exists@4.0.0: {} + + path-is-absolute@1.0.1: {} + + path-key@3.1.1: {} + + path-parse@1.0.7: {} + + path-to-regexp@8.2.0: {} + + picocolors@1.1.1: {} + + picomatch@2.3.1: {} + + picomatch@4.0.2: {} + + pidtree@0.6.0: {} + + pkce-challenge@5.0.0: {} + + pluralize@8.0.0: {} + + postcss-load-config@3.1.4(postcss@8.5.3): + dependencies: + lilconfig: 2.1.0 + yaml: 1.10.2 + optionalDependencies: + postcss: 8.5.3 + + postcss-safe-parser@7.0.1(postcss@8.5.3): + dependencies: + postcss: 8.5.3 + + postcss-scss@4.0.9(postcss@8.5.3): + dependencies: + postcss: 8.5.3 + + postcss-selector-parser@6.0.10: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss-selector-parser@7.1.0: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss@8.5.3: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + prelude-ls@1.2.1: {} + + prettier-plugin-svelte@3.3.3(prettier@3.5.3)(svelte@5.38.1): + dependencies: + prettier: 3.5.3 + svelte: 5.38.1 + + prettier-plugin-tailwindcss@0.6.11(prettier-plugin-svelte@3.3.3(prettier@3.5.3)(svelte@5.38.1))(prettier@3.5.3): + dependencies: + prettier: 3.5.3 + optionalDependencies: + prettier-plugin-svelte: 3.3.3(prettier@3.5.3)(svelte@5.38.1) + + prettier@3.5.3: {} + + proxy-addr@2.0.7: + dependencies: + forwarded: 0.2.0 + ipaddr.js: 1.9.1 + + punycode@2.3.1: {} + + qs@6.14.0: + dependencies: + side-channel: 1.1.0 + + queue-microtask@1.2.3: {} + + range-parser@1.2.1: {} + + raw-body@3.0.0: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.0 + iconv-lite: 0.6.3 + unpipe: 1.0.0 + + readable-stream@3.6.2: + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + + readdirp@4.1.2: {} + + rechoir@0.6.2: + dependencies: + resolve: 1.22.10 + + resolve-from@4.0.0: {} + + resolve-pkg-maps@1.0.0: {} + + resolve@1.22.10: + dependencies: + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + restore-cursor@5.1.0: + dependencies: + onetime: 7.0.0 + signal-exit: 4.1.0 + + reusify@1.1.0: {} + + rfdc@1.4.1: {} + + rimraf@3.0.2: + dependencies: + glob: 7.2.3 + + rollup@4.40.2: + dependencies: + '@types/estree': 1.0.7 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.40.2 + '@rollup/rollup-android-arm64': 4.40.2 + '@rollup/rollup-darwin-arm64': 4.40.2 + '@rollup/rollup-darwin-x64': 4.40.2 + '@rollup/rollup-freebsd-arm64': 4.40.2 + '@rollup/rollup-freebsd-x64': 4.40.2 + '@rollup/rollup-linux-arm-gnueabihf': 4.40.2 + '@rollup/rollup-linux-arm-musleabihf': 4.40.2 + '@rollup/rollup-linux-arm64-gnu': 4.40.2 + '@rollup/rollup-linux-arm64-musl': 4.40.2 + '@rollup/rollup-linux-loongarch64-gnu': 4.40.2 + '@rollup/rollup-linux-powerpc64le-gnu': 4.40.2 + '@rollup/rollup-linux-riscv64-gnu': 4.40.2 + '@rollup/rollup-linux-riscv64-musl': 4.40.2 + '@rollup/rollup-linux-s390x-gnu': 4.40.2 + '@rollup/rollup-linux-x64-gnu': 4.40.2 + '@rollup/rollup-linux-x64-musl': 4.40.2 + '@rollup/rollup-win32-arm64-msvc': 4.40.2 + '@rollup/rollup-win32-ia32-msvc': 4.40.2 + '@rollup/rollup-win32-x64-msvc': 4.40.2 + fsevents: 2.3.3 + + router@2.2.0: + dependencies: + debug: 4.4.0 + depd: 2.0.0 + is-promise: 4.0.0 + parseurl: 1.3.3 + path-to-regexp: 8.2.0 + transitivePeerDependencies: + - supports-color + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + sade@1.8.1: + dependencies: + mri: 1.2.0 + + safe-buffer@5.2.1: {} + + safer-buffer@2.1.2: {} + + semver@6.3.1: {} + + semver@7.7.1: {} + + semver@7.7.2: {} + + send@1.2.0: + dependencies: + debug: 4.4.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 2.0.0 + http-errors: 2.0.0 + mime-types: 3.0.1 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.1 + transitivePeerDependencies: + - supports-color + + seq-queue@0.0.5: {} + + serve-static@2.2.0: + dependencies: + encodeurl: 2.0.0 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 1.2.0 + transitivePeerDependencies: + - supports-color + + set-blocking@2.0.0: {} + + set-cookie-parser@2.7.1: {} + + setprototypeof@1.2.0: {} + + sharp@0.34.2: + dependencies: + color: 4.2.3 + detect-libc: 2.0.4 + semver: 7.7.2 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.34.2 + '@img/sharp-darwin-x64': 0.34.2 + '@img/sharp-libvips-darwin-arm64': 1.1.0 + '@img/sharp-libvips-darwin-x64': 1.1.0 + '@img/sharp-libvips-linux-arm': 1.1.0 + '@img/sharp-libvips-linux-arm64': 1.1.0 + '@img/sharp-libvips-linux-ppc64': 1.1.0 + '@img/sharp-libvips-linux-s390x': 1.1.0 + '@img/sharp-libvips-linux-x64': 1.1.0 + '@img/sharp-libvips-linuxmusl-arm64': 1.1.0 + '@img/sharp-libvips-linuxmusl-x64': 1.1.0 + '@img/sharp-linux-arm': 0.34.2 + '@img/sharp-linux-arm64': 0.34.2 + '@img/sharp-linux-s390x': 0.34.2 + '@img/sharp-linux-x64': 0.34.2 + '@img/sharp-linuxmusl-arm64': 0.34.2 + '@img/sharp-linuxmusl-x64': 0.34.2 + '@img/sharp-wasm32': 0.34.2 + '@img/sharp-win32-arm64': 0.34.2 + '@img/sharp-win32-ia32': 0.34.2 + '@img/sharp-win32-x64': 0.34.2 + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + shelljs.exec@1.1.8: {} + + shelljs@0.8.5: + dependencies: + glob: 7.2.3 + interpret: 1.4.0 + rechoir: 0.6.2 + + side-channel-list@1.0.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + + side-channel@1.1.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.0 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + + signal-exit@3.0.7: {} + + signal-exit@4.1.0: {} + + simple-json-db@2.0.0: {} + + simple-swizzle@0.2.2: + dependencies: + is-arrayish: 0.3.2 + + sirv@3.0.1: + dependencies: + '@polka/url': 1.0.0-next.29 + mrmime: 2.0.1 + totalist: 3.0.1 + + slice-ansi@5.0.0: + dependencies: + ansi-styles: 6.2.1 + is-fullwidth-code-point: 4.0.0 + + slice-ansi@7.1.0: + dependencies: + ansi-styles: 6.2.1 + is-fullwidth-code-point: 5.0.0 + + source-map-js@1.2.1: {} + + sqlstring@2.3.3: {} + + starback@https://codeload.github.com/patrick11514/starback.js/tar.gz/fe6f8be567f2e4c4d2eadb06c007544c58dd3a0d: {} + + statuses@2.0.1: {} + + string-argv@0.3.2: {} + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string-width@7.2.0: + dependencies: + emoji-regex: 10.4.0 + get-east-asian-width: 1.3.0 + strip-ansi: 7.1.0 + + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.1.0: + dependencies: + ansi-regex: 6.1.0 + + strip-json-comments@3.1.1: {} + + supports-color@5.5.0: + dependencies: + has-flag: 3.0.0 + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + svelte-check@4.1.7(picomatch@4.0.2)(svelte@5.38.1)(typescript@5.8.3): + dependencies: + '@jridgewell/trace-mapping': 0.3.25 + chokidar: 4.0.3 + fdir: 6.4.4(picomatch@4.0.2) + picocolors: 1.1.1 + sade: 1.8.1 + svelte: 5.38.1 + typescript: 5.8.3 + transitivePeerDependencies: + - picomatch + + svelte-eslint-parser@1.1.3(svelte@5.38.1): + dependencies: + eslint-scope: 8.3.0 + eslint-visitor-keys: 4.2.0 + espree: 10.3.0 + postcss: 8.5.3 + postcss-scss: 4.0.9(postcss@8.5.3) + postcss-selector-parser: 7.1.0 + optionalDependencies: + svelte: 5.38.1 + + svelte@5.38.1: + dependencies: + '@jridgewell/remapping': 2.3.5 + '@jridgewell/sourcemap-codec': 1.5.5 + '@sveltejs/acorn-typescript': 1.0.5(acorn@8.15.0) + '@types/estree': 1.0.8 + acorn: 8.15.0 + aria-query: 5.3.2 + axobject-query: 4.1.0 + clsx: 2.1.1 + esm-env: 1.2.2 + esrap: 2.1.0 + is-reference: 3.0.3 + locate-character: 3.0.0 + magic-string: 0.30.17 + zimmerframe: 1.1.2 + + sweetalert2@11.21.0: {} + + tailwind-merge@3.2.0: {} + + tailwindcss@4.1.5: {} + + tapable@2.2.1: {} + + tar@6.2.1: + dependencies: + chownr: 2.0.0 + fs-minipass: 2.1.0 + minipass: 5.0.0 + minizlib: 2.1.2 + mkdirp: 1.0.4 + yallist: 4.0.0 + + tinyglobby@0.2.13: + dependencies: + fdir: 6.4.4(picomatch@4.0.2) + picomatch: 4.0.2 + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + toidentifier@1.0.1: {} + + totalist@3.0.1: {} + + tr46@0.0.3: {} + + ts-api-utils@2.1.0(typescript@5.8.3): + dependencies: + typescript: 5.8.3 + + tslib@2.8.1: + optional: true + + tsx@4.20.3: + dependencies: + esbuild: 0.25.4 + get-tsconfig: 4.10.1 + optionalDependencies: + fsevents: 2.3.3 + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + type-is@2.0.1: + dependencies: + content-type: 1.0.5 + media-typer: 1.1.0 + mime-types: 3.0.1 + + typescript-eslint@8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3): + dependencies: + '@typescript-eslint/eslint-plugin': 8.32.0(@typescript-eslint/parser@8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) + '@typescript-eslint/parser': 8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) + '@typescript-eslint/utils': 8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) + eslint: 9.26.0(jiti@2.4.2) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + typescript@5.8.3: {} + + undici-types@6.21.0: {} + + unpipe@1.0.0: {} + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + util-deprecate@1.0.2: {} + + uuid@11.1.0: {} + + vary@1.1.2: {} + + vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0): + dependencies: + esbuild: 0.25.4 + fdir: 6.4.4(picomatch@4.0.2) + picomatch: 4.0.2 + postcss: 8.5.3 + rollup: 4.40.2 + tinyglobby: 0.2.13 + optionalDependencies: + '@types/node': 22.15.17 + fsevents: 2.3.3 + jiti: 2.4.2 + lightningcss: 1.29.2 + tsx: 4.20.3 + yaml: 2.8.0 + + vitefu@1.0.6(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0)): + optionalDependencies: + vite: 6.3.5(@types/node@22.15.17)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.20.3)(yaml@2.8.0) + + webidl-conversions@3.0.1: {} + + whatwg-url@5.0.0: + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + wide-align@1.1.5: + dependencies: + string-width: 4.2.3 + + word-wrap@1.2.5: {} + + wrap-ansi@9.0.0: + dependencies: + ansi-styles: 6.2.1 + string-width: 7.2.0 + strip-ansi: 7.1.0 + + wrappy@1.0.2: {} + + yallist@4.0.0: {} + + yaml@1.10.2: {} + + yaml@2.8.0: {} + + yocto-queue@0.1.0: {} + + zimmerframe@1.1.2: {} + + zod-to-json-schema@3.24.5(zod@3.25.76): + dependencies: + zod: 3.25.76 + + zod@3.25.76: {} + + zod@4.0.17: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..024281b --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,4 @@ +onlyBuiltDependencies: + - bcrypt + - esbuild + - sharp diff --git a/postcss.config.js b/postcss.config.js deleted file mode 100644 index badd100..0000000 --- a/postcss.config.js +++ /dev/null @@ -1,6 +0,0 @@ -export default { - plugins: { - tailwindcss: {}, - autoprefixer: {} - } -}; diff --git a/script/createMigration.sh b/script/createMigration.sh new file mode 100755 index 0000000..4c5a9c4 --- /dev/null +++ b/script/createMigration.sh @@ -0,0 +1,24 @@ +#!/bin/bash +read -p "Enter migration name: " NAME + + +# Convert name to lowercase and replace spaces with underscores +FILENAME=$(date +"%Y%m%d%H%M%S")_$(echo "$NAME" | tr '[:upper:]' '[:lower:]' | tr ' ' '_').ts + +# Create the file inside migrations directory +mkdir -p migrations +cat < "migrations/$FILENAME" +/*eslint-disable @typescript-eslint/no-explicit-any*/ + +import { Kysely } from 'kysely'; + +export const up = async (conn: Kysely) => { + // Add migration logic here +} + +export const down = async (conn: Kysely) => { + // Add rollback logic here +} +EOL + +echo "Created migration: migrations/$FILENAME" diff --git a/script/runMigration.ts b/script/runMigration.ts new file mode 100644 index 0000000..50b518b --- /dev/null +++ b/script/runMigration.ts @@ -0,0 +1,54 @@ +/* eslint-disable no-console */ + +import { Migrator, FileMigrationProvider, MigrationResultSet } from 'kysely'; +import { promises as fs } from 'node:fs'; +import path from 'node:path'; +import dotenv from 'dotenv'; +import { Kysely, MysqlDialect } from 'kysely'; +import { createPool } from 'mysql2'; + +dotenv.config(); + +const dialect = new MysqlDialect({ + pool: createPool({ + host: process.env.DATABASE_IP, + port: parseInt(process.env.DATABASE_PORT!), + user: process.env.DATABASE_USER, + password: process.env.DATABASE_PASSWORD, + database: process.env.DATABASE_NAME + }) +}); + +const conn = new Kysely({ dialect }); + +async function runMigrations() { + const migrator = new Migrator({ + db: conn, + provider: new FileMigrationProvider({ + fs, + path, + migrationFolder: path.resolve(import.meta.dirname, '../migrations') + }) + }); + + const direction = process.argv[2]; + + let result: MigrationResultSet; + + if (direction === 'up') { + result = await migrator.migrateUp(); + } else if (direction === 'down') { + result = await migrator.migrateDown(); + } else { + result = await migrator.migrateToLatest(); + } + + if (result.error) { + console.error('Migration failed:', result.error); + process.exit(1); + } + + console.log('Migrations applied successfully.'); +} + +runMigrations().then(() => process.exit(0)); diff --git a/script/start.sh b/script/start.sh new file mode 120000 index 0000000..2cd9b25 --- /dev/null +++ b/script/start.sh @@ -0,0 +1 @@ +../../template/script/start.sh \ No newline at end of file diff --git a/src/app.css b/src/app.css index 2f25dd5..4fbf06c 100644 --- a/src/app.css +++ b/src/app.css @@ -1,1509 +1,54 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; +@import 'tailwindcss'; +@plugin '@tailwindcss/typography'; +@config "../tailwindcss.config.js"; -/* Google fonts */ -/* cyrillic-ext */ -@font-face { - font-family: 'Fira Sans'; - font-style: normal; - font-weight: 100; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9C4kDNxMZdWfMOD5Vn9LjEYTLHdQ.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Fira Sans'; - font-style: normal; - font-weight: 100; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9C4kDNxMZdWfMOD5Vn9LjNYTLHdQ.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* greek-ext */ -@font-face { - font-family: 'Fira Sans'; - font-style: normal; - font-weight: 100; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9C4kDNxMZdWfMOD5Vn9LjFYTLHdQ.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; -} -/* greek */ -@font-face { - font-family: 'Fira Sans'; - font-style: normal; - font-weight: 100; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9C4kDNxMZdWfMOD5Vn9LjKYTLHdQ.woff2) format('woff2'); - unicode-range: U+0370-03FF; -} -/* vietnamese */ -@font-face { - font-family: 'Fira Sans'; - font-style: normal; - font-weight: 100; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9C4kDNxMZdWfMOD5Vn9LjGYTLHdQ.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: 'Fira Sans'; - font-style: normal; - font-weight: 100; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9C4kDNxMZdWfMOD5Vn9LjHYTLHdQ.woff2) format('woff2'); - unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Fira Sans'; - font-style: normal; - font-weight: 100; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9C4kDNxMZdWfMOD5Vn9LjJYTI.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+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, - U+2215, U+FEFF, U+FFFD; -} -/* cyrillic-ext */ -@font-face { - font-family: 'Fira Sans'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9B4kDNxMZdWfMOD5VnPKreSxf6TF0.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Fira Sans'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9B4kDNxMZdWfMOD5VnPKreQhf6TF0.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* greek-ext */ -@font-face { - font-family: 'Fira Sans'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9B4kDNxMZdWfMOD5VnPKreShf6TF0.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; -} -/* greek */ -@font-face { - font-family: 'Fira Sans'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9B4kDNxMZdWfMOD5VnPKreRRf6TF0.woff2) format('woff2'); - unicode-range: U+0370-03FF; -} -/* vietnamese */ -@font-face { - font-family: 'Fira Sans'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9B4kDNxMZdWfMOD5VnPKreSRf6TF0.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: 'Fira Sans'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9B4kDNxMZdWfMOD5VnPKreSBf6TF0.woff2) format('woff2'); - unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Fira Sans'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9B4kDNxMZdWfMOD5VnPKreRhf6.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+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, - U+2215, U+FEFF, U+FFFD; -} -/* cyrillic-ext */ -@font-face { - font-family: 'Fira Sans'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9E4kDNxMZdWfMOD5VvmojLeTY.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Fira Sans'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9E4kDNxMZdWfMOD5Vvk4jLeTY.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* greek-ext */ -@font-face { - font-family: 'Fira Sans'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9E4kDNxMZdWfMOD5Vvm4jLeTY.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; -} -/* greek */ -@font-face { - font-family: 'Fira Sans'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9E4kDNxMZdWfMOD5VvlIjLeTY.woff2) format('woff2'); - unicode-range: U+0370-03FF; -} -/* vietnamese */ -@font-face { - font-family: 'Fira Sans'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9E4kDNxMZdWfMOD5VvmIjLeTY.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: 'Fira Sans'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9E4kDNxMZdWfMOD5VvmYjLeTY.woff2) format('woff2'); - unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Fira Sans'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9E4kDNxMZdWfMOD5Vvl4jL.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+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, - U+2215, U+FEFF, U+FFFD; -} -/* cyrillic-ext */ -@font-face { - font-family: 'Fira Sans'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9B4kDNxMZdWfMOD5VnZKveSxf6TF0.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Fira Sans'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9B4kDNxMZdWfMOD5VnZKveQhf6TF0.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* greek-ext */ -@font-face { - font-family: 'Fira Sans'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9B4kDNxMZdWfMOD5VnZKveShf6TF0.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; -} -/* greek */ -@font-face { - font-family: 'Fira Sans'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9B4kDNxMZdWfMOD5VnZKveRRf6TF0.woff2) format('woff2'); - unicode-range: U+0370-03FF; -} -/* vietnamese */ -@font-face { - font-family: 'Fira Sans'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9B4kDNxMZdWfMOD5VnZKveSRf6TF0.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: 'Fira Sans'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9B4kDNxMZdWfMOD5VnZKveSBf6TF0.woff2) format('woff2'); - unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Fira Sans'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9B4kDNxMZdWfMOD5VnZKveRhf6.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+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, - U+2215, U+FEFF, U+FFFD; -} -/* cyrillic-ext */ -@font-face { - font-family: 'Fira Sans'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9B4kDNxMZdWfMOD5VnLK3eSxf6TF0.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Fira Sans'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9B4kDNxMZdWfMOD5VnLK3eQhf6TF0.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* greek-ext */ -@font-face { - font-family: 'Fira Sans'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9B4kDNxMZdWfMOD5VnLK3eShf6TF0.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; -} -/* greek */ -@font-face { - font-family: 'Fira Sans'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9B4kDNxMZdWfMOD5VnLK3eRRf6TF0.woff2) format('woff2'); - unicode-range: U+0370-03FF; -} -/* vietnamese */ -@font-face { - font-family: 'Fira Sans'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9B4kDNxMZdWfMOD5VnLK3eSRf6TF0.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: 'Fira Sans'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9B4kDNxMZdWfMOD5VnLK3eSBf6TF0.woff2) format('woff2'); - unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Fira Sans'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/firasans/v17/va9B4kDNxMZdWfMOD5VnLK3eRhf6.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+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, - U+2215, U+FEFF, U+FFFD; -} -/* cyrillic-ext */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 100; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2JL7SUc.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 100; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa0ZL7SUc.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* greek-ext */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 100; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2ZL7SUc.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; -} -/* greek */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 100; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1pL7SUc.woff2) format('woff2'); - unicode-range: U+0370-03FF; -} -/* vietnamese */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 100; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2pL7SUc.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: 'Inter'; - font-style: normal; - font-weight: 100; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa25L7SUc.woff2) format('woff2'); - unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 100; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1ZL7.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+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, - U+2215, U+FEFF, U+FFFD; -} -/* cyrillic-ext */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2JL7SUc.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa0ZL7SUc.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* greek-ext */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2ZL7SUc.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; -} -/* greek */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1pL7SUc.woff2) format('woff2'); - unicode-range: U+0370-03FF; -} -/* vietnamese */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2pL7SUc.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: 'Inter'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa25L7SUc.woff2) format('woff2'); - unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1ZL7.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+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, - U+2215, U+FEFF, U+FFFD; -} -/* cyrillic-ext */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2JL7SUc.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa0ZL7SUc.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* greek-ext */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2ZL7SUc.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; -} -/* greek */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1pL7SUc.woff2) format('woff2'); - unicode-range: U+0370-03FF; -} -/* vietnamese */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2pL7SUc.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: 'Inter'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa25L7SUc.woff2) format('woff2'); - unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1ZL7.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+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, - U+2215, U+FEFF, U+FFFD; -} -/* cyrillic-ext */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2JL7SUc.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa0ZL7SUc.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* greek-ext */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2ZL7SUc.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; -} -/* greek */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1pL7SUc.woff2) format('woff2'); - unicode-range: U+0370-03FF; -} -/* vietnamese */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2pL7SUc.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: 'Inter'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa25L7SUc.woff2) format('woff2'); - unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1ZL7.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+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, - U+2215, U+FEFF, U+FFFD; -} -/* cyrillic-ext */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2JL7SUc.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa0ZL7SUc.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* greek-ext */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2ZL7SUc.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; -} -/* greek */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1pL7SUc.woff2) format('woff2'); - unicode-range: U+0370-03FF; -} -/* vietnamese */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2pL7SUc.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: 'Inter'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa25L7SUc.woff2) format('woff2'); - unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1ZL7.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+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, - U+2215, U+FEFF, U+FFFD; -} -/* cyrillic-ext */ -@font-face { - font-family: 'Oswald'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/oswald/v53/TK3iWkUHHAIjg752FD8Ghe4.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Oswald'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/oswald/v53/TK3iWkUHHAIjg752HT8Ghe4.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* vietnamese */ -@font-face { - font-family: 'Oswald'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/oswald/v53/TK3iWkUHHAIjg752Fj8Ghe4.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: 'Oswald'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/oswald/v53/TK3iWkUHHAIjg752Fz8Ghe4.woff2) format('woff2'); - unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Oswald'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/oswald/v53/TK3iWkUHHAIjg752GT8G.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+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, - U+2215, U+FEFF, U+FFFD; -} -/* cyrillic-ext */ -@font-face { - font-family: 'Oswald'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/oswald/v53/TK3iWkUHHAIjg752FD8Ghe4.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Oswald'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/oswald/v53/TK3iWkUHHAIjg752HT8Ghe4.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* vietnamese */ -@font-face { - font-family: 'Oswald'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/oswald/v53/TK3iWkUHHAIjg752Fj8Ghe4.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: 'Oswald'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/oswald/v53/TK3iWkUHHAIjg752Fz8Ghe4.woff2) format('woff2'); - unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Oswald'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/oswald/v53/TK3iWkUHHAIjg752GT8G.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+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, - U+2215, U+FEFF, U+FFFD; -} -/* cyrillic-ext */ -@font-face { - font-family: 'Oswald'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/oswald/v53/TK3iWkUHHAIjg752FD8Ghe4.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Oswald'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/oswald/v53/TK3iWkUHHAIjg752HT8Ghe4.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* vietnamese */ -@font-face { - font-family: 'Oswald'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/oswald/v53/TK3iWkUHHAIjg752Fj8Ghe4.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: 'Oswald'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/oswald/v53/TK3iWkUHHAIjg752Fz8Ghe4.woff2) format('woff2'); - unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Oswald'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/oswald/v53/TK3iWkUHHAIjg752GT8G.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+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, - U+2215, U+FEFF, U+FFFD; -} -/* cyrillic-ext */ -@font-face { - font-family: 'Oswald'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/oswald/v53/TK3iWkUHHAIjg752FD8Ghe4.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Oswald'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/oswald/v53/TK3iWkUHHAIjg752HT8Ghe4.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* vietnamese */ -@font-face { - font-family: 'Oswald'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/oswald/v53/TK3iWkUHHAIjg752Fj8Ghe4.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: 'Oswald'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/oswald/v53/TK3iWkUHHAIjg752Fz8Ghe4.woff2) format('woff2'); - unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Oswald'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/oswald/v53/TK3iWkUHHAIjg752GT8G.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+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, - U+2215, U+FEFF, U+FFFD; -} -/* devanagari */ -@font-face { - font-family: 'Poppins'; - font-style: normal; - font-weight: 100; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v20/pxiGyp8kv8JHgFVrLPTucXtAKPY.woff2) format('woff2'); - unicode-range: U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+25CC, U+A830-A839, U+A8E0-A8FF; -} -/* latin-ext */ -@font-face { - font-family: 'Poppins'; - font-style: normal; - font-weight: 100; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v20/pxiGyp8kv8JHgFVrLPTufntAKPY.woff2) format('woff2'); - unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Poppins'; - font-style: normal; - font-weight: 100; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v20/pxiGyp8kv8JHgFVrLPTucHtA.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+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, - U+2215, U+FEFF, U+FFFD; -} -/* devanagari */ -@font-face { - font-family: 'Poppins'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v20/pxiByp8kv8JHgFVrLDz8Z11lFc-K.woff2) format('woff2'); - unicode-range: U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+25CC, U+A830-A839, U+A8E0-A8FF; -} -/* latin-ext */ -@font-face { - font-family: 'Poppins'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v20/pxiByp8kv8JHgFVrLDz8Z1JlFc-K.woff2) format('woff2'); - unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Poppins'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v20/pxiByp8kv8JHgFVrLDz8Z1xlFQ.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+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, - U+2215, U+FEFF, U+FFFD; -} -/* devanagari */ -@font-face { - font-family: 'Poppins'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v20/pxiEyp8kv8JHgFVrJJbecmNE.woff2) format('woff2'); - unicode-range: U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+25CC, U+A830-A839, U+A8E0-A8FF; -} -/* latin-ext */ -@font-face { - font-family: 'Poppins'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v20/pxiEyp8kv8JHgFVrJJnecmNE.woff2) format('woff2'); - unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Poppins'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v20/pxiEyp8kv8JHgFVrJJfecg.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+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, - U+2215, U+FEFF, U+FFFD; -} -/* devanagari */ -@font-face { - font-family: 'Poppins'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v20/pxiByp8kv8JHgFVrLGT9Z11lFc-K.woff2) format('woff2'); - unicode-range: U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+25CC, U+A830-A839, U+A8E0-A8FF; -} -/* latin-ext */ -@font-face { - font-family: 'Poppins'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v20/pxiByp8kv8JHgFVrLGT9Z1JlFc-K.woff2) format('woff2'); - unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Poppins'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v20/pxiByp8kv8JHgFVrLGT9Z1xlFQ.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+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, - U+2215, U+FEFF, U+FFFD; -} -/* devanagari */ -@font-face { - font-family: 'Poppins'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v20/pxiByp8kv8JHgFVrLCz7Z11lFc-K.woff2) format('woff2'); - unicode-range: U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+25CC, U+A830-A839, U+A8E0-A8FF; -} -/* latin-ext */ -@font-face { - font-family: 'Poppins'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v20/pxiByp8kv8JHgFVrLCz7Z1JlFc-K.woff2) format('woff2'); - unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Poppins'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v20/pxiByp8kv8JHgFVrLCz7Z1xlFQ.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+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, - U+2215, U+FEFF, U+FFFD; -} -/* cyrillic-ext */ -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 100; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOkCnqEu92Fr1MmgVxFIzIFKw.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 100; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOkCnqEu92Fr1MmgVxMIzIFKw.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* greek-ext */ -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 100; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOkCnqEu92Fr1MmgVxEIzIFKw.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; -} -/* greek */ -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 100; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOkCnqEu92Fr1MmgVxLIzIFKw.woff2) format('woff2'); - unicode-range: U+0370-03FF; -} -/* vietnamese */ -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 100; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOkCnqEu92Fr1MmgVxHIzIFKw.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: 'Roboto'; - font-style: normal; - font-weight: 100; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOkCnqEu92Fr1MmgVxGIzIFKw.woff2) format('woff2'); - unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 100; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOkCnqEu92Fr1MmgVxIIzI.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+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, - U+2215, U+FEFF, U+FFFD; -} -/* cyrillic-ext */ -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmSU5fCRc4EsA.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmSU5fABc4EsA.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* greek-ext */ -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmSU5fCBc4EsA.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; -} -/* greek */ -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmSU5fBxc4EsA.woff2) format('woff2'); - unicode-range: U+0370-03FF; -} -/* vietnamese */ -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmSU5fCxc4EsA.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: 'Roboto'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmSU5fChc4EsA.woff2) format('woff2'); - unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmSU5fBBc4.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+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, - U+2215, U+FEFF, U+FFFD; -} -/* cyrillic-ext */ -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOmCnqEu92Fr1Mu72xKOzY.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOmCnqEu92Fr1Mu5mxKOzY.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* greek-ext */ -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOmCnqEu92Fr1Mu7mxKOzY.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; -} -/* greek */ -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOmCnqEu92Fr1Mu4WxKOzY.woff2) format('woff2'); - unicode-range: U+0370-03FF; -} -/* vietnamese */ -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOmCnqEu92Fr1Mu7WxKOzY.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: 'Roboto'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOmCnqEu92Fr1Mu7GxKOzY.woff2) format('woff2'); - unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOmCnqEu92Fr1Mu4mxK.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+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, - U+2215, U+FEFF, U+FFFD; -} -/* cyrillic-ext */ -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmEU9fCRc4EsA.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmEU9fABc4EsA.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* greek-ext */ -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmEU9fCBc4EsA.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; -} -/* greek */ -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmEU9fBxc4EsA.woff2) format('woff2'); - unicode-range: U+0370-03FF; -} -/* vietnamese */ -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmEU9fCxc4EsA.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: 'Roboto'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmEU9fChc4EsA.woff2) format('woff2'); - unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmEU9fBBc4.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+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, - U+2215, U+FEFF, U+FFFD; -} -/* cyrillic-ext */ -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmWUlfCRc4EsA.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmWUlfABc4EsA.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* greek-ext */ -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmWUlfCBc4EsA.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; -} -/* greek */ -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmWUlfBxc4EsA.woff2) format('woff2'); - unicode-range: U+0370-03FF; -} -/* vietnamese */ -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmWUlfCxc4EsA.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: 'Roboto'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmWUlfChc4EsA.woff2) format('woff2'); - unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Roboto'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmWUlfBBc4.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+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, - U+2215, U+FEFF, U+FFFD; -} -/* cyrillic-ext */ -@font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoC1CzjvWyNL4U.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoC1CzjtGyNL4U.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* greek-ext */ -@font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoC1CzjvGyNL4U.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; -} -/* greek */ -@font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoC1Czjs2yNL4U.woff2) format('woff2'); - unicode-range: U+0370-03FF; -} -/* latin-ext */ -@font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoC1CzjvmyNL4U.woff2) format('woff2'); - unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoC1CzjsGyN.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+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, - U+2215, U+FEFF, U+FFFD; -} -/* cyrillic-ext */ -@font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCs6KVjbNBYlgoKcg72j00.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCs6KVjbNBYlgoKew72j00.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* greek-ext */ -@font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCs6KVjbNBYlgoKcw72j00.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; -} -/* greek */ -@font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCs6KVjbNBYlgoKfA72j00.woff2) format('woff2'); - unicode-range: U+0370-03FF; -} -/* latin-ext */ -@font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCs6KVjbNBYlgoKcQ72j00.woff2) format('woff2'); - unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCs6KVjbNBYlgoKfw72.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+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, - U+2215, U+FEFF, U+FFFD; -} -/* cyrillic-ext */ -@font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoCjC3jvWyNL4U.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoCjC3jtGyNL4U.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* greek-ext */ -@font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoCjC3jvGyNL4U.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; -} -/* greek */ -@font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoCjC3js2yNL4U.woff2) format('woff2'); - unicode-range: U+0370-03FF; -} -/* latin-ext */ -@font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoCjC3jvmyNL4U.woff2) format('woff2'); - unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoCjC3jsGyN.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+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, - U+2215, U+FEFF, U+FFFD; -} -/* cyrillic-ext */ -@font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoCxCvjvWyNL4U.woff2) format('woff2'); - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} -/* cyrillic */ -@font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoCxCvjtGyNL4U.woff2) format('woff2'); - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} -/* greek-ext */ -@font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoCxCvjvGyNL4U.woff2) format('woff2'); - unicode-range: U+1F00-1FFF; -} -/* greek */ -@font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoCxCvjs2yNL4U.woff2) format('woff2'); - unicode-range: U+0370-03FF; -} -/* latin-ext */ -@font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoCxCvjvmyNL4U.woff2) format('woff2'); - unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -/* latin */ -@font-face { - font-family: 'Ubuntu'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoCxCvjsGyN.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+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, - U+2215, U+FEFF, U+FFFD; +@theme { + /* Base Colors */ + --color-background: oklch(0.1 0.005 260); + --color-text: oklch(0.85 0.01 260); + + /* Text Variants (cool, tuned to background) */ + --color-text-muted: oklch(0.6 0.005 260); + --color-text-strong: oklch(0.95 0.01 260); + --color-text-inverse: oklch(0.15 0.005 260); + + /* Primary Color Group (warm hue 35) */ + --color-primary: oklch(0.75 0.23 35); + --color-primary-text: oklch(0.85 0.14 65); + + /* Secondary Color Group (warm hue 55) */ + --color-secondary: oklch(0.8 0.17 55); + --color-secondary-text: oklch(0.7 0.01 260); /* cool-muted */ + + /* Grays (cool neutral, hue 260) */ + --color-gray-950: oklch(0.12 0.002 260); + --color-gray-900: oklch(0.16 0.003 260); + --color-gray-800: oklch(0.25 0.004 260); + --color-gray-600: oklch(0.4 0.006 260); + --color-gray-400: oklch(0.6 0.008 260); + --color-gray-200: oklch(0.75 0.01 260); + --color-gray-50: oklch(0.9 0.015 260); + + /* Primary color spectrum */ + --color-primary-50: oklch(0.98 0.04 35); + --color-primary-100: oklch(0.94 0.08 35); + --color-primary-200: oklch(0.87 0.13 35); + --color-primary-300: oklch(0.8 0.18 35); + --color-primary-400: oklch(0.73 0.21 35); + --color-primary-500: oklch(0.75 0.23 35); /* base */ + --color-primary-600: oklch(0.65 0.2 35); + --color-primary-700: oklch(0.53 0.16 35); + --color-primary-800: oklch(0.42 0.12 35); + --color-primary-900: oklch(0.32 0.08 35); + --color-primary-950: oklch(0.23 0.05 35); + + /* UI / Surface Colors */ + --color-border: oklch(0.2 0.004 260); + --color-divider: oklch(0.3 0.005 260); + --color-surface: oklch(0.18 0.004 260); + + --font-roboto: 'Roboto', sans-serif; + --font-ubuntu: 'Ubuntu', sans-serif; + --font-poppins: 'Poppins', sans-serif; + --font-ephesis: 'Ephesis', cursive; } diff --git a/src/app.d.ts b/src/app.d.ts index c7c0ed1..e9134cd 100644 --- a/src/app.d.ts +++ b/src/app.d.ts @@ -1,13 +1,15 @@ -// See https://kit.svelte.dev/docs/types#app +// 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 {} + namespace App { + // interface Error {} + interface Locals { + is404?: boolean; } + // interface PageData {} + // interface PageState {} + // interface Platform {} + } } export {}; diff --git a/src/app.html b/src/app.html index 05ad376..0dc0086 100644 --- a/src/app.html +++ b/src/app.html @@ -1,43 +1,84 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - %sveltekit.head% - - - -
%sveltekit.body%
- + + + + + + + + + + + + + + + + + + + + + + + + + + + + + %sveltekit.head% + + + + + + + + + + + +
%sveltekit.body%
+ diff --git a/src/components/Footer.svelte b/src/components/Footer.svelte new file mode 100644 index 0000000..e4fcb9c --- /dev/null +++ b/src/components/Footer.svelte @@ -0,0 +1,3 @@ +
+ Patrik Mintěl © {new Date().getFullYear()} +
diff --git a/src/components/Icon.svelte b/src/components/Icon.svelte deleted file mode 100644 index d05c9fa..0000000 --- a/src/components/Icon.svelte +++ /dev/null @@ -1,10 +0,0 @@ - - - - {@render children?.()} - diff --git a/src/components/Navigation.svelte b/src/components/Navigation.svelte new file mode 100644 index 0000000..ceec170 --- /dev/null +++ b/src/components/Navigation.svelte @@ -0,0 +1,273 @@ + + + + {#if currentItem} + {title} + + {/if} + + + + + + + + +{#if _state.userState.logged && _state.path.startsWith('/admin')} + +{/if} + + (mobileOpened = true)} + name="bi-list" + class="border-text transition-color hover:bg-primary m-4 ml-auto w-max cursor-pointer rounded-md border-2 px-1 text-4xl duration-200 md:hidden" +/> + +{#if mobileOpened} +
+ (mobileOpened = false)} + name="bi-x-lg" + class="border-text transition-color hover:bg-primary m-4 ml-auto w-max cursor-pointer rounded-md border-2 px-1 text-4xl duration-200 md:hidden" + /> + + {#each filteredNavigation as item, index (index)} + {@const isActive = _isActive(item)} + (mobileOpened = false)} + href="/{selectedLanguage}{item.path}" + class={{ + 'text-3xl font-bold transition-colors duration-200 ease-in-out': true, + 'text-primary': isActive, + 'hover:text-primary-text': !isActive + }} + > + + {item.name} + + {/each} + + {#if _state.userState.logged && _state.path.startsWith('/admin')} +
+ {#each filteredAdminNavigation as item, index (index)} + {@const isActive = _isActive(item)} + (mobileOpened = false)} + href="/{selectedLanguage}{item.path}" + class={{ + 'text-3xl font-bold transition-colors duration-200 ease-in-out': true, + 'text-primary': isActive, + 'hover:text-primary-text': !isActive + }} + > + + {item.name} + + {/each} + {/if} +
+{/if} diff --git a/src/components/admin/chart.svelte b/src/components/admin/chart.svelte deleted file mode 100644 index 2d76f27..0000000 --- a/src/components/admin/chart.svelte +++ /dev/null @@ -1,37 +0,0 @@ - - - diff --git a/src/components/admin/fileInput.svelte b/src/components/admin/fileInput.svelte deleted file mode 100644 index 69912de..0000000 --- a/src/components/admin/fileInput.svelte +++ /dev/null @@ -1,82 +0,0 @@ - - -
{ - return; - }} - ondragover={handleDragOver} - ondragenter={handleEnter} - ondragleave={handleLeave} - onclick={() => input.click()} - role="button" - tabindex="0" - class={cls} -> - {@render children()} -
- diff --git a/src/components/admin/gallery.svelte b/src/components/admin/gallery.svelte deleted file mode 100644 index 90b7db1..0000000 --- a/src/components/admin/gallery.svelte +++ /dev/null @@ -1,9 +0,0 @@ - - -
- {#each images as image} - gallery item - {/each} -
diff --git a/src/components/admin/galleryManage.svelte b/src/components/admin/galleryManage.svelte deleted file mode 100644 index 7ae070c..0000000 --- a/src/components/admin/galleryManage.svelte +++ /dev/null @@ -1,214 +0,0 @@ - - -
-

- {#if data.id !== undefined} - Úprava {data.alt} - {:else} - Nový obrázek - {/if} -

- - - - - {#if !data.name} - - {:else} - preview of project - {/if} - - - - - - - - - - - - - - - - - - -
- {#each data.equipment as equipment} -
- {equipment.name} - removeEquipment(equipment.id)} class="cursor-pointer text-red-500" name="bi-trash-fill" /> -
- {/each} - -
-
- - - - - - - -
diff --git a/src/components/admin/message.svelte b/src/components/admin/message.svelte deleted file mode 100644 index cf99625..0000000 --- a/src/components/admin/message.svelte +++ /dev/null @@ -1,18 +0,0 @@ - - -

- {@render children()} -

diff --git a/src/components/admin/navigation.svelte b/src/components/admin/navigation.svelte deleted file mode 100644 index f16e3ce..0000000 --- a/src/components/admin/navigation.svelte +++ /dev/null @@ -1,128 +0,0 @@ - - - - -{#if $userState.logged} - (folded = true)}> - - -{/if} diff --git a/src/components/admin/project.svelte b/src/components/admin/project.svelte deleted file mode 100644 index 4c29505..0000000 --- a/src/components/admin/project.svelte +++ /dev/null @@ -1,25 +0,0 @@ - - - -
- {name} -
-

{name}

-

- {date.toLocaleDateString('cs-CZ')} -

-
-
-
diff --git a/src/components/admin/tagCreate.svelte b/src/components/admin/tagCreate.svelte deleted file mode 100644 index ec3306d..0000000 --- a/src/components/admin/tagCreate.svelte +++ /dev/null @@ -1,114 +0,0 @@ - - - - - - - - - - - {#if color === 'custom'} - - {/if} - - - - - {tagText ?? 'Nějaký text'} - - - - - - diff --git a/src/components/admin/tagSelect.svelte b/src/components/admin/tagSelect.svelte deleted file mode 100644 index 690ef66..0000000 --- a/src/components/admin/tagSelect.svelte +++ /dev/null @@ -1,38 +0,0 @@ - - -{#if color.startsWith('#')} -
- {@render children()} - -
-{:else} -
- {@render children()} - -
-{/if} diff --git a/src/components/button.svelte b/src/components/button.svelte deleted file mode 100644 index 5949a74..0000000 --- a/src/components/button.svelte +++ /dev/null @@ -1,22 +0,0 @@ - - - diff --git a/src/components/clickOutside.svelte b/src/components/clickOutside.svelte deleted file mode 100644 index 1a9c66d..0000000 --- a/src/components/clickOutside.svelte +++ /dev/null @@ -1,34 +0,0 @@ - - -
{@render children()}
diff --git a/src/components/form/Button.svelte b/src/components/form/Button.svelte new file mode 100644 index 0000000..dd9b9de --- /dev/null +++ b/src/components/form/Button.svelte @@ -0,0 +1,19 @@ + + + diff --git a/src/components/form/DatePicker.svelte b/src/components/form/DatePicker.svelte new file mode 100644 index 0000000..7a33ef1 --- /dev/null +++ b/src/components/form/DatePicker.svelte @@ -0,0 +1,59 @@ + + + diff --git a/src/components/form/FormItem.svelte b/src/components/form/FormItem.svelte new file mode 100644 index 0000000..97668a6 --- /dev/null +++ b/src/components/form/FormItem.svelte @@ -0,0 +1,57 @@ + + +
+
+ + {#if right} + {@render right()} + {/if} +
+ {@render children()} + {error ?? '...'} +
diff --git a/src/components/form/Input.svelte b/src/components/form/Input.svelte new file mode 100644 index 0000000..d49f985 --- /dev/null +++ b/src/components/form/Input.svelte @@ -0,0 +1,55 @@ + + + diff --git a/src/components/form/Select.svelte b/src/components/form/Select.svelte new file mode 100644 index 0000000..9ceda8b --- /dev/null +++ b/src/components/form/Select.svelte @@ -0,0 +1,27 @@ + + + diff --git a/src/components/form/TextArea.svelte b/src/components/form/TextArea.svelte new file mode 100644 index 0000000..faae921 --- /dev/null +++ b/src/components/form/TextArea.svelte @@ -0,0 +1,25 @@ + + + diff --git a/src/components/form/index.ts b/src/components/form/index.ts new file mode 100644 index 0000000..abd0a94 --- /dev/null +++ b/src/components/form/index.ts @@ -0,0 +1,6 @@ +export { default as Button } from './Button.svelte'; +export { default as DatePicker } from './DatePicker.svelte'; +export { default as FormItem } from './FormItem.svelte'; +export { default as Input } from './Input.svelte'; +export { default as Select } from './Select.svelte'; +export { default as TextArea } from './TextArea.svelte'; diff --git a/src/components/galleryItem.svelte b/src/components/galleryItem.svelte deleted file mode 100644 index 79f7379..0000000 --- a/src/components/galleryItem.svelte +++ /dev/null @@ -1,107 +0,0 @@ - - - - {data.alt} -
-

- {data.alt} - {#if admin} - - {/if} -

- -

Datum Pořízení:

-

{formatDate(data.date)}

-
- {#if Object.keys(transformedEquipment).length > 0} - -

Vybavení:

- {#each Object.entries(transformedEquipment) as [name, items]} - -

{name}:

-
    - {#each items as item} - - {item.name} - - {/each} -
-
- {/each} -
- {/if} -
-
diff --git a/src/components/group.svelte b/src/components/group.svelte deleted file mode 100644 index c9eace8..0000000 --- a/src/components/group.svelte +++ /dev/null @@ -1,10 +0,0 @@ - - -
- {@render children()} -
diff --git a/src/components/headers/H1.svelte b/src/components/headers/H1.svelte new file mode 100644 index 0000000..81791ba --- /dev/null +++ b/src/components/headers/H1.svelte @@ -0,0 +1,18 @@ + + +

+ {@render children?.()} +

diff --git a/src/components/headers/H2.svelte b/src/components/headers/H2.svelte new file mode 100644 index 0000000..b3be8d1 --- /dev/null +++ b/src/components/headers/H2.svelte @@ -0,0 +1,15 @@ + + +

+ {@render children?.()} +

diff --git a/src/components/headers/H3.svelte b/src/components/headers/H3.svelte new file mode 100644 index 0000000..32c5381 --- /dev/null +++ b/src/components/headers/H3.svelte @@ -0,0 +1,10 @@ + + +

+ {@render children?.()} +

diff --git a/src/components/headers/index.ts b/src/components/headers/index.ts new file mode 100644 index 0000000..f66878a --- /dev/null +++ b/src/components/headers/index.ts @@ -0,0 +1,3 @@ +export { default as H1 } from './H1.svelte'; +export { default as H2 } from './H2.svelte'; +export { default as H3 } from './H3.svelte'; diff --git a/src/components/input.svelte b/src/components/input.svelte deleted file mode 100644 index faa6816..0000000 --- a/src/components/input.svelte +++ /dev/null @@ -1,14 +0,0 @@ - - - (value = ev.currentTarget.value)} -/> diff --git a/src/components/label.svelte b/src/components/label.svelte deleted file mode 100644 index cb28d21..0000000 --- a/src/components/label.svelte +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/src/components/link.svelte b/src/components/link.svelte deleted file mode 100644 index 95ebf23..0000000 --- a/src/components/link.svelte +++ /dev/null @@ -1,24 +0,0 @@ - - -{#if button} - -{:else} - {@render children()} -{/if} diff --git a/src/components/navigation.svelte b/src/components/navigation.svelte deleted file mode 100644 index 198d97f..0000000 --- a/src/components/navigation.svelte +++ /dev/null @@ -1,119 +0,0 @@ - - - - {currentNavItem?.name} | patrick115.eu - - - - - - - - - - diff --git a/src/components/newForm/Button.svelte b/src/components/newForm/Button.svelte new file mode 100644 index 0000000..e9924b4 --- /dev/null +++ b/src/components/newForm/Button.svelte @@ -0,0 +1,26 @@ + + + + {@render children?.()} + diff --git a/src/components/newForm/DataProvider.svelte b/src/components/newForm/DataProvider.svelte new file mode 100644 index 0000000..a022e5a --- /dev/null +++ b/src/components/newForm/DataProvider.svelte @@ -0,0 +1,24 @@ + diff --git a/src/components/newForm/DatePicker.svelte b/src/components/newForm/DatePicker.svelte new file mode 100644 index 0000000..8eb2eff --- /dev/null +++ b/src/components/newForm/DatePicker.svelte @@ -0,0 +1,61 @@ + + + + {#snippet right()} + {#if _right} + {@render _right()} + {/if} + {#if context.multiLang} + + {/if} + {/snippet} + + + diff --git a/src/components/newForm/Form.svelte b/src/components/newForm/Form.svelte new file mode 100644 index 0000000..5cdd3f3 --- /dev/null +++ b/src/components/newForm/Form.svelte @@ -0,0 +1,443 @@ + + + + +
ev.preventDefault()} + use:enhance={enhanceFunction} + {method} + {action} +> + {@render children?.()} +
diff --git a/src/components/newForm/FormItem.svelte b/src/components/newForm/FormItem.svelte new file mode 100644 index 0000000..425f71f --- /dev/null +++ b/src/components/newForm/FormItem.svelte @@ -0,0 +1,46 @@ + + +
+
+ + {#if right} + {@render right()} + {/if} +
+ {@render children()} + {error ?? '...'} +
diff --git a/src/components/newForm/ImageManager.svelte b/src/components/newForm/ImageManager.svelte new file mode 100644 index 0000000..5b7f588 --- /dev/null +++ b/src/components/newForm/ImageManager.svelte @@ -0,0 +1,148 @@ + + + + {#snippet right()} + + + {/snippet} + +
+ {#each images as img, idx (img.name)} +
+ Preview +
+ {#each Object.entries(languages) as [langKey, langInfo] (langKey)} + {@const inputId = `${name}-${idx}-${langKey}`} +
+ + + setAltText(idx, langKey, e.currentTarget.value)} + placeholder="Description / Alt Text" + /> +
+ {/each} +
+ +
+ {/each} + + {#if images.length === 0} +
No images selected
+ {/if} +
+
diff --git a/src/components/newForm/Input.svelte b/src/components/newForm/Input.svelte new file mode 100644 index 0000000..00b33bc --- /dev/null +++ b/src/components/newForm/Input.svelte @@ -0,0 +1,62 @@ + + + + {#snippet right()} + {#if _right} + {@render _right()} + {/if} + {#if context.multiLang} + + {/if} + {/snippet} + + + diff --git a/src/components/newForm/LanguageSelector.svelte b/src/components/newForm/LanguageSelector.svelte new file mode 100644 index 0000000..bb44443 --- /dev/null +++ b/src/components/newForm/LanguageSelector.svelte @@ -0,0 +1,29 @@ + + +
+

+ + {getState().lang.language}: +

+ {#each Object.entries(languages) as [lang, data] (lang)} + + {/each} +
diff --git a/src/components/newForm/Markdown.svelte b/src/components/newForm/Markdown.svelte new file mode 100644 index 0000000..d7af287 --- /dev/null +++ b/src/components/newForm/Markdown.svelte @@ -0,0 +1,36 @@ + + + + {#snippet right()} + {#if _right} + {@render _right()} + {/if} + {/snippet} + + + diff --git a/src/components/newForm/Select.svelte b/src/components/newForm/Select.svelte new file mode 100644 index 0000000..a74877d --- /dev/null +++ b/src/components/newForm/Select.svelte @@ -0,0 +1,59 @@ + + + + {#snippet right()} + {#if _right} + {@render _right()} + {/if} + {#if context.multiLang} + + {/if} + {/snippet} + + + {@render children?.()} + + diff --git a/src/components/newForm/TextArea.svelte b/src/components/newForm/TextArea.svelte new file mode 100644 index 0000000..cae1af0 --- /dev/null +++ b/src/components/newForm/TextArea.svelte @@ -0,0 +1,81 @@ + + + + {#snippet right()} + {#if _right} + {@render _right()} + {/if} + {#if context.multiLang} + + {/if} + {/snippet} + + + diff --git a/src/components/newForm/TranslationAvailability.svelte b/src/components/newForm/TranslationAvailability.svelte new file mode 100644 index 0000000..fb3e4de --- /dev/null +++ b/src/components/newForm/TranslationAvailability.svelte @@ -0,0 +1,39 @@ + + +
+ {#each Object.entries(languages) as [lang, langData] (lang)} + {@const langErrors = (errors as unknown as Record>)[ + lang + ]} + {@const error = langErrors?.[path]} + {@const langData_ = (data as Record>)[lang]} + {@const value = langData_?.[path]} + {@const langDefault = (defaultValue as Record>)[lang]} + {@const defaultVal = langDefault?.[path]} + {@const isDefaultValue = defaultVal === '' || defaultVal === null} + {@const isValid = + error === undefined && (isDefaultValue ? value !== defaultVal : true)} +
+ + {langData.flag} +
+ {/each} +
diff --git a/src/components/newForm/index.ts b/src/components/newForm/index.ts new file mode 100644 index 0000000..32c4779 --- /dev/null +++ b/src/components/newForm/index.ts @@ -0,0 +1,12 @@ +export { default as Button } from './Button.svelte'; +export { default as DataProvider } from './DataProvider.svelte'; +export { default as DatePicker } from './DatePicker.svelte'; +export { default as Form } from './Form.svelte'; +export { default as FormItem } from './FormItem.svelte'; +export { default as ImageManager } from './ImageManager.svelte'; +export { default as Input } from './Input.svelte'; +export { default as LanguageSelector } from './LanguageSelector.svelte'; +export { default as Markdown } from './Markdown.svelte'; +export { default as Select } from './Select.svelte'; +export { default as TextArea } from './TextArea.svelte'; +export { default as TranslationAvailability } from './TranslationAvailability.svelte'; diff --git a/src/components/pre.svelte b/src/components/pre.svelte deleted file mode 100644 index 720b3a2..0000000 --- a/src/components/pre.svelte +++ /dev/null @@ -1,8 +0,0 @@ - - -

{@render children()}

diff --git a/src/components/project.svelte b/src/components/project.svelte deleted file mode 100644 index bd60bb5..0000000 --- a/src/components/project.svelte +++ /dev/null @@ -1,25 +0,0 @@ - - - - Project's Preview -
-

{project.name}

-

{project.date.toLocaleDateString()}

-
-
{@html createSimpleMarkDown(project.description)}
-
- {#each project.tags as tag} - {tag.name} - {/each} -
-
diff --git a/src/components/select.svelte b/src/components/select.svelte deleted file mode 100644 index e15bf24..0000000 --- a/src/components/select.svelte +++ /dev/null @@ -1,20 +0,0 @@ - - - diff --git a/src/components/table/TBody.svelte b/src/components/table/TBody.svelte new file mode 100644 index 0000000..26e4ebe --- /dev/null +++ b/src/components/table/TBody.svelte @@ -0,0 +1,13 @@ + + + + {@render children?.()} + diff --git a/src/components/table/THead.svelte b/src/components/table/THead.svelte new file mode 100644 index 0000000..99c44e3 --- /dev/null +++ b/src/components/table/THead.svelte @@ -0,0 +1,11 @@ + + + + {@render children?.()} + diff --git a/src/components/table/Table.svelte b/src/components/table/Table.svelte new file mode 100644 index 0000000..e4d0e8c --- /dev/null +++ b/src/components/table/Table.svelte @@ -0,0 +1,15 @@ + + +
+ + {@render children?.()} +
+
diff --git a/src/components/table/Td.svelte b/src/components/table/Td.svelte new file mode 100644 index 0000000..3b01e38 --- /dev/null +++ b/src/components/table/Td.svelte @@ -0,0 +1,13 @@ + + + + {@render children?.()} + diff --git a/src/components/table/Th.svelte b/src/components/table/Th.svelte new file mode 100644 index 0000000..b5ae2e6 --- /dev/null +++ b/src/components/table/Th.svelte @@ -0,0 +1,13 @@ + + + + {@render children?.()} + diff --git a/src/components/table/Tr.svelte b/src/components/table/Tr.svelte new file mode 100644 index 0000000..576a787 --- /dev/null +++ b/src/components/table/Tr.svelte @@ -0,0 +1,19 @@ + + + + {@render children?.()} + diff --git a/src/components/table/index.ts b/src/components/table/index.ts new file mode 100644 index 0000000..680d765 --- /dev/null +++ b/src/components/table/index.ts @@ -0,0 +1,6 @@ +export { default as Table } from './Table.svelte'; +export { default as TBody } from './TBody.svelte'; +export { default as Td } from './Td.svelte'; +export { default as Th } from './Th.svelte'; +export { default as THead } from './THead.svelte'; +export { default as Tr } from './Tr.svelte'; diff --git a/src/components/tag.svelte b/src/components/tag.svelte deleted file mode 100644 index 94772ce..0000000 --- a/src/components/tag.svelte +++ /dev/null @@ -1,17 +0,0 @@ - - -{#if color.startsWith('#')} - -{:else} - -{/if} diff --git a/src/components/textArea.svelte b/src/components/textArea.svelte deleted file mode 100644 index 61c4cfc..0000000 --- a/src/components/textArea.svelte +++ /dev/null @@ -1,17 +0,0 @@ - - - diff --git a/src/components/timelinePart.svelte b/src/components/timelinePart.svelte deleted file mode 100644 index 7bf847b..0000000 --- a/src/components/timelinePart.svelte +++ /dev/null @@ -1,43 +0,0 @@ - - -
- {#if typeof src === 'string'} - - {:else} - - {#each Object.keys(src).filter((src) => src !== 'default') as selector} - - {/each} - - - {/if} -
-
- {#if label !== undefined} - {label} - {/if} -
-
- {@render children()} -
diff --git a/src/components/utility/Card.svelte b/src/components/utility/Card.svelte new file mode 100644 index 0000000..bf42b91 --- /dev/null +++ b/src/components/utility/Card.svelte @@ -0,0 +1,22 @@ + + +
+ {@render children()} +
diff --git a/src/components/utility/Chart.svelte b/src/components/utility/Chart.svelte new file mode 100644 index 0000000..0872c8f --- /dev/null +++ b/src/components/utility/Chart.svelte @@ -0,0 +1,40 @@ + + + diff --git a/src/components/utility/Dialog.svelte b/src/components/utility/Dialog.svelte new file mode 100644 index 0000000..b640199 --- /dev/null +++ b/src/components/utility/Dialog.svelte @@ -0,0 +1,26 @@ + + +{#if opened} +
+ { + opened = false; + onClose(); + }} + class="border-text bg-background/80 m-auto flex max-w-[90%] flex-col rounded-md border-2 p-4" + > + {@render children()} + +
+{/if} diff --git a/src/components/utility/Dots.svelte b/src/components/utility/Dots.svelte new file mode 100644 index 0000000..4b997e7 --- /dev/null +++ b/src/components/utility/Dots.svelte @@ -0,0 +1,23 @@ + + +
+ + {#each Array.from({ length: count }) as _, i (i)} + + {/each} +
diff --git a/src/components/utility/Icon.svelte b/src/components/utility/Icon.svelte new file mode 100644 index 0000000..3a7294b --- /dev/null +++ b/src/components/utility/Icon.svelte @@ -0,0 +1,15 @@ + + + + {@render children?.()} + diff --git a/src/components/utility/Image.svelte b/src/components/utility/Image.svelte new file mode 100644 index 0000000..2e96edb --- /dev/null +++ b/src/components/utility/Image.svelte @@ -0,0 +1,29 @@ + + + + + + + + + + + + diff --git a/src/components/utility/Link.svelte b/src/components/utility/Link.svelte new file mode 100644 index 0000000..714e4a5 --- /dev/null +++ b/src/components/utility/Link.svelte @@ -0,0 +1,18 @@ + + + + {@render children()} + diff --git a/src/components/utility/Markdown.svelte b/src/components/utility/Markdown.svelte new file mode 100644 index 0000000..0759d38 --- /dev/null +++ b/src/components/utility/Markdown.svelte @@ -0,0 +1,18 @@ + + +
+ + {@html marked(content)} +
diff --git a/src/components/utility/RichText.svelte b/src/components/utility/RichText.svelte new file mode 100644 index 0000000..a9cee8e --- /dev/null +++ b/src/components/utility/RichText.svelte @@ -0,0 +1,29 @@ + + +{#each text as part, index (index)} + {#if typeof part === 'string'} + {#if part === '%%SPACE%%'} + + {:else} + + {@html part} + {/if} + {:else} + + {part.text} + + {/if} +{/each} diff --git a/src/components/utility/clickOutside.svelte b/src/components/utility/clickOutside.svelte new file mode 100644 index 0000000..9df55d0 --- /dev/null +++ b/src/components/utility/clickOutside.svelte @@ -0,0 +1,47 @@ + + +
{@render children()}
diff --git a/src/components/utility/index.ts b/src/components/utility/index.ts new file mode 100644 index 0000000..7dee1bf --- /dev/null +++ b/src/components/utility/index.ts @@ -0,0 +1,10 @@ +export { default as Card } from './Card.svelte'; +export { default as Chart } from './Chart.svelte'; +export { default as clickOutside } from './clickOutside.svelte'; +export { default as Dialog } from './Dialog.svelte'; +export { default as Dots } from './Dots.svelte'; +export { default as Icon } from './Icon.svelte'; +export { default as Image } from './Image.svelte'; +export { default as Link } from './Link.svelte'; +export { default as Markdown } from './Markdown.svelte'; +export { default as RichText } from './RichText.svelte'; diff --git a/src/fonts.css b/src/fonts.css new file mode 100644 index 0000000..021248d --- /dev/null +++ b/src/fonts.css @@ -0,0 +1,2843 @@ +/* vietnamese */ +@font-face { + font-family: 'Ephesis'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ephesis/v9/uU9PCBUS8IerL2VG3xvR38yH.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: 'Ephesis'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ephesis/v9/uU9PCBUS8IerL2VG3xrR38yH.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: 'Ephesis'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ephesis/v9/uU9PCBUS8IerL2VG3xTR3w.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; +} +/* devanagari */ +@font-face { + font-family: 'Poppins'; + font-style: italic; + font-weight: 100; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiAyp8kv8JHgFVrJJLmE0tDMPKzSQ.woff2) + format('woff2'); + unicode-range: + U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+20F0, U+25CC, U+A830-A839, + U+A8E0-A8FF, U+11B00-11B09; +} +/* latin-ext */ +@font-face { + font-family: 'Poppins'; + font-style: italic; + font-weight: 100; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiAyp8kv8JHgFVrJJLmE0tMMPKzSQ.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: 'Poppins'; + font-style: italic; + font-weight: 100; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiAyp8kv8JHgFVrJJLmE0tCMPI.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; +} +/* devanagari */ +@font-face { + font-family: 'Poppins'; + font-style: italic; + font-weight: 200; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiDyp8kv8JHgFVrJJLmv1pVFteOcEg.woff2) + format('woff2'); + unicode-range: + U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+20F0, U+25CC, U+A830-A839, + U+A8E0-A8FF, U+11B00-11B09; +} +/* latin-ext */ +@font-face { + font-family: 'Poppins'; + font-style: italic; + font-weight: 200; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiDyp8kv8JHgFVrJJLmv1pVGdeOcEg.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: 'Poppins'; + font-style: italic; + font-weight: 200; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiDyp8kv8JHgFVrJJLmv1pVF9eO.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; +} +/* devanagari */ +@font-face { + font-family: 'Poppins'; + font-style: italic; + font-weight: 300; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiDyp8kv8JHgFVrJJLm21lVFteOcEg.woff2) + format('woff2'); + unicode-range: + U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+20F0, U+25CC, U+A830-A839, + U+A8E0-A8FF, U+11B00-11B09; +} +/* latin-ext */ +@font-face { + font-family: 'Poppins'; + font-style: italic; + font-weight: 300; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiDyp8kv8JHgFVrJJLm21lVGdeOcEg.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: 'Poppins'; + font-style: italic; + font-weight: 300; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiDyp8kv8JHgFVrJJLm21lVF9eO.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; +} +/* devanagari */ +@font-face { + font-family: 'Poppins'; + font-style: italic; + font-weight: 400; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiGyp8kv8JHgFVrJJLucXtAKPY.woff2) + format('woff2'); + unicode-range: + U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+20F0, U+25CC, U+A830-A839, + U+A8E0-A8FF, U+11B00-11B09; +} +/* latin-ext */ +@font-face { + font-family: 'Poppins'; + font-style: italic; + font-weight: 400; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiGyp8kv8JHgFVrJJLufntAKPY.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: 'Poppins'; + font-style: italic; + font-weight: 400; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiGyp8kv8JHgFVrJJLucHtA.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; +} +/* devanagari */ +@font-face { + font-family: 'Poppins'; + font-style: italic; + font-weight: 500; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiDyp8kv8JHgFVrJJLmg1hVFteOcEg.woff2) + format('woff2'); + unicode-range: + U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+20F0, U+25CC, U+A830-A839, + U+A8E0-A8FF, U+11B00-11B09; +} +/* latin-ext */ +@font-face { + font-family: 'Poppins'; + font-style: italic; + font-weight: 500; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiDyp8kv8JHgFVrJJLmg1hVGdeOcEg.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: 'Poppins'; + font-style: italic; + font-weight: 500; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiDyp8kv8JHgFVrJJLmg1hVF9eO.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; +} +/* devanagari */ +@font-face { + font-family: 'Poppins'; + font-style: italic; + font-weight: 600; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiDyp8kv8JHgFVrJJLmr19VFteOcEg.woff2) + format('woff2'); + unicode-range: + U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+20F0, U+25CC, U+A830-A839, + U+A8E0-A8FF, U+11B00-11B09; +} +/* latin-ext */ +@font-face { + font-family: 'Poppins'; + font-style: italic; + font-weight: 600; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiDyp8kv8JHgFVrJJLmr19VGdeOcEg.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: 'Poppins'; + font-style: italic; + font-weight: 600; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiDyp8kv8JHgFVrJJLmr19VF9eO.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; +} +/* devanagari */ +@font-face { + font-family: 'Poppins'; + font-style: italic; + font-weight: 700; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiDyp8kv8JHgFVrJJLmy15VFteOcEg.woff2) + format('woff2'); + unicode-range: + U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+20F0, U+25CC, U+A830-A839, + U+A8E0-A8FF, U+11B00-11B09; +} +/* latin-ext */ +@font-face { + font-family: 'Poppins'; + font-style: italic; + font-weight: 700; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiDyp8kv8JHgFVrJJLmy15VGdeOcEg.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: 'Poppins'; + font-style: italic; + font-weight: 700; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiDyp8kv8JHgFVrJJLmy15VF9eO.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; +} +/* devanagari */ +@font-face { + font-family: 'Poppins'; + font-style: italic; + font-weight: 800; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiDyp8kv8JHgFVrJJLm111VFteOcEg.woff2) + format('woff2'); + unicode-range: + U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+20F0, U+25CC, U+A830-A839, + U+A8E0-A8FF, U+11B00-11B09; +} +/* latin-ext */ +@font-face { + font-family: 'Poppins'; + font-style: italic; + font-weight: 800; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiDyp8kv8JHgFVrJJLm111VGdeOcEg.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: 'Poppins'; + font-style: italic; + font-weight: 800; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiDyp8kv8JHgFVrJJLm111VF9eO.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; +} +/* devanagari */ +@font-face { + font-family: 'Poppins'; + font-style: italic; + font-weight: 900; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiDyp8kv8JHgFVrJJLm81xVFteOcEg.woff2) + format('woff2'); + unicode-range: + U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+20F0, U+25CC, U+A830-A839, + U+A8E0-A8FF, U+11B00-11B09; +} +/* latin-ext */ +@font-face { + font-family: 'Poppins'; + font-style: italic; + font-weight: 900; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiDyp8kv8JHgFVrJJLm81xVGdeOcEg.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: 'Poppins'; + font-style: italic; + font-weight: 900; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiDyp8kv8JHgFVrJJLm81xVF9eO.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; +} +/* devanagari */ +@font-face { + font-family: 'Poppins'; + font-style: normal; + font-weight: 100; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiGyp8kv8JHgFVrLPTucXtAKPY.woff2) + format('woff2'); + unicode-range: + U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+20F0, U+25CC, U+A830-A839, + U+A8E0-A8FF, U+11B00-11B09; +} +/* latin-ext */ +@font-face { + font-family: 'Poppins'; + font-style: normal; + font-weight: 100; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiGyp8kv8JHgFVrLPTufntAKPY.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: 'Poppins'; + font-style: normal; + font-weight: 100; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiGyp8kv8JHgFVrLPTucHtA.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; +} +/* devanagari */ +@font-face { + font-family: 'Poppins'; + font-style: normal; + font-weight: 200; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLFj_Z11lFc-K.woff2) + format('woff2'); + unicode-range: + U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+20F0, U+25CC, U+A830-A839, + U+A8E0-A8FF, U+11B00-11B09; +} +/* latin-ext */ +@font-face { + font-family: 'Poppins'; + font-style: normal; + font-weight: 200; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLFj_Z1JlFc-K.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: 'Poppins'; + font-style: normal; + font-weight: 200; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLFj_Z1xlFQ.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; +} +/* devanagari */ +@font-face { + font-family: 'Poppins'; + font-style: normal; + font-weight: 300; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLDz8Z11lFc-K.woff2) + format('woff2'); + unicode-range: + U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+20F0, U+25CC, U+A830-A839, + U+A8E0-A8FF, U+11B00-11B09; +} +/* latin-ext */ +@font-face { + font-family: 'Poppins'; + font-style: normal; + font-weight: 300; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLDz8Z1JlFc-K.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: 'Poppins'; + font-style: normal; + font-weight: 300; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLDz8Z1xlFQ.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; +} +/* devanagari */ +@font-face { + font-family: 'Poppins'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiEyp8kv8JHgFVrJJbecmNE.woff2) + format('woff2'); + unicode-range: + U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+20F0, U+25CC, U+A830-A839, + U+A8E0-A8FF, U+11B00-11B09; +} +/* latin-ext */ +@font-face { + font-family: 'Poppins'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiEyp8kv8JHgFVrJJnecmNE.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: 'Poppins'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiEyp8kv8JHgFVrJJfecg.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; +} +/* devanagari */ +@font-face { + font-family: 'Poppins'; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLGT9Z11lFc-K.woff2) + format('woff2'); + unicode-range: + U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+20F0, U+25CC, U+A830-A839, + U+A8E0-A8FF, U+11B00-11B09; +} +/* latin-ext */ +@font-face { + font-family: 'Poppins'; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLGT9Z1JlFc-K.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: 'Poppins'; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLGT9Z1xlFQ.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; +} +/* devanagari */ +@font-face { + font-family: 'Poppins'; + font-style: normal; + font-weight: 600; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLEj6Z11lFc-K.woff2) + format('woff2'); + unicode-range: + U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+20F0, U+25CC, U+A830-A839, + U+A8E0-A8FF, U+11B00-11B09; +} +/* latin-ext */ +@font-face { + font-family: 'Poppins'; + font-style: normal; + font-weight: 600; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLEj6Z1JlFc-K.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: 'Poppins'; + font-style: normal; + font-weight: 600; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLEj6Z1xlFQ.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; +} +/* devanagari */ +@font-face { + font-family: 'Poppins'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLCz7Z11lFc-K.woff2) + format('woff2'); + unicode-range: + U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+20F0, U+25CC, U+A830-A839, + U+A8E0-A8FF, U+11B00-11B09; +} +/* latin-ext */ +@font-face { + font-family: 'Poppins'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLCz7Z1JlFc-K.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: 'Poppins'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLCz7Z1xlFQ.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; +} +/* devanagari */ +@font-face { + font-family: 'Poppins'; + font-style: normal; + font-weight: 800; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLDD4Z11lFc-K.woff2) + format('woff2'); + unicode-range: + U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+20F0, U+25CC, U+A830-A839, + U+A8E0-A8FF, U+11B00-11B09; +} +/* latin-ext */ +@font-face { + font-family: 'Poppins'; + font-style: normal; + font-weight: 800; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLDD4Z1JlFc-K.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: 'Poppins'; + font-style: normal; + font-weight: 800; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLDD4Z1xlFQ.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; +} +/* devanagari */ +@font-face { + font-family: 'Poppins'; + font-style: normal; + font-weight: 900; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLBT5Z11lFc-K.woff2) + format('woff2'); + unicode-range: + U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+20F0, U+25CC, U+A830-A839, + U+A8E0-A8FF, U+11B00-11B09; +} +/* latin-ext */ +@font-face { + font-family: 'Poppins'; + font-style: normal; + font-weight: 900; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLBT5Z1JlFc-K.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: 'Poppins'; + font-style: normal; + font-weight: 900; + font-display: swap; + src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLBT5Z1xlFQ.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; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 100; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkC3kaWzU.woff2) + format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 100; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkAnkaWzU.woff2) + format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* greek-ext */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 100; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkCnkaWzU.woff2) + format('woff2'); + unicode-range: U+1F00-1FFF; +} +/* greek */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 100; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkBXkaWzU.woff2) + format('woff2'); + unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; +} +/* math */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 100; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkenkaWzU.woff2) + format('woff2'); + unicode-range: + U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315, U+031A, U+0326-0327, U+032C, + U+032F-0330, U+0332-0333, U+0338, U+033A, U+0346, U+034D, U+0391-03A1, U+03A3-03A9, + U+03B1-03C9, U+03D1, U+03D5-03D6, U+03F0-03F1, U+03F4-03F5, U+2016-2017, U+2034-2038, + U+203C, U+2040, U+2043, U+2047, U+2050, U+2057, U+205F, U+2070-2071, U+2074-208E, + U+2090-209C, U+20D0-20DC, U+20E1, U+20E5-20EF, U+2100-2112, U+2114-2115, U+2117-2121, + U+2123-214F, U+2190, U+2192, U+2194-21AE, U+21B0-21E5, U+21F1-21F2, U+21F4-2211, + U+2213-2214, U+2216-22FF, U+2308-230B, U+2310, U+2319, U+231C-2321, U+2336-237A, + U+237C, U+2395, U+239B-23B7, U+23D0, U+23DC-23E1, U+2474-2475, U+25AF, U+25B3, U+25B7, + U+25BD, U+25C1, U+25CA, U+25CC, U+25FB, U+266D-266F, U+27C0-27FF, U+2900-2AFF, + U+2B0E-2B11, U+2B30-2B4C, U+2BFE, U+3030, U+FF5B, U+FF5D, U+1D400-1D7FF, U+1EE00-1EEFF; +} +/* symbols */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 100; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkaHkaWzU.woff2) + format('woff2'); + unicode-range: + U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, + U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, + U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, + U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, + U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, + U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, + U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, + U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, + U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, + U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, + U+1F4A3, U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, + U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, + U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, + U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, + U+1F6B9-1F6BA, U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, + U+1F6F7-1F6FC, U+1F700-1F7FF, U+1F800-1F80B, U+1F810-1F847, U+1F850-1F859, + U+1F860-1F887, U+1F890-1F8AD, U+1F8B0-1F8BB, U+1F8C0-1F8C1, U+1F900-1F90B, U+1F93B, + U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FA89, + U+1FA8F-1FAC6, U+1FACE-1FADC, U+1FADF-1FAE9, U+1FAF0-1FAF8, U+1FB00-1FBFF; +} +/* vietnamese */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 100; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkCXkaWzU.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: 'Roboto'; + font-style: italic; + font-weight: 100; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkCHkaWzU.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: 'Roboto'; + font-style: italic; + font-weight: 100; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkBnka.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; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 300; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkC3kaWzU.woff2) + format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 300; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkAnkaWzU.woff2) + format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* greek-ext */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 300; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkCnkaWzU.woff2) + format('woff2'); + unicode-range: U+1F00-1FFF; +} +/* greek */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 300; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkBXkaWzU.woff2) + format('woff2'); + unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; +} +/* math */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 300; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkenkaWzU.woff2) + format('woff2'); + unicode-range: + U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315, U+031A, U+0326-0327, U+032C, + U+032F-0330, U+0332-0333, U+0338, U+033A, U+0346, U+034D, U+0391-03A1, U+03A3-03A9, + U+03B1-03C9, U+03D1, U+03D5-03D6, U+03F0-03F1, U+03F4-03F5, U+2016-2017, U+2034-2038, + U+203C, U+2040, U+2043, U+2047, U+2050, U+2057, U+205F, U+2070-2071, U+2074-208E, + U+2090-209C, U+20D0-20DC, U+20E1, U+20E5-20EF, U+2100-2112, U+2114-2115, U+2117-2121, + U+2123-214F, U+2190, U+2192, U+2194-21AE, U+21B0-21E5, U+21F1-21F2, U+21F4-2211, + U+2213-2214, U+2216-22FF, U+2308-230B, U+2310, U+2319, U+231C-2321, U+2336-237A, + U+237C, U+2395, U+239B-23B7, U+23D0, U+23DC-23E1, U+2474-2475, U+25AF, U+25B3, U+25B7, + U+25BD, U+25C1, U+25CA, U+25CC, U+25FB, U+266D-266F, U+27C0-27FF, U+2900-2AFF, + U+2B0E-2B11, U+2B30-2B4C, U+2BFE, U+3030, U+FF5B, U+FF5D, U+1D400-1D7FF, U+1EE00-1EEFF; +} +/* symbols */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 300; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkaHkaWzU.woff2) + format('woff2'); + unicode-range: + U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, + U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, + U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, + U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, + U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, + U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, + U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, + U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, + U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, + U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, + U+1F4A3, U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, + U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, + U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, + U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, + U+1F6B9-1F6BA, U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, + U+1F6F7-1F6FC, U+1F700-1F7FF, U+1F800-1F80B, U+1F810-1F847, U+1F850-1F859, + U+1F860-1F887, U+1F890-1F8AD, U+1F8B0-1F8BB, U+1F8C0-1F8C1, U+1F900-1F90B, U+1F93B, + U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FA89, + U+1FA8F-1FAC6, U+1FACE-1FADC, U+1FADF-1FAE9, U+1FAF0-1FAF8, U+1FB00-1FBFF; +} +/* vietnamese */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 300; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkCXkaWzU.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: 'Roboto'; + font-style: italic; + font-weight: 300; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkCHkaWzU.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: 'Roboto'; + font-style: italic; + font-weight: 300; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkBnka.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; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 400; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkC3kaWzU.woff2) + format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 400; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkAnkaWzU.woff2) + format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* greek-ext */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 400; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkCnkaWzU.woff2) + format('woff2'); + unicode-range: U+1F00-1FFF; +} +/* greek */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 400; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkBXkaWzU.woff2) + format('woff2'); + unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; +} +/* math */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 400; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkenkaWzU.woff2) + format('woff2'); + unicode-range: + U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315, U+031A, U+0326-0327, U+032C, + U+032F-0330, U+0332-0333, U+0338, U+033A, U+0346, U+034D, U+0391-03A1, U+03A3-03A9, + U+03B1-03C9, U+03D1, U+03D5-03D6, U+03F0-03F1, U+03F4-03F5, U+2016-2017, U+2034-2038, + U+203C, U+2040, U+2043, U+2047, U+2050, U+2057, U+205F, U+2070-2071, U+2074-208E, + U+2090-209C, U+20D0-20DC, U+20E1, U+20E5-20EF, U+2100-2112, U+2114-2115, U+2117-2121, + U+2123-214F, U+2190, U+2192, U+2194-21AE, U+21B0-21E5, U+21F1-21F2, U+21F4-2211, + U+2213-2214, U+2216-22FF, U+2308-230B, U+2310, U+2319, U+231C-2321, U+2336-237A, + U+237C, U+2395, U+239B-23B7, U+23D0, U+23DC-23E1, U+2474-2475, U+25AF, U+25B3, U+25B7, + U+25BD, U+25C1, U+25CA, U+25CC, U+25FB, U+266D-266F, U+27C0-27FF, U+2900-2AFF, + U+2B0E-2B11, U+2B30-2B4C, U+2BFE, U+3030, U+FF5B, U+FF5D, U+1D400-1D7FF, U+1EE00-1EEFF; +} +/* symbols */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 400; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkaHkaWzU.woff2) + format('woff2'); + unicode-range: + U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, + U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, + U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, + U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, + U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, + U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, + U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, + U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, + U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, + U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, + U+1F4A3, U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, + U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, + U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, + U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, + U+1F6B9-1F6BA, U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, + U+1F6F7-1F6FC, U+1F700-1F7FF, U+1F800-1F80B, U+1F810-1F847, U+1F850-1F859, + U+1F860-1F887, U+1F890-1F8AD, U+1F8B0-1F8BB, U+1F8C0-1F8C1, U+1F900-1F90B, U+1F93B, + U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FA89, + U+1FA8F-1FAC6, U+1FACE-1FADC, U+1FADF-1FAE9, U+1FAF0-1FAF8, U+1FB00-1FBFF; +} +/* vietnamese */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 400; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkCXkaWzU.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: 'Roboto'; + font-style: italic; + font-weight: 400; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkCHkaWzU.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: 'Roboto'; + font-style: italic; + font-weight: 400; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkBnka.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; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 500; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkC3kaWzU.woff2) + format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 500; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkAnkaWzU.woff2) + format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* greek-ext */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 500; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkCnkaWzU.woff2) + format('woff2'); + unicode-range: U+1F00-1FFF; +} +/* greek */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 500; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkBXkaWzU.woff2) + format('woff2'); + unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; +} +/* math */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 500; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkenkaWzU.woff2) + format('woff2'); + unicode-range: + U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315, U+031A, U+0326-0327, U+032C, + U+032F-0330, U+0332-0333, U+0338, U+033A, U+0346, U+034D, U+0391-03A1, U+03A3-03A9, + U+03B1-03C9, U+03D1, U+03D5-03D6, U+03F0-03F1, U+03F4-03F5, U+2016-2017, U+2034-2038, + U+203C, U+2040, U+2043, U+2047, U+2050, U+2057, U+205F, U+2070-2071, U+2074-208E, + U+2090-209C, U+20D0-20DC, U+20E1, U+20E5-20EF, U+2100-2112, U+2114-2115, U+2117-2121, + U+2123-214F, U+2190, U+2192, U+2194-21AE, U+21B0-21E5, U+21F1-21F2, U+21F4-2211, + U+2213-2214, U+2216-22FF, U+2308-230B, U+2310, U+2319, U+231C-2321, U+2336-237A, + U+237C, U+2395, U+239B-23B7, U+23D0, U+23DC-23E1, U+2474-2475, U+25AF, U+25B3, U+25B7, + U+25BD, U+25C1, U+25CA, U+25CC, U+25FB, U+266D-266F, U+27C0-27FF, U+2900-2AFF, + U+2B0E-2B11, U+2B30-2B4C, U+2BFE, U+3030, U+FF5B, U+FF5D, U+1D400-1D7FF, U+1EE00-1EEFF; +} +/* symbols */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 500; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkaHkaWzU.woff2) + format('woff2'); + unicode-range: + U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, + U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, + U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, + U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, + U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, + U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, + U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, + U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, + U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, + U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, + U+1F4A3, U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, + U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, + U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, + U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, + U+1F6B9-1F6BA, U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, + U+1F6F7-1F6FC, U+1F700-1F7FF, U+1F800-1F80B, U+1F810-1F847, U+1F850-1F859, + U+1F860-1F887, U+1F890-1F8AD, U+1F8B0-1F8BB, U+1F8C0-1F8C1, U+1F900-1F90B, U+1F93B, + U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FA89, + U+1FA8F-1FAC6, U+1FACE-1FADC, U+1FADF-1FAE9, U+1FAF0-1FAF8, U+1FB00-1FBFF; +} +/* vietnamese */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 500; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkCXkaWzU.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: 'Roboto'; + font-style: italic; + font-weight: 500; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkCHkaWzU.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: 'Roboto'; + font-style: italic; + font-weight: 500; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkBnka.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; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 700; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkC3kaWzU.woff2) + format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 700; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkAnkaWzU.woff2) + format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* greek-ext */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 700; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkCnkaWzU.woff2) + format('woff2'); + unicode-range: U+1F00-1FFF; +} +/* greek */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 700; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkBXkaWzU.woff2) + format('woff2'); + unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; +} +/* math */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 700; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkenkaWzU.woff2) + format('woff2'); + unicode-range: + U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315, U+031A, U+0326-0327, U+032C, + U+032F-0330, U+0332-0333, U+0338, U+033A, U+0346, U+034D, U+0391-03A1, U+03A3-03A9, + U+03B1-03C9, U+03D1, U+03D5-03D6, U+03F0-03F1, U+03F4-03F5, U+2016-2017, U+2034-2038, + U+203C, U+2040, U+2043, U+2047, U+2050, U+2057, U+205F, U+2070-2071, U+2074-208E, + U+2090-209C, U+20D0-20DC, U+20E1, U+20E5-20EF, U+2100-2112, U+2114-2115, U+2117-2121, + U+2123-214F, U+2190, U+2192, U+2194-21AE, U+21B0-21E5, U+21F1-21F2, U+21F4-2211, + U+2213-2214, U+2216-22FF, U+2308-230B, U+2310, U+2319, U+231C-2321, U+2336-237A, + U+237C, U+2395, U+239B-23B7, U+23D0, U+23DC-23E1, U+2474-2475, U+25AF, U+25B3, U+25B7, + U+25BD, U+25C1, U+25CA, U+25CC, U+25FB, U+266D-266F, U+27C0-27FF, U+2900-2AFF, + U+2B0E-2B11, U+2B30-2B4C, U+2BFE, U+3030, U+FF5B, U+FF5D, U+1D400-1D7FF, U+1EE00-1EEFF; +} +/* symbols */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 700; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkaHkaWzU.woff2) + format('woff2'); + unicode-range: + U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, + U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, + U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, + U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, + U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, + U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, + U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, + U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, + U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, + U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, + U+1F4A3, U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, + U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, + U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, + U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, + U+1F6B9-1F6BA, U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, + U+1F6F7-1F6FC, U+1F700-1F7FF, U+1F800-1F80B, U+1F810-1F847, U+1F850-1F859, + U+1F860-1F887, U+1F890-1F8AD, U+1F8B0-1F8BB, U+1F8C0-1F8C1, U+1F900-1F90B, U+1F93B, + U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FA89, + U+1FA8F-1FAC6, U+1FACE-1FADC, U+1FADF-1FAE9, U+1FAF0-1FAF8, U+1FB00-1FBFF; +} +/* vietnamese */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 700; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkCXkaWzU.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: 'Roboto'; + font-style: italic; + font-weight: 700; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkCHkaWzU.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: 'Roboto'; + font-style: italic; + font-weight: 700; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkBnka.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; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 900; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkC3kaWzU.woff2) + format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 900; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkAnkaWzU.woff2) + format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* greek-ext */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 900; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkCnkaWzU.woff2) + format('woff2'); + unicode-range: U+1F00-1FFF; +} +/* greek */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 900; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkBXkaWzU.woff2) + format('woff2'); + unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; +} +/* math */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 900; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkenkaWzU.woff2) + format('woff2'); + unicode-range: + U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315, U+031A, U+0326-0327, U+032C, + U+032F-0330, U+0332-0333, U+0338, U+033A, U+0346, U+034D, U+0391-03A1, U+03A3-03A9, + U+03B1-03C9, U+03D1, U+03D5-03D6, U+03F0-03F1, U+03F4-03F5, U+2016-2017, U+2034-2038, + U+203C, U+2040, U+2043, U+2047, U+2050, U+2057, U+205F, U+2070-2071, U+2074-208E, + U+2090-209C, U+20D0-20DC, U+20E1, U+20E5-20EF, U+2100-2112, U+2114-2115, U+2117-2121, + U+2123-214F, U+2190, U+2192, U+2194-21AE, U+21B0-21E5, U+21F1-21F2, U+21F4-2211, + U+2213-2214, U+2216-22FF, U+2308-230B, U+2310, U+2319, U+231C-2321, U+2336-237A, + U+237C, U+2395, U+239B-23B7, U+23D0, U+23DC-23E1, U+2474-2475, U+25AF, U+25B3, U+25B7, + U+25BD, U+25C1, U+25CA, U+25CC, U+25FB, U+266D-266F, U+27C0-27FF, U+2900-2AFF, + U+2B0E-2B11, U+2B30-2B4C, U+2BFE, U+3030, U+FF5B, U+FF5D, U+1D400-1D7FF, U+1EE00-1EEFF; +} +/* symbols */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 900; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkaHkaWzU.woff2) + format('woff2'); + unicode-range: + U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, + U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, + U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, + U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, + U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, + U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, + U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, + U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, + U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, + U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, + U+1F4A3, U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, + U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, + U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, + U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, + U+1F6B9-1F6BA, U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, + U+1F6F7-1F6FC, U+1F700-1F7FF, U+1F800-1F80B, U+1F810-1F847, U+1F850-1F859, + U+1F860-1F887, U+1F890-1F8AD, U+1F8B0-1F8BB, U+1F8C0-1F8C1, U+1F900-1F90B, U+1F93B, + U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FA89, + U+1FA8F-1FAC6, U+1FACE-1FADC, U+1FADF-1FAE9, U+1FAF0-1FAF8, U+1FB00-1FBFF; +} +/* vietnamese */ +@font-face { + font-family: 'Roboto'; + font-style: italic; + font-weight: 900; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkCXkaWzU.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: 'Roboto'; + font-style: italic; + font-weight: 900; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkCHkaWzU.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: 'Roboto'; + font-style: italic; + font-weight: 900; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO5CnqEu92Fr1Mu53ZEC9_Vu3r1gIhOszmkBnka.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; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 100; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3GUBGEe.woff2) + format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 100; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3iUBGEe.woff2) + format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* greek-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 100; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3CUBGEe.woff2) + format('woff2'); + unicode-range: U+1F00-1FFF; +} +/* greek */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 100; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3-UBGEe.woff2) + format('woff2'); + unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; +} +/* math */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 100; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMawCUBGEe.woff2) + format('woff2'); + unicode-range: + U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315, U+031A, U+0326-0327, U+032C, + U+032F-0330, U+0332-0333, U+0338, U+033A, U+0346, U+034D, U+0391-03A1, U+03A3-03A9, + U+03B1-03C9, U+03D1, U+03D5-03D6, U+03F0-03F1, U+03F4-03F5, U+2016-2017, U+2034-2038, + U+203C, U+2040, U+2043, U+2047, U+2050, U+2057, U+205F, U+2070-2071, U+2074-208E, + U+2090-209C, U+20D0-20DC, U+20E1, U+20E5-20EF, U+2100-2112, U+2114-2115, U+2117-2121, + U+2123-214F, U+2190, U+2192, U+2194-21AE, U+21B0-21E5, U+21F1-21F2, U+21F4-2211, + U+2213-2214, U+2216-22FF, U+2308-230B, U+2310, U+2319, U+231C-2321, U+2336-237A, + U+237C, U+2395, U+239B-23B7, U+23D0, U+23DC-23E1, U+2474-2475, U+25AF, U+25B3, U+25B7, + U+25BD, U+25C1, U+25CA, U+25CC, U+25FB, U+266D-266F, U+27C0-27FF, U+2900-2AFF, + U+2B0E-2B11, U+2B30-2B4C, U+2BFE, U+3030, U+FF5B, U+FF5D, U+1D400-1D7FF, U+1EE00-1EEFF; +} +/* symbols */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 100; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMaxKUBGEe.woff2) + format('woff2'); + unicode-range: + U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, + U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, + U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, + U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, + U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, + U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, + U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, + U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, + U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, + U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, + U+1F4A3, U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, + U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, + U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, + U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, + U+1F6B9-1F6BA, U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, + U+1F6F7-1F6FC, U+1F700-1F7FF, U+1F800-1F80B, U+1F810-1F847, U+1F850-1F859, + U+1F860-1F887, U+1F890-1F8AD, U+1F8B0-1F8BB, U+1F8C0-1F8C1, U+1F900-1F90B, U+1F93B, + U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FA89, + U+1FA8F-1FAC6, U+1FACE-1FADC, U+1FADF-1FAE9, U+1FAF0-1FAF8, U+1FB00-1FBFF; +} +/* vietnamese */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 100; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3OUBGEe.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: 'Roboto'; + font-style: normal; + font-weight: 100; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3KUBGEe.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: 'Roboto'; + font-style: normal; + font-weight: 100; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3yUBA.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; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 300; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3GUBGEe.woff2) + format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 300; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3iUBGEe.woff2) + format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* greek-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 300; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3CUBGEe.woff2) + format('woff2'); + unicode-range: U+1F00-1FFF; +} +/* greek */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 300; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3-UBGEe.woff2) + format('woff2'); + unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; +} +/* math */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 300; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMawCUBGEe.woff2) + format('woff2'); + unicode-range: + U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315, U+031A, U+0326-0327, U+032C, + U+032F-0330, U+0332-0333, U+0338, U+033A, U+0346, U+034D, U+0391-03A1, U+03A3-03A9, + U+03B1-03C9, U+03D1, U+03D5-03D6, U+03F0-03F1, U+03F4-03F5, U+2016-2017, U+2034-2038, + U+203C, U+2040, U+2043, U+2047, U+2050, U+2057, U+205F, U+2070-2071, U+2074-208E, + U+2090-209C, U+20D0-20DC, U+20E1, U+20E5-20EF, U+2100-2112, U+2114-2115, U+2117-2121, + U+2123-214F, U+2190, U+2192, U+2194-21AE, U+21B0-21E5, U+21F1-21F2, U+21F4-2211, + U+2213-2214, U+2216-22FF, U+2308-230B, U+2310, U+2319, U+231C-2321, U+2336-237A, + U+237C, U+2395, U+239B-23B7, U+23D0, U+23DC-23E1, U+2474-2475, U+25AF, U+25B3, U+25B7, + U+25BD, U+25C1, U+25CA, U+25CC, U+25FB, U+266D-266F, U+27C0-27FF, U+2900-2AFF, + U+2B0E-2B11, U+2B30-2B4C, U+2BFE, U+3030, U+FF5B, U+FF5D, U+1D400-1D7FF, U+1EE00-1EEFF; +} +/* symbols */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 300; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMaxKUBGEe.woff2) + format('woff2'); + unicode-range: + U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, + U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, + U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, + U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, + U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, + U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, + U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, + U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, + U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, + U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, + U+1F4A3, U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, + U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, + U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, + U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, + U+1F6B9-1F6BA, U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, + U+1F6F7-1F6FC, U+1F700-1F7FF, U+1F800-1F80B, U+1F810-1F847, U+1F850-1F859, + U+1F860-1F887, U+1F890-1F8AD, U+1F8B0-1F8BB, U+1F8C0-1F8C1, U+1F900-1F90B, U+1F93B, + U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FA89, + U+1FA8F-1FAC6, U+1FACE-1FADC, U+1FADF-1FAE9, U+1FAF0-1FAF8, U+1FB00-1FBFF; +} +/* vietnamese */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 300; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3OUBGEe.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: 'Roboto'; + font-style: normal; + font-weight: 300; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3KUBGEe.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: 'Roboto'; + font-style: normal; + font-weight: 300; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3yUBA.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; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 400; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3GUBGEe.woff2) + format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 400; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3iUBGEe.woff2) + format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* greek-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 400; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3CUBGEe.woff2) + format('woff2'); + unicode-range: U+1F00-1FFF; +} +/* greek */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 400; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3-UBGEe.woff2) + format('woff2'); + unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; +} +/* math */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 400; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMawCUBGEe.woff2) + format('woff2'); + unicode-range: + U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315, U+031A, U+0326-0327, U+032C, + U+032F-0330, U+0332-0333, U+0338, U+033A, U+0346, U+034D, U+0391-03A1, U+03A3-03A9, + U+03B1-03C9, U+03D1, U+03D5-03D6, U+03F0-03F1, U+03F4-03F5, U+2016-2017, U+2034-2038, + U+203C, U+2040, U+2043, U+2047, U+2050, U+2057, U+205F, U+2070-2071, U+2074-208E, + U+2090-209C, U+20D0-20DC, U+20E1, U+20E5-20EF, U+2100-2112, U+2114-2115, U+2117-2121, + U+2123-214F, U+2190, U+2192, U+2194-21AE, U+21B0-21E5, U+21F1-21F2, U+21F4-2211, + U+2213-2214, U+2216-22FF, U+2308-230B, U+2310, U+2319, U+231C-2321, U+2336-237A, + U+237C, U+2395, U+239B-23B7, U+23D0, U+23DC-23E1, U+2474-2475, U+25AF, U+25B3, U+25B7, + U+25BD, U+25C1, U+25CA, U+25CC, U+25FB, U+266D-266F, U+27C0-27FF, U+2900-2AFF, + U+2B0E-2B11, U+2B30-2B4C, U+2BFE, U+3030, U+FF5B, U+FF5D, U+1D400-1D7FF, U+1EE00-1EEFF; +} +/* symbols */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 400; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMaxKUBGEe.woff2) + format('woff2'); + unicode-range: + U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, + U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, + U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, + U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, + U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, + U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, + U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, + U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, + U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, + U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, + U+1F4A3, U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, + U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, + U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, + U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, + U+1F6B9-1F6BA, U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, + U+1F6F7-1F6FC, U+1F700-1F7FF, U+1F800-1F80B, U+1F810-1F847, U+1F850-1F859, + U+1F860-1F887, U+1F890-1F8AD, U+1F8B0-1F8BB, U+1F8C0-1F8C1, U+1F900-1F90B, U+1F93B, + U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FA89, + U+1FA8F-1FAC6, U+1FACE-1FADC, U+1FADF-1FAE9, U+1FAF0-1FAF8, U+1FB00-1FBFF; +} +/* vietnamese */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 400; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3OUBGEe.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: 'Roboto'; + font-style: normal; + font-weight: 400; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3KUBGEe.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: 'Roboto'; + font-style: normal; + font-weight: 400; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3yUBA.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; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 500; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3GUBGEe.woff2) + format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 500; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3iUBGEe.woff2) + format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* greek-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 500; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3CUBGEe.woff2) + format('woff2'); + unicode-range: U+1F00-1FFF; +} +/* greek */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 500; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3-UBGEe.woff2) + format('woff2'); + unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; +} +/* math */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 500; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMawCUBGEe.woff2) + format('woff2'); + unicode-range: + U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315, U+031A, U+0326-0327, U+032C, + U+032F-0330, U+0332-0333, U+0338, U+033A, U+0346, U+034D, U+0391-03A1, U+03A3-03A9, + U+03B1-03C9, U+03D1, U+03D5-03D6, U+03F0-03F1, U+03F4-03F5, U+2016-2017, U+2034-2038, + U+203C, U+2040, U+2043, U+2047, U+2050, U+2057, U+205F, U+2070-2071, U+2074-208E, + U+2090-209C, U+20D0-20DC, U+20E1, U+20E5-20EF, U+2100-2112, U+2114-2115, U+2117-2121, + U+2123-214F, U+2190, U+2192, U+2194-21AE, U+21B0-21E5, U+21F1-21F2, U+21F4-2211, + U+2213-2214, U+2216-22FF, U+2308-230B, U+2310, U+2319, U+231C-2321, U+2336-237A, + U+237C, U+2395, U+239B-23B7, U+23D0, U+23DC-23E1, U+2474-2475, U+25AF, U+25B3, U+25B7, + U+25BD, U+25C1, U+25CA, U+25CC, U+25FB, U+266D-266F, U+27C0-27FF, U+2900-2AFF, + U+2B0E-2B11, U+2B30-2B4C, U+2BFE, U+3030, U+FF5B, U+FF5D, U+1D400-1D7FF, U+1EE00-1EEFF; +} +/* symbols */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 500; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMaxKUBGEe.woff2) + format('woff2'); + unicode-range: + U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, + U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, + U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, + U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, + U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, + U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, + U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, + U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, + U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, + U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, + U+1F4A3, U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, + U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, + U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, + U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, + U+1F6B9-1F6BA, U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, + U+1F6F7-1F6FC, U+1F700-1F7FF, U+1F800-1F80B, U+1F810-1F847, U+1F850-1F859, + U+1F860-1F887, U+1F890-1F8AD, U+1F8B0-1F8BB, U+1F8C0-1F8C1, U+1F900-1F90B, U+1F93B, + U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FA89, + U+1FA8F-1FAC6, U+1FACE-1FADC, U+1FADF-1FAE9, U+1FAF0-1FAF8, U+1FB00-1FBFF; +} +/* vietnamese */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 500; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3OUBGEe.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: 'Roboto'; + font-style: normal; + font-weight: 500; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3KUBGEe.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: 'Roboto'; + font-style: normal; + font-weight: 500; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3yUBA.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; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 700; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3GUBGEe.woff2) + format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 700; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3iUBGEe.woff2) + format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* greek-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 700; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3CUBGEe.woff2) + format('woff2'); + unicode-range: U+1F00-1FFF; +} +/* greek */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 700; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3-UBGEe.woff2) + format('woff2'); + unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; +} +/* math */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 700; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMawCUBGEe.woff2) + format('woff2'); + unicode-range: + U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315, U+031A, U+0326-0327, U+032C, + U+032F-0330, U+0332-0333, U+0338, U+033A, U+0346, U+034D, U+0391-03A1, U+03A3-03A9, + U+03B1-03C9, U+03D1, U+03D5-03D6, U+03F0-03F1, U+03F4-03F5, U+2016-2017, U+2034-2038, + U+203C, U+2040, U+2043, U+2047, U+2050, U+2057, U+205F, U+2070-2071, U+2074-208E, + U+2090-209C, U+20D0-20DC, U+20E1, U+20E5-20EF, U+2100-2112, U+2114-2115, U+2117-2121, + U+2123-214F, U+2190, U+2192, U+2194-21AE, U+21B0-21E5, U+21F1-21F2, U+21F4-2211, + U+2213-2214, U+2216-22FF, U+2308-230B, U+2310, U+2319, U+231C-2321, U+2336-237A, + U+237C, U+2395, U+239B-23B7, U+23D0, U+23DC-23E1, U+2474-2475, U+25AF, U+25B3, U+25B7, + U+25BD, U+25C1, U+25CA, U+25CC, U+25FB, U+266D-266F, U+27C0-27FF, U+2900-2AFF, + U+2B0E-2B11, U+2B30-2B4C, U+2BFE, U+3030, U+FF5B, U+FF5D, U+1D400-1D7FF, U+1EE00-1EEFF; +} +/* symbols */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 700; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMaxKUBGEe.woff2) + format('woff2'); + unicode-range: + U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, + U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, + U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, + U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, + U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, + U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, + U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, + U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, + U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, + U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, + U+1F4A3, U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, + U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, + U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, + U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, + U+1F6B9-1F6BA, U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, + U+1F6F7-1F6FC, U+1F700-1F7FF, U+1F800-1F80B, U+1F810-1F847, U+1F850-1F859, + U+1F860-1F887, U+1F890-1F8AD, U+1F8B0-1F8BB, U+1F8C0-1F8C1, U+1F900-1F90B, U+1F93B, + U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FA89, + U+1FA8F-1FAC6, U+1FACE-1FADC, U+1FADF-1FAE9, U+1FAF0-1FAF8, U+1FB00-1FBFF; +} +/* vietnamese */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 700; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3OUBGEe.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: 'Roboto'; + font-style: normal; + font-weight: 700; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3KUBGEe.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: 'Roboto'; + font-style: normal; + font-weight: 700; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3yUBA.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; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 900; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3GUBGEe.woff2) + format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 900; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3iUBGEe.woff2) + format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* greek-ext */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 900; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3CUBGEe.woff2) + format('woff2'); + unicode-range: U+1F00-1FFF; +} +/* greek */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 900; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3-UBGEe.woff2) + format('woff2'); + unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; +} +/* math */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 900; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMawCUBGEe.woff2) + format('woff2'); + unicode-range: + U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315, U+031A, U+0326-0327, U+032C, + U+032F-0330, U+0332-0333, U+0338, U+033A, U+0346, U+034D, U+0391-03A1, U+03A3-03A9, + U+03B1-03C9, U+03D1, U+03D5-03D6, U+03F0-03F1, U+03F4-03F5, U+2016-2017, U+2034-2038, + U+203C, U+2040, U+2043, U+2047, U+2050, U+2057, U+205F, U+2070-2071, U+2074-208E, + U+2090-209C, U+20D0-20DC, U+20E1, U+20E5-20EF, U+2100-2112, U+2114-2115, U+2117-2121, + U+2123-214F, U+2190, U+2192, U+2194-21AE, U+21B0-21E5, U+21F1-21F2, U+21F4-2211, + U+2213-2214, U+2216-22FF, U+2308-230B, U+2310, U+2319, U+231C-2321, U+2336-237A, + U+237C, U+2395, U+239B-23B7, U+23D0, U+23DC-23E1, U+2474-2475, U+25AF, U+25B3, U+25B7, + U+25BD, U+25C1, U+25CA, U+25CC, U+25FB, U+266D-266F, U+27C0-27FF, U+2900-2AFF, + U+2B0E-2B11, U+2B30-2B4C, U+2BFE, U+3030, U+FF5B, U+FF5D, U+1D400-1D7FF, U+1EE00-1EEFF; +} +/* symbols */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 900; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMaxKUBGEe.woff2) + format('woff2'); + unicode-range: + U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, + U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, + U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, + U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, + U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, + U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, + U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, + U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, + U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, + U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, + U+1F4A3, U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, + U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, + U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, + U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, + U+1F6B9-1F6BA, U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, + U+1F6F7-1F6FC, U+1F700-1F7FF, U+1F800-1F80B, U+1F810-1F847, U+1F850-1F859, + U+1F860-1F887, U+1F890-1F8AD, U+1F8B0-1F8BB, U+1F8C0-1F8C1, U+1F900-1F90B, U+1F93B, + U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FA89, + U+1FA8F-1FAC6, U+1FACE-1FADC, U+1FADF-1FAE9, U+1FAF0-1FAF8, U+1FB00-1FBFF; +} +/* vietnamese */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 900; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3OUBGEe.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: 'Roboto'; + font-style: normal; + font-weight: 900; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3KUBGEe.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: 'Roboto'; + font-style: normal; + font-weight: 900; + font-stretch: 100%; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v47/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3yUBA.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; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Ubuntu'; + font-style: italic; + font-weight: 300; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCp6KVjbNBYlgoKejZftVyCN4Ffgg.woff2) + format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Ubuntu'; + font-style: italic; + font-weight: 300; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCp6KVjbNBYlgoKejZftVyLN4Ffgg.woff2) + format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* greek-ext */ +@font-face { + font-family: 'Ubuntu'; + font-style: italic; + font-weight: 300; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCp6KVjbNBYlgoKejZftVyDN4Ffgg.woff2) + format('woff2'); + unicode-range: U+1F00-1FFF; +} +/* greek */ +@font-face { + font-family: 'Ubuntu'; + font-style: italic; + font-weight: 300; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCp6KVjbNBYlgoKejZftVyMN4Ffgg.woff2) + format('woff2'); + unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; +} +/* latin-ext */ +@font-face { + font-family: 'Ubuntu'; + font-style: italic; + font-weight: 300; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCp6KVjbNBYlgoKejZftVyBN4Ffgg.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: 'Ubuntu'; + font-style: italic; + font-weight: 300; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCp6KVjbNBYlgoKejZftVyPN4E.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; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Ubuntu'; + font-style: italic; + font-weight: 400; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCu6KVjbNBYlgoKej75l0mwFg.woff2) + format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Ubuntu'; + font-style: italic; + font-weight: 400; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCu6KVjbNBYlgoKej7wl0mwFg.woff2) + format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* greek-ext */ +@font-face { + font-family: 'Ubuntu'; + font-style: italic; + font-weight: 400; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCu6KVjbNBYlgoKej74l0mwFg.woff2) + format('woff2'); + unicode-range: U+1F00-1FFF; +} +/* greek */ +@font-face { + font-family: 'Ubuntu'; + font-style: italic; + font-weight: 400; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCu6KVjbNBYlgoKej73l0mwFg.woff2) + format('woff2'); + unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; +} +/* latin-ext */ +@font-face { + font-family: 'Ubuntu'; + font-style: italic; + font-weight: 400; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCu6KVjbNBYlgoKej76l0mwFg.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: 'Ubuntu'; + font-style: italic; + font-weight: 400; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCu6KVjbNBYlgoKej70l0k.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; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Ubuntu'; + font-style: italic; + font-weight: 500; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCp6KVjbNBYlgoKejYHtFyCN4Ffgg.woff2) + format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Ubuntu'; + font-style: italic; + font-weight: 500; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCp6KVjbNBYlgoKejYHtFyLN4Ffgg.woff2) + format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* greek-ext */ +@font-face { + font-family: 'Ubuntu'; + font-style: italic; + font-weight: 500; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCp6KVjbNBYlgoKejYHtFyDN4Ffgg.woff2) + format('woff2'); + unicode-range: U+1F00-1FFF; +} +/* greek */ +@font-face { + font-family: 'Ubuntu'; + font-style: italic; + font-weight: 500; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCp6KVjbNBYlgoKejYHtFyMN4Ffgg.woff2) + format('woff2'); + unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; +} +/* latin-ext */ +@font-face { + font-family: 'Ubuntu'; + font-style: italic; + font-weight: 500; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCp6KVjbNBYlgoKejYHtFyBN4Ffgg.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: 'Ubuntu'; + font-style: italic; + font-weight: 500; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCp6KVjbNBYlgoKejYHtFyPN4E.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; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Ubuntu'; + font-style: italic; + font-weight: 700; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCp6KVjbNBYlgoKejZPslyCN4Ffgg.woff2) + format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Ubuntu'; + font-style: italic; + font-weight: 700; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCp6KVjbNBYlgoKejZPslyLN4Ffgg.woff2) + format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* greek-ext */ +@font-face { + font-family: 'Ubuntu'; + font-style: italic; + font-weight: 700; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCp6KVjbNBYlgoKejZPslyDN4Ffgg.woff2) + format('woff2'); + unicode-range: U+1F00-1FFF; +} +/* greek */ +@font-face { + font-family: 'Ubuntu'; + font-style: italic; + font-weight: 700; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCp6KVjbNBYlgoKejZPslyMN4Ffgg.woff2) + format('woff2'); + unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; +} +/* latin-ext */ +@font-face { + font-family: 'Ubuntu'; + font-style: italic; + font-weight: 700; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCp6KVjbNBYlgoKejZPslyBN4Ffgg.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: 'Ubuntu'; + font-style: italic; + font-weight: 700; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCp6KVjbNBYlgoKejZPslyPN4E.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; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Ubuntu'; + font-style: normal; + font-weight: 300; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoC1CzjvWyNL4U.woff2) + format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Ubuntu'; + font-style: normal; + font-weight: 300; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoC1CzjtGyNL4U.woff2) + format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* greek-ext */ +@font-face { + font-family: 'Ubuntu'; + font-style: normal; + font-weight: 300; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoC1CzjvGyNL4U.woff2) + format('woff2'); + unicode-range: U+1F00-1FFF; +} +/* greek */ +@font-face { + font-family: 'Ubuntu'; + font-style: normal; + font-weight: 300; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoC1Czjs2yNL4U.woff2) + format('woff2'); + unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; +} +/* latin-ext */ +@font-face { + font-family: 'Ubuntu'; + font-style: normal; + font-weight: 300; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoC1CzjvmyNL4U.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: 'Ubuntu'; + font-style: normal; + font-weight: 300; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoC1CzjsGyN.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; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Ubuntu'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCs6KVjbNBYlgoKcg72j00.woff2) + format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Ubuntu'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCs6KVjbNBYlgoKew72j00.woff2) + format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* greek-ext */ +@font-face { + font-family: 'Ubuntu'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCs6KVjbNBYlgoKcw72j00.woff2) + format('woff2'); + unicode-range: U+1F00-1FFF; +} +/* greek */ +@font-face { + font-family: 'Ubuntu'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCs6KVjbNBYlgoKfA72j00.woff2) + format('woff2'); + unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; +} +/* latin-ext */ +@font-face { + font-family: 'Ubuntu'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCs6KVjbNBYlgoKcQ72j00.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: 'Ubuntu'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCs6KVjbNBYlgoKfw72.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; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Ubuntu'; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoCjC3jvWyNL4U.woff2) + format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Ubuntu'; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoCjC3jtGyNL4U.woff2) + format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* greek-ext */ +@font-face { + font-family: 'Ubuntu'; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoCjC3jvGyNL4U.woff2) + format('woff2'); + unicode-range: U+1F00-1FFF; +} +/* greek */ +@font-face { + font-family: 'Ubuntu'; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoCjC3js2yNL4U.woff2) + format('woff2'); + unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; +} +/* latin-ext */ +@font-face { + font-family: 'Ubuntu'; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoCjC3jvmyNL4U.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: 'Ubuntu'; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoCjC3jsGyN.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; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Ubuntu'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoCxCvjvWyNL4U.woff2) + format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Ubuntu'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoCxCvjtGyNL4U.woff2) + format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* greek-ext */ +@font-face { + font-family: 'Ubuntu'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoCxCvjvGyNL4U.woff2) + format('woff2'); + unicode-range: U+1F00-1FFF; +} +/* greek */ +@font-face { + font-family: 'Ubuntu'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoCxCvjs2yNL4U.woff2) + format('woff2'); + unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; +} +/* latin-ext */ +@font-face { + font-family: 'Ubuntu'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoCxCvjvmyNL4U.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: 'Ubuntu'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url(https://fonts.gstatic.com/s/ubuntu/v20/4iCv6KVjbNBYlgoCxCvjsGyN.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; +} diff --git a/src/hooks.server.ts b/src/hooks.server.ts new file mode 100644 index 0000000..b51551c --- /dev/null +++ b/src/hooks.server.ts @@ -0,0 +1,33 @@ +import type { Handle } from '@sveltejs/kit'; +import { conn } from './lib/server/variables'; + +export const handle = (async ({ event, resolve }) => { + const response = await resolve(event); + const path = event.url.pathname; + + const disallowedPaths = ['/api']; + const UA = event.request.headers.get('user-agent') || ''; + const isBot = /bot|crawl|spider|slurp/i.test(UA); + + if ( + !isBot && + response.status === 200 && + !event.locals.is404 && + !disallowedPaths.some((p) => path.startsWith(p)) + ) { + conn + .insertInto('visitors') + .values({ + ip: event.getClientAddress(), + page: path, + user_agent: event.request.headers.get('user-agent') || '' + }) + .execute() + .catch((err) => { + //eslint-disable-next-line no-console + console.error('Error logging visitor:', err); + }); + } + + return response; +}) satisfies Handle; diff --git a/src/lib/api.ts b/src/lib/api.ts index e577a98..47fe2fe 100644 --- a/src/lib/api.ts +++ b/src/lib/api.ts @@ -1,4 +1,4 @@ import { createAPIClient } from '@patrick115/sveltekitapi'; -import type { AppRouter } from './server/routes/_app'; +import type { AppRouter } from './server/routes'; export const API = createAPIClient('/api'); diff --git a/src/lib/functions.ts b/src/lib/functions.ts index 7382f85..8339ea5 100644 --- a/src/lib/functions.ts +++ b/src/lib/functions.ts @@ -1,145 +1,68 @@ import { browser } from '$app/environment'; -import { goto } from '$app/navigation'; -import DOMPurify from 'dompurify'; import Swal, { type SweetAlertOptions } from 'sweetalert2'; -import { API } from './api'; -import { getContext } from 'svelte'; -import type { Writable } from 'svelte/store'; -import type { LoginData } from '$/types/types'; export const sleep = (ms: number) => { - return new Promise((resolve) => setTimeout(resolve, ms)); + return new Promise((resolve) => setTimeout(resolve, ms)); }; -export const SwalAlert = async (data: SweetAlertOptions) => { - if (!browser) { - return { - isConfirmed: false - }; - } - - return Swal.fire({ - toast: true, - position: 'top-end', - timer: 2000, - timerProgressBar: true, - showCancelButton: false, - showConfirmButton: false, - ...data - }); -}; - -export const createSimpleMarkDown = (data: string | undefined) => { - if (!data || !browser) return ''; - let result = data.replaceAll(/\[(.*)\]\((.*)\)/g, '$1'); - result = result.replaceAll(/\*\*(.*)\*\*/g, '$1'); - - DOMPurify.addHook('afterSanitizeAttributes', function(node) { - // set all elements owning target to target=_blank - if ('target' in node) { - node.setAttribute('target', '_blank'); - node.setAttribute('rel', 'noopener'); - } - }); - - return DOMPurify.sanitize(result); +export const SwalAlert = <$Type = unknown>(data: SweetAlertOptions) => { + if (!browser) { + return { + isConfirmed: false + } as const; + } + + return Swal.fire<$Type>({ + toast: true, + position: 'top-end', + timer: 2000, + timerProgressBar: true, + showCancelButton: false, + showConfirmButton: false, + ...data + }); }; -export const logout = async () => { - const result = await API.auth.logout(); - - if (result.status === false) { - SwalAlert({ - icon: 'error', - title: result.message - }); - return; - } - - getContext>('userState').set({ - logged: false - }); - - goto('/admin'); -}; - -export const getFiles = (files: (File | null)[]) => { - if (files.length == 0) { - SwalAlert({ - icon: 'error', - title: 'Nenahrál jsi žádný soubor' - }); - return []; - } - - const onlyFiles = files.filter((file) => file !== null) as File[]; - - if (onlyFiles.length == 0) { - SwalAlert({ - icon: 'error', - title: 'Nenahrál jsi žádný soubor' - }); - return []; - } - - return onlyFiles; +export const formatDate = (date: Date | string, time = true) => { + if (typeof date === 'string') { + date = new Date(date); + } + + const options: Intl.DateTimeFormatOptions = { + year: 'numeric', + month: '2-digit', + day: '2-digit', + ...(time && { + hour: '2-digit', + minute: '2-digit', + second: '2-digit' + }) + }; + + const locale = navigator.language || 'cs-CZ'; + + return new Intl.DateTimeFormat(locale, options).format(date); }; -export const getDate = (date: string | Date) => { - const d = new Date(date); +export const sToHHMM = (seconds: number) => { + const hours = Math.floor(seconds / 3600); + const minutes = Math.floor((seconds % 3600) / 60); - const day = d.getDate().toString().padStart(2, '0'); - const month = (d.getMonth() + 1).toString().padStart(2, '0'); - const year = d.getFullYear(); - - return `${year}-${month}-${day}`; + return `${hours.toString().padStart(2, '0')}h ${minutes.toString().padStart(2, '0')}m`; }; -export const formatDate = (date: string | Date, withTime = true) => { - const d = new Date(date); - - const day = d.getDate().toString().padStart(2, '0'); - const month = (d.getMonth() + 1).toString().padStart(2, '0'); - const year = d.getFullYear(); +export const resolveObject = (path: string, _path: object) => { + const parts = path.split('.'); - if (withTime) { - const hours = d.getHours().toString().padStart(2, '0'); - const minutes = d.getMinutes().toString().padStart(2, '0'); - const seconds = d.getSeconds().toString().padStart(2, '0'); - - return `${hours}:${minutes}:${seconds} ${day}.${month}.${year}`; + for (const part of parts) { + if (_path[part as keyof typeof _path] !== undefined) { + //eslint-disable-next-line @typescript-eslint/no-explicit-any + _path = _path[part as keyof typeof _path] as any; + } else { + // If the path does not exist, return the original error string + return path; } - return `${day}.${month}.${year}`; -}; - -export const uploadImage = async (file: File) => { - // eslint-disable-next-line no-async-promise-executor - return new Promise(async (resolve, reject) => { - const formData = new FormData(); - formData.append('file', file); - - const result = await API.upload.POST(formData); + } - if (!result.status) { - return reject(result.message); - } - - return resolve(result.data); - }); -}; - -export const extractDate = (date: Date, withSeconds = false) => { - const day = date.getDate().toString().padStart(2, '0'); - const month = (date.getMonth() + 1).toString().padStart(2, '0'); - const year = date.getFullYear(); - - const hours = date.getHours().toString().padStart(2, '0'); - const minutes = date.getMinutes().toString().padStart(2, '0'); - - if (withSeconds) { - const seconds = date.getSeconds().toString().padStart(2, '0'); - - return [`${year}-${month}-${day}`, `${hours}:${minutes}:${seconds}`]; - } - return [`${year}-${month}-${day}`, `${hours}:${minutes}`]; + return _path as unknown as string; }; diff --git a/src/lib/lang/_template.ts b/src/lib/lang/_template.ts new file mode 100644 index 0000000..8affa26 --- /dev/null +++ b/src/lib/lang/_template.ts @@ -0,0 +1,255 @@ +import { extensions } from '$/types/types'; +import { z } from 'zod'; + +// STRING +const _ = z.string(); +// OBJECT +const o = z.object; +// RICH TEXT +export const r = z.array( + z.string().or( + o({ + link: z.string(), + text: z.string(), + blank: z.boolean().optional() + }) + ) +); + +export const languagable = z.object({ + 1: _, + 2: _, + other: _ +}); + +export const _extensions = extensions.map((ext) => `.${ext}`).join(', '); + +export default o({ + default_desc: _, + yes: _, + no: _, + language: _, + navigation: o({ + home: _, + gallery: _, + admin: _, + login: _, + contact: _, + about: _ + }), + adminNavigation: o({ + home: _, + equipment: _, + articles: _ + }), + error: o({ + title: _, + message: _, + sub_message: _, + go_home: _, + invalid_type_number: _ + }), + main: o({ + age: _, + text: r + }), + admin: o({ + login: o({ + title: _, + username: _, + password: _, + submit: _ + }), + main: o({ + stats: _, + today: _, + week: _ + }), + equipment: o({ + actions: _, + types: o({ + title: _, + addTitle: _, + translateKey: _, + priority: _, + placeholder: _, + button: _, + success: _, + empty: _, + editSuccess: _, + deleteSuccess: _, + edit: o({ + title: _, + button: _ + }), + delete: o({ + question: _ + }) + }), + equipment: o({ + title: _, + addTitle: _, + type: _, + name: _, + link: _, + button: _, + success: _, + empty: _, + editSuccess: _, + deleteSuccess: _, + edit: o({ + title: _, + button: _ + }), + delete: o({ + question: _ + }) + }) + }), + article: o({ + title: _, + description: _, + create: _, + empty: _, + image: _, + articleTitle: _, + published: _, + lastEdit: _, + actions: _, + form: o({ + back: _, + editTitle: _, + createTitle: _, + details: o({ + title: _, + titleInput: _, + titlePlaceholder: _, + description: _, + descriptionPlaceholder: _, + content: _, + editContent: _, + previewContent: _, + contentPlaceholder: _ + }), + equipment: o({ + title: _, + select: _, + empty: _ + }), + images: o({ + title: _, + upload: _, + descriptionPlaceholder: _, + browse: _, + button: _, + alt: _, + empty: _, + noImage: _, + multiple: _, + confirmDelete: _ + }), + exposures: o({ + title: _, + date: _, + type: _, + count: _, + seconds: _, + button: _, + time: _, + total: _, + empty: _, + frames: _ + }), + cancel: _, + save: _, + create: _, + created: _, + updated: _ + }) + }) + }), + gallery: o({ + title: _, + description: _, + updated: _, + created: _, + readMore: _, + more: _, + back: _, + totalExposure: _, + article: _, + details: _, + equipment: _, + exposureSummary: _, + framesCount: languagable, + exposureDetails: _, + equipmentDetails: _, + images: _ + }), + contact: o({ + title: _, + visit: _, + send: _, + descriptions: o({ + github: _, + email: _, + linkedin: _, + instagram: _, + twitch: _, + discord: _, + discordServer: _ + }) + }), + about: o({ + title: _ + }), + errors: o({ + internal: _, + login: o({ + form: _, + username: _, + password: _ + }), + types: o({ + form: _, + empty: _, + priority: _ + }), + equipment: o({ + form: _, + url: _ + }), + upload: o({ + missing: _, + error: _, + invalidFile: _, + extension: _, + notFound: _ + }), + article: o({ + notFound: _, + noImages: _, + noTitle: _, + noDescrption: _, + noContent: _, + noAltText: _ + }) + }), + equipmentType: o({ + telescope: _, + camera: _, + mount: _, + filter: _, + barlow: _, + reducer: _, + guidescope: _, + phone: _, + focuser: _ + }), + frames: o({ + light: _, + dark: _, + flat: _, + bias: _ + }) +}); diff --git a/src/lib/lang/czech.ts b/src/lib/lang/czech.ts new file mode 100644 index 0000000..ad84cdc --- /dev/null +++ b/src/lib/lang/czech.ts @@ -0,0 +1,293 @@ +import type { z } from 'zod'; +import lang, { _extensions } from './_template'; + +export default lang.parse({ + default_desc: + 'Ahoj, jsem Patrik, student a programátor, který se ve volném čase věnuje astrofotografování. Věnuji se tvorbě webových stránek a aplikací ve frameworku SvelteKit. Nebráním se tvorbě jiných aplikací, například v NodeJS, nebo jiných frameworkcích, jako jsou Vue.js, nebo React.', + yes: 'Ano', + no: 'Ne', + language: 'Jazyk', + navigation: { + home: 'Domů', + gallery: 'Galerie', + admin: 'Administrace', + login: 'Přihlášení', + contact: 'Kontakt', + about: 'O mně' + }, + adminNavigation: { + home: 'Panel', + equipment: 'Vybavení', + articles: 'Články' + }, + error: { + title: 'Chyba', + message: 'Oops!', + sub_message: 'Tuto stránku neznáme :(', + go_home: 'Zpátky domů', + invalid_type_number: 'Zadejte platné číslo.' + }, + main: { + age: 'Věk', + text: [ + '🎓 Aktuálně studuji na ', + { + text: 'Vysoké škole báňské – Technické univerzitě Ostrava', + link: 'https://www.vsb.cz/' + }, + ', obor Informatika na ', + { + text: 'Fakultě elektrotechniky a informatiky', + link: 'https://www.fei.vsb.cz/' + }, + '.', + '%%SPACE%%', + ' 💻 Ve volném čase se nejvíce věnuji vývoji webových aplikací pomocí ', + { + text: 'SvelteKit', + link: 'https://kit.svelte.dev/' + }, + ', ', + { + text: 'Tailwind CSS', + link: 'https://tailwindcss.com/' + }, + ' a ', + { + text: 'TypeScriptu', + link: 'https://www.typescriptlang.org/' + }, + '. Mám zkušenosti jak s frontendem, tak backendem a občas zabrousím i do designu.', + '%%SPACE%%', + ' 🧠 Kromě SvelteKitu zvládám i práci s jinými frameworky, jako je ', + { + text: 'Vue.js', + link: 'https://vuejs.org/' + }, + ' nebo ', + { + text: 'React', + link: 'https://react.dev/' + }, + ', takže se umím přizpůsobit různým technologiím.', + '%%SPACE%%', + ' 🛠️ V Node.js vytvářím i různé aplikace jako jsou Discord boti, Twitch boti nebo jiné skripty, které zjednodušují život.', + '%%SPACE%%', + ' 🌌 Mimo programování se věnuji také astrofotografii – moje snímky najdeš v ', + { + text: 'mé galerii', + link: '/cs/gallery', + blank: false + }, + '!' + ] + }, + admin: { + login: { + title: 'Přihlášení', + username: 'Jméno', + password: 'Heslo', + submit: 'Přihlásit se' + }, + main: { + stats: 'Statistiky', + today: 'Dnes', + week: 'Tento týden' + }, + equipment: { + actions: 'Akce', + types: { + title: 'Typy vybavení', + addTitle: 'Přidání nového typu', + translateKey: 'Překlad', + priority: 'Priorita', + placeholder: 'Název typu vybavení', + button: 'Přidat', + success: 'Nový typ vybavení byl přidán!', + empty: 'Žádné typy vybavení nebyly přidány.', + editSuccess: 'Název typu vybavení byl úspěšně upraven!', + deleteSuccess: 'Typ vybavení byl úspěšně smazán!', + edit: { + title: 'Úprava typu vybavení s id %1', + button: 'Upravit' + }, + delete: { + question: 'Opravdu chceš smazat tento typ?' + } + }, + equipment: { + title: 'Vybavení', + addTitle: 'Přidání nového vybavení', + type: 'Typ', + name: 'Název', + link: 'Odkaz', + button: 'Přidat', + success: 'Nové vybavení bylo přidáno!', + empty: 'Žádné vybavení nebylo přidáno.', + editSuccess: 'Vybavení bylo úspěšně upraveno!', + deleteSuccess: 'Vybavení bylo úspěšně smazáno!', + edit: { + title: 'Úprava vybavení s id %1', + button: 'Upravit' + }, + delete: { + question: 'Opravdu chceš smazat toto vybavení?' + } + } + }, + article: { + title: 'Články', + create: 'Vytvořit článek', + empty: 'Žádné články nebyly vytvořeny.', + articleTitle: 'Název článku', + description: 'Popisek', + image: 'Obrázek', + lastEdit: 'Poslední úprava', + published: 'Publikováno', + actions: 'Akce', + form: { + back: 'Zpátky k článkům', + editTitle: 'Upravit článek', + createTitle: 'Vytvořit článek', + details: { + title: 'Detaily článku', + titleInput: 'Název článku', + titlePlaceholder: 'Zadej název článku', + description: 'Popis článku', + descriptionPlaceholder: 'Zadej krátký popis článku', + content: 'Obsah článku (Markdown)', + editContent: 'Úprava', + previewContent: 'Náhled', + contentPlaceholder: 'Zadej obsah článku v markdownu...' + }, + equipment: { + title: 'Vybavení', + select: 'Vyber vybavení', + empty: 'Nevybral jsi žádné vybavení.' + }, + images: { + title: 'Obrázky', + upload: 'Nahrát obrázek', + descriptionPlaceholder: 'Zadej popis obrázku', + browse: 'Procházet', + button: 'Přidat obrázek', + alt: 'Zadej prosím popisek obrázku', + empty: 'Zatím nebyl přidán žádný obrázek.', + noImage: 'Vyber prosím obrázek', + multiple: 'Vyber prosím pouze jeden obrázek', + confirmDelete: 'Opravdu chceš smazat tento obrázek?' + }, + exposures: { + title: 'Expozice', + date: 'Datum', + type: 'Typ', + count: 'Počet', + seconds: 'Sekundy', + button: 'Přidat expozici', + time: 'Čas (s)', + total: 'Celkový čas (s)', + empty: 'Zatím jsi nepřidal žádnou expozici.', + frames: 'Snímky' + }, + cancel: 'Zrušit', + save: 'Uložit článek', + create: 'Vytvořit článek', + created: 'Článek byl úspěšně vytvořen!', + updated: 'Článek byl úspěšně upraven!' + } + } + }, + gallery: { + title: 'Astro-Galerie', + description: + 'Zde nalezneš astrofotografie pořízené mnou, společně s popisy a detaily o expozicích.', + updated: 'Aktualizováno', + created: 'Vytvořeno', + readMore: 'Čti dále', + more: 'více', + back: 'Zpět na galerii', + totalExposure: 'Celkový čas expozice', + article: 'Článek', + details: 'Technické detaily', + equipment: 'Použité vybavení', + exposureSummary: 'Souhrn expozic', + framesCount: { + '1': 'snímek', + '2': 'snímky', + other: 'snímků' + }, + exposureDetails: 'Detaily expozic', + equipmentDetails: 'Detaily vybavení', + images: 'Obrázky' + }, + contact: { + title: 'Spoj se se mnou', + visit: 'Navštívit', + send: 'Napsat', + descriptions: { + github: 'Koukni na můj GitHub, kde najdeš repa s mými projekty', + email: 'Napiš mi email, pokud máš nějaké dotazy, nebo máš zájem o spolupráci', + linkedin: + 'Spoj se se mnout na LinkedInu, zde najdeš mé profesní zkušenosti a dovednosti', + instagram: + 'Sleduj mě na Instagramu, kde sdílím své astrofotografie a další zajímavosti', + twitch: 'Sleduj mě na Twitch, kde streamuji programování, nebo hraní her', + discord: + 'Přidej si mě na Discordu, zde jsem nejvíce aktivní a nejrychleji ti odpovím', + discordServer: + 'Pokud tě zajímá dění okolo mě, určitě se připoj na můj Discord server' + } + }, + about: { + title: 'Zde najdeš něco málo o mně' + }, + errors: { + internal: 'Něco se nepovedlo, zkus to prosím zachvíli znova.', + login: { + form: 'Vyplň prosím všechny údaje.', + username: 'Špatné uživatelské jméno.', + password: 'Špatné heslo.' + }, + types: { + form: 'Vyplň prosím název typu vybavení.', + empty: 'Překladový klíč nesmí být prázdný.', + priority: 'Priorita musí být číslo >=0.' + }, + equipment: { + form: 'Vyplň prosím všechny údaje.', + url: 'Zadej platný odkaz.' + }, + upload: { + missing: 'Vyber prosím soubor k nahrání.', + error: 'Nepodařilo se nahrát soubor, zkus to prosím znovu.', + invalidFile: 'Nahrej prosím platný soubor', + extension: 'Nahrej prosím platný obrázek s příponou ' + _extensions, + notFound: 'Soubor s tímto jménem neexistuje.' + }, + article: { + notFound: 'Článek s tímto id neexistuje.', + noImages: 'Článek musí obsahovat alespoň jeden obrázek.', + noTitle: 'Musíš zadat název článku.', + noDescrption: 'Musíš zadat popisek článku.', + noContent: 'Musíš zadat obsah článku.', + noAltText: 'Musíš zadat popisek obrázku.' + } + }, + equipmentType: { + camera: 'Kamera', + mount: 'Montáž', + telescope: 'Dalekohled', + filter: 'Filtr', + barlow: 'Barlow', + reducer: 'Reduktor', + guidescope: 'Guidescope', + phone: 'Telefon', + focuser: 'Zaostřovač' + }, + frames: { + light: 'Light', + dark: 'Dark', + flat: 'Flat', + bias: 'Bias' + } +} satisfies z.infer); diff --git a/src/lib/lang/english.ts b/src/lib/lang/english.ts new file mode 100644 index 0000000..f26488b --- /dev/null +++ b/src/lib/lang/english.ts @@ -0,0 +1,293 @@ +import type { z } from 'zod'; +import lang, { _extensions } from './_template'; + +export default lang.parse({ + default_desc: + 'Hello, I am Patrik, a student and programmer who enjoys astrophotography in my free time. I create websites and applications using the SvelteKit framework. I am open to creating other applications, for example in NodeJS or other frameworks like Vue.js or React.', + yes: 'Yes', + no: 'No', + language: 'Language', + navigation: { + home: 'Home', + gallery: 'Gallery', + admin: 'Administration', + login: 'Login', + contact: 'Contact', + about: 'About Me' + }, + adminNavigation: { + home: 'Dashboard', + equipment: 'Equipment', + articles: 'Articles' + }, + error: { + title: 'Error', + message: 'Oops!', + sub_message: "We don't know this page :(", + go_home: 'Back home', + invalid_type_number: 'Please enter a valid number.' + }, + main: { + age: 'Age', + text: [ + '🎓 I am currently studying at ', + { + text: 'VŠB – Technical University of Ostrava', + link: 'https://www.vsb.cz/' + }, + ', majoring in Computer Science at the ', + { + text: 'Faculty of Electrical Engineering and Computer Science', + link: 'https://www.fei.vsb.cz/' + }, + '.', + '%%SPACE%%', + ' 💻 In my free time, I mostly focus on developing web applications using ', + { + text: 'SvelteKit', + link: 'https://kit.svelte.dev/' + }, + ', ', + { + text: 'Tailwind CSS', + link: 'https://tailwindcss.com/' + }, + ', and ', + { + text: 'TypeScript', + link: 'https://www.typescriptlang.org/' + }, + '. I have experience with both frontend and backend, and I occasionally dive into design as well.', + '%%SPACE%%', + ' 🧠 Besides SvelteKit, I also work with other frameworks like ', + { + text: 'Vue.js', + link: 'https://vuejs.org/' + }, + ' and ', + { + text: 'React', + link: 'https://react.dev/' + }, + ', so I can adapt to different technologies.', + '%%SPACE%%', + ' 🛠️ In Node.js, I also develop various applications such as Discord bots, Twitch bots, and other scripts that simplify life.', + '%%SPACE%%', + ' 🌌 Outside of programming, I enjoy astrophotography — you can find my images in ', + { + text: 'my gallery', + link: '/en/gallery', + blank: false + }, + '!' + ] + }, + admin: { + login: { + title: 'Login', + username: 'Username', + password: 'Password', + submit: 'Login' + }, + main: { + stats: 'Statistics', + today: 'Today', + week: 'This Week' + }, + equipment: { + actions: 'Actions', + types: { + title: 'Equipment Types', + addTitle: 'Editing equipment type', + translateKey: 'Translate', + priority: 'Priority', + placeholder: 'Type name', + button: 'Add Type', + success: 'New type was successfully added!', + empty: 'No equipment types have been added yet.', + editSuccess: 'Equpment type name was successfully edited!', + deleteSuccess: 'Equipment type was successfully deleted!', + edit: { + title: 'Editing equipment type id %1', + button: 'Edit' + }, + delete: { + question: 'Are you sure you want to delete this type?' + } + }, + equipment: { + title: 'Equipment', + addTitle: 'Editing equipment', + type: 'Type', + name: 'Name', + link: 'Link', + button: 'Add Equipment', + success: 'New equipment was successfully added!', + empty: 'No equipment has been added yet.', + editSuccess: 'Equipment was successfully edited!', + deleteSuccess: 'Equipment was successfully deleted!', + edit: { + title: 'Editing equipment id %1', + button: 'Edit' + }, + delete: { + question: 'Are you sure you want to delete this equipment?' + } + } + }, + article: { + title: 'Articles', + create: 'Create new article', + empty: 'No articles have been created yet.', + image: 'Image', + actions: 'Actions', + articleTitle: 'Article title', + description: 'Description', + published: 'Published', + lastEdit: 'Last edit', + form: { + back: 'Back to articles', + editTitle: 'Edit article', + createTitle: 'Create article', + details: { + title: 'Article details', + titleInput: 'Article title', + titlePlaceholder: 'Enter article title', + description: 'Article description', + descriptionPlaceholder: 'Enter article description', + content: 'Article content (Markdown)', + editContent: 'Edit', + previewContent: 'Preview', + contentPlaceholder: 'Enter article content in markdown...' + }, + equipment: { + title: 'Equipment', + select: 'Select equipment', + empty: 'You have not selected any equipment.' + }, + images: { + title: 'Images', + upload: 'Upload image', + descriptionPlaceholder: 'Enter image description', + browse: 'Browse', + button: 'Add image', + empty: 'No images have been added yet.', + alt: 'Please enter image description', + noImage: 'Please select an image to upload', + multiple: 'Please select only one image', + confirmDelete: 'Are you sure you want to delete this image?' + }, + exposures: { + title: 'Exposures', + date: 'Date', + type: 'Type', + count: 'Count', + seconds: 'Seconds', + button: 'Add exposure', + time: 'Time (s)', + total: 'Total (s)', + empty: 'No exposures have been added yet.', + frames: 'Frames' + }, + cancel: 'Cancel', + save: 'Save article', + create: 'Create article', + created: 'Article was successfully created!', + updated: 'Article was successfully updated!' + } + } + }, + gallery: { + title: 'Astro-Gallery', + description: + 'Here you can find astrophotographs taken by me, along with descriptions and details about the exposures.', + updated: 'Updated', + created: 'Created', + readMore: 'Read more', + more: 'more', + back: 'Back to gallery', + totalExposure: 'Total exposure', + article: 'Article', + details: 'Technical details', + equipment: 'Equipment', + exposureSummary: 'Exposure summary', + framesCount: { + '1': 'frames', + '2': 'frames', + other: 'frames' + }, + exposureDetails: 'Exposure details', + equipmentDetails: 'Equipment details', + images: 'Images' + }, + contact: { + title: 'Get in Touch', + visit: 'Visit', + send: 'Send', + descriptions: { + github: 'Check out my GitHub, where you can find repositories with my projects', + email: 'Email me if you have any questions or are interested in collaboration', + linkedin: + 'Connect with me on LinkedIn, where you can find my professional experience and skills', + instagram: + 'Follow me on Instagram, where I share my astrophotography and other interesting things', + twitch: 'Watch me on Twitch, where I stream programming or gaming', + discord: + 'Add me on Discord, I am most active there and will respond to you the fastest', + discordServer: + 'If you are interested in what I am up to, definitely join my Discord server' + } + }, + about: { + title: 'Here you will find something about me' + }, + errors: { + internal: 'Internal Server Error, please try again later.', + login: { + form: 'Please fill in the form', + username: 'Invalid username', + password: 'Invalid password' + }, + types: { + form: 'Please fill eqipment type name.', + empty: 'Translation key cannot be empty.', + priority: 'Priority must be a number >=0.' + }, + equipment: { + form: 'Please fill in the form', + url: 'Please enter a valid URL' + }, + upload: { + missing: 'Please select a file to upload.', + error: 'Image upload failed, please try again later.', + invalidFile: 'Please enter a valid file', + extension: 'Please enter an image with valid extension: ' + _extensions, + notFound: 'File with this name does not exist.' + }, + article: { + notFound: 'Article with this ID does not exist.', + noImages: 'Article must contain at least one image.', + noTitle: 'You need to enter article title.', + noDescrption: 'You need to enter article description.', + noContent: 'You need to enter article content.', + noAltText: 'You need to enter image description.' + } + }, + equipmentType: { + camera: 'Camera', + telescope: 'Telescope', + mount: 'Mount', + filter: 'Filter', + barlow: 'Barlow', + reducer: 'Reducer', + guidescope: 'Guidescope', + phone: 'Phone', + focuser: 'Focuser' + }, + frames: { + light: 'Light', + dark: 'Dark', + flat: 'Flat', + bias: 'Bias' + } +} satisfies z.infer); diff --git a/src/lib/lang/index.ts b/src/lib/lang/index.ts new file mode 100644 index 0000000..5473693 --- /dev/null +++ b/src/lib/lang/index.ts @@ -0,0 +1,166 @@ +import type { Path } from '$/types/types'; +import type { z } from 'zod'; +import { resolveObject } from '../functions'; +import type { GatheredTranslationsAll } from '../server/functions'; +import template, { languagable } from './_template'; +import czech from './czech'; +import english from './english'; + +export const languages = { + cs: { + t: czech, + flag: '🇨🇿', + name: 'Čeština' + }, + en: { + t: english, + flag: '🇺🇸', + name: 'English' + } +}; + +export const getPath = (path: string, langs: string[]) => { + const parts = path.split('/').filter(Boolean); + if (langs.includes(parts[0])) { + parts.shift(); + } + return `/${parts.join('/')}`; +}; + +type Error = z.infer['errors']; + +export type ErrorPath = Path; +export type LanguagePath = Path>; + +export const compareErrors = (a: string, b: ErrorPath) => { + return a === b; +}; + +export const resolveError = (error: string, lang: z.infer) => { + return resolveObject(error, lang.errors); +}; + +export const resolveTranslation = ( + translation: string, + lang: z.infer +) => { + let path = lang; + const parts = translation.split('.'); + + for (const part of parts) { + if (path[part as keyof typeof path]) { + //eslint-disable-next-line @typescript-eslint/no-explicit-any + path = path[part as keyof typeof path] as any; + } else { + // If the path does not exist, return the original translation string + return translation; + } + } + + return path as unknown as string; +}; + +export const replacePlaceholders = (text: string, ...placeholders: string[]) => { + let i = 1; + for (const placeholder of placeholders) { + text = text.replace(`%${i}`, placeholder); + i++; + } + return text; +}; + +export const resolveLanguagable = ( + translate: z.infer, + number: number +) => { + if (number === 1) { + return translate[1]; + } + if (number >= 2 && number <= 4) { + return translate[2]; + } + return translate.other; +}; + +type TransformIntoLanguagableReturn<$Object, $Keys extends keyof $Object> = Record< + keyof typeof languages, + Pick<$Object, $Keys> +> & + Omit<$Object, $Keys>; +//Resolve arrays and sub objects +export const transformIntoLanguagable = < + $Object extends object, + $Keys extends keyof $Object +>( + object: $Object, + gathered: GatheredTranslationsAll, + languageKeys: $Keys[] +): TransformIntoLanguagableReturn<$Object, $Keys> => { + //eslint-disable-next-line @typescript-eslint/no-explicit-any + const result = {} as any; + + //Fill the non language keys + for (const key in object) { + //@ts-expect-error Here we put string into .includes, which is fine + if (languageKeys.includes(key)) continue; + + result[key] = object[key]; + } + + for (const _lang in languages) { + const lang = _lang as keyof typeof languages; + + const baseObject = structuredClone(object); + + //delete non-language keys from base object + for (const key in baseObject) { + //@ts-expect-error Here we put string into .includes, which is fine + if (!languageKeys.includes(key)) { + delete baseObject[key]; + } + } + + const currentLang = gathered[lang]; + + const translateObject = (obj: Record) => { + for (const key in obj) { + const value = obj[key]; + if ( + value === null || + value === undefined || + (typeof value !== 'object' && typeof value !== 'string') + ) { + continue; + } + + if (Array.isArray(value)) { + obj[key] = value.map(translateObject); + continue; + } + + if (typeof value === 'object') { + translateObject(obj[key] as Record); + continue; + } + + const translated = currentLang[value]; + + if (!translated) { + // If translation is not found, keep the original value + // This allows non-translatable fields (like UUIDs for images) to persist + obj[key] = value; + continue; + } + obj[key] = translated; + } + + return obj; + }; + + translateObject(baseObject as Record); + + result[lang] = baseObject as $Object; + } + + return result; +}; diff --git a/src/lib/server/_routes/article.ts b/src/lib/server/_routes/article.ts new file mode 100644 index 0000000..8ef3b2e --- /dev/null +++ b/src/lib/server/_routes/article.ts @@ -0,0 +1,396 @@ +import { languages, type ErrorPath } from '$/lib/lang'; +import { articleSchema as _articleSchema } from '$/types/schemes'; +import type { ActionsResponse, Response } from '$/types/types'; +import { FILE_FOLDER } from '$env/static/private'; +import { AnyFormDataInput, type ErrorApiResponse } from '@patrick115/sveltekitapi'; +import { fail } from '@sveltejs/kit'; +import fs from 'node:fs/promises'; +import Path from 'node:path'; +import { v4 } from 'uuid'; +import { z } from 'zod'; +import { loggedProcedure } from '../api'; +import { insertTranslations, parseFormData } from '../functions'; +import { conn } from '../variables'; + +const articleSchema = _articleSchema('cs'); + +export default [ + loggedProcedure.POST.input(AnyFormDataInput).query(async ({ input: _input }) => { + const parsed = parseFormData(_input, articleSchema); + const input = parsed.cs; + + const trx = await conn.startTransaction().execute(); + + try { + const translations = await insertTranslations(trx, parsed, [ + 'title', + 'description', + 'content_md' + ]); + + const uuid = v4(); + + await trx + .insertInto('article') + .values({ + id: uuid, + title: translations.title, + description: translations.description, + content_md: translations.content_md + }) + .execute(); + + //equipment + if (input.equipment.length > 0) { + await trx + .insertInto('article_equipment') + .values( + input.equipment.map((eq) => ({ + article_id: uuid, + equipment_id: eq + })) + ) + .execute(); + } + + //images + if (input.images.length === 0) { + return fail(400, { + status: false, + message: 'article.noImages' satisfies ErrorPath + } satisfies ActionsResponse); + } + + const languagesKeys = Object.keys(languages); + const imagesToInsert = []; + + for (let i = 0; i < input.images.length; i++) { + const image = input.images[i]; + + const transObject: Record = {}; + for (const lang of languagesKeys) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const langData = parsed[lang] as any; + transObject[lang] = { alt_text: langData?.images?.[i]?.alt_text ?? '' }; + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const transResult = await insertTranslations(trx, transObject as any, [ + 'alt_text' + ]); + + imagesToInsert.push({ + article_id: uuid, + name: image.name, + alt_text: transResult.alt_text + }); + } + + await trx.insertInto('gallery_image').values(imagesToInsert).execute(); + + //exposures + if (input.exposures.length > 0) { + await trx + .insertInto('exposure') + .values( + input.exposures.map((exposure) => ({ + ...exposure, + article_id: uuid + })) + ) + .execute(); + } + + await trx.commit().execute(); + return { + status: true + } satisfies Response; + } catch (err) { + console.error(err); + await trx.rollback().execute(); + + return fail(500, { + status: false, + message: 'internal' satisfies ErrorPath + } satisfies ActionsResponse); + } + }), + loggedProcedure.PUT.input(AnyFormDataInput).query(async ({ input: _input }) => { + const parsed = parseFormData(_input, articleSchema); + const input = parsed.cs; + + if (!input.id) { + return fail(400, { + status: false, + message: 'article.notFound' satisfies ErrorPath + } satisfies ActionsResponse); + } + + const originalData = await conn + .selectFrom('article') + .selectAll() + .where('id', '=', input.id) + .executeTakeFirst(); + if (!originalData) { + return fail(404, { + status: false, + message: 'article.notFound' satisfies ErrorPath + } satisfies ActionsResponse); + } + + const trx = await conn.startTransaction().execute(); + + try { + let someChanged = false; + const languagesKeys = Object.keys(languages); + + // Update Translations (Title, Description, Content) + const fields = ['title', 'description', 'content_md'] as const; + for (const field of fields) { + const uuid = originalData[field]; + for (const lang of languagesKeys) { + const newText = parsed[lang]?.[field]; + if (newText) { + await trx + .updateTable('translations') + .set({ text: newText }) + .where('key', '=', uuid) + .where('lang', '=', lang) + .execute(); + someChanged = true; + } + } + } + + //equipment + const originalEquipment = await conn + .selectFrom('article_equipment') + .select(['equipment_id']) + .where('article_id', '=', input.id) + .execute(); + const ids = originalEquipment.map((eq) => eq.equipment_id); + const toAdd = input.equipment.filter((eq) => !ids.includes(eq)); + const toRemove = originalEquipment.filter( + (eq) => !input.equipment.includes(eq.equipment_id) + ); + + if (toAdd.length > 0) { + someChanged = true; + await trx + .insertInto('article_equipment') + .values( + toAdd.map((eq) => ({ + article_id: input.id!, + equipment_id: eq + })) + ) + .execute(); + } + + if (toRemove.length > 0) { + someChanged = true; + await trx + .deleteFrom('article_equipment') + .where('article_id', '=', input.id!) + .where( + 'equipment_id', + 'in', + toRemove.map((eq) => eq.equipment_id) + ) + .execute(); + } + + //images + if (input.images.length === 0) { + return fail(400, { + status: false, + message: 'article.noImages' satisfies ErrorPath + } satisfies ActionsResponse); + } + + const originalImages = await conn + .selectFrom('gallery_image') + .selectAll() + .where('article_id', '=', input.id) + .execute(); + + const inputImageNames = input.images.map((img) => img.name); + + // Removed Images + const imagesToRemove = originalImages.filter( + (img) => !inputImageNames.includes(img.name) + ); + if (imagesToRemove.length > 0) { + someChanged = true; + // Delete images + await trx + .deleteFrom('gallery_image') + .where( + 'id', + 'in', + imagesToRemove.map((img) => img.id) + ) + .execute(); + + // Cleanup translations + await trx + .deleteFrom('translations') + .where( + 'key', + 'in', + imagesToRemove.map((img) => img.alt_text) + ) + .execute(); + } + + // Process Input Images + for (let i = 0; i < input.images.length; i++) { + const img = input.images[i]; + const existing = originalImages.find((orig) => orig.name === img.name); + + if (existing) { + // Update Translations + const uuid = existing.alt_text; + for (const lang of languagesKeys) { + const newText = parsed[lang]?.images?.[i]?.alt_text ?? ''; + + const result = await trx + .updateTable('translations') + .set({ text: newText }) + .where('key', '=', uuid) + .where('lang', '=', lang) + .executeTakeFirst(); + + if (Number(result.numUpdatedRows) === 0) { + await trx + .insertInto('translations') + .values({ key: uuid, lang: lang, text: newText }) + .execute(); + } + } + } else { + someChanged = true; + // New Image -> Insert + const transObject: Record = {}; + for (const lang of languagesKeys) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const langData = parsed[lang] as any; + transObject[lang] = { alt_text: langData?.images?.[i]?.alt_text ?? '' }; + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const transResult = await insertTranslations(trx, transObject as any, [ + 'alt_text' + ]); + + await trx + .insertInto('gallery_image') + .values({ + article_id: input.id!, + name: img.name, + alt_text: transResult.alt_text + }) + .execute(); + } + } + + //exposures + const originalExposures = await conn + .selectFrom('exposure') + .select(['id']) + .where('article_id', '=', input.id) + .execute(); + const exposureIds = originalExposures.map((exp) => exp.id); + const toAddExposures = input.exposures.filter( + (exp) => exp.id === undefined || !exposureIds.includes(exp.id) + ); + const toRemoveExposures = originalExposures.filter( + (exp) => !input.exposures.some((_exp) => _exp.id === exp.id) + ); + + if (toAddExposures.length > 0) { + await trx + .insertInto('exposure') + .values( + toAddExposures.map((exposure) => ({ + ...exposure, + article_id: input.id! + })) + ) + .execute(); + } + + if (toRemoveExposures.length > 0) { + await trx + .deleteFrom('exposure') + .where('article_id', '=', input.id!) + .where( + 'id', + 'in', + toRemoveExposures.map((exp) => exp.id) + ) + .execute(); + } + + if (someChanged) { + await trx + .updateTable('article') + .set({ + updated_at: new Date() + }) + .where('id', '=', input.id!) + .execute(); + } + + await trx.commit().execute(); + return { + status: true + } satisfies Response; + } catch (err) { + console.error(err); + await trx.rollback().execute(); + + return fail(500, { + status: false, + message: 'internal' satisfies ErrorPath + } satisfies ActionsResponse); + } + }), + loggedProcedure.DELETE.input(z.string()).query(async ({ input }) => { + const trx = await conn.startTransaction().execute(); + try { + //equipment + await trx.deleteFrom('article_equipment').where('article_id', '=', input).execute(); + //exposures + await trx.deleteFrom('exposure').where('article_id', '=', input).execute(); + //images + const images = await conn + .selectFrom('gallery_image') + .select(['name']) + .where('article_id', '=', input) + .execute(); + await trx.deleteFrom('gallery_image').where('article_id', '=', input).execute(); + //remove images + const imgPaths = images.map((img) => Path.join(FILE_FOLDER, img.name)); + await Promise.all(imgPaths.map(async (path) => fs.unlink(path).catch(() => {}))); + + await trx.deleteFrom('article').where('id', '=', input).execute(); + + await trx.commit().execute(); + + return { + status: true + } satisfies Response; + } catch (err) { + console.error(err); + + await trx.rollback().execute(); + + return { + status: false, + code: 500, + message: 'internal' satisfies ErrorPath + } satisfies ErrorApiResponse; + } + }) +]; diff --git a/src/lib/server/_routes/equipment.ts b/src/lib/server/_routes/equipment.ts new file mode 100644 index 0000000..4513e2a --- /dev/null +++ b/src/lib/server/_routes/equipment.ts @@ -0,0 +1,146 @@ +import type { ErrorPath } from '$/lib/lang'; +import type { ActionsResponse, Response } from '$/types/types'; +import { createFormDataInput, type ErrorApiResponse } from '@patrick115/sveltekitapi'; +import { fail } from '@sveltejs/kit'; +import { z } from 'zod'; +import { loggedProcedure } from '../api'; +import { conn } from '../variables'; + +const equipmentSchema = z.object({ + name: z.string().min(1, 'equipment.form.name'), + type: z.string().min(1, 'equipment.form.type'), + link: z.url('equipment.form.link') +}); + +export default [ + loggedProcedure.POST.input(createFormDataInput(equipmentSchema)).query( + async ({ input }) => { + const name = input.get('name') as string; + const type = input.get('type') as string; + const link = input.get('link') as string; + + if (!name || !type || !link || isNaN(Number(type))) { + return fail(400, { + status: false, + message: 'equipment.form' satisfies ErrorPath + } satisfies ActionsResponse); + } + + try { + const equipmentType = await conn + .selectFrom('equipment_type') + .select('id') + .where('id', '=', Number(type)) + .executeTakeFirst(); + if (!equipmentType) { + return fail(400, { + status: false, + message: 'equipment.form' satisfies ErrorPath + } satisfies ActionsResponse); + } + + await conn + .insertInto('equipment') + .values({ + name, + type_id: Number(type), + link + }) + .executeTakeFirst(); + + return { + status: true + } satisfies Response; + } catch (err) { + console.error(err); + return fail(500, { + status: false, + message: 'internal' satisfies ErrorPath + } satisfies ActionsResponse); + } + } + ), + loggedProcedure.PATCH.input(createFormDataInput(equipmentSchema.partial())).query( + async ({ input }) => { + const id = input.get('id') as string | null; + const name = input.get('name') as string | null; + const type = input.get('type') as string | null; + const link = input.get('link') as string | null; + + if (!id || isNaN(Number(id))) { + return fail(401, { + status: false, + message: 'internal' satisfies ErrorPath + } satisfies ActionsResponse); + } + + if (!name || !type || !link || isNaN(Number(type))) { + return fail(401, { + status: false, + message: 'equipment.form' satisfies ErrorPath + } satisfies ActionsResponse); + } + + try { + const equipmentType = await conn + .selectFrom('equipment_type') + .select('id') + .where('id', '=', Number(type)) + .executeTakeFirst(); + if (!equipmentType) { + return fail(400, { + status: false, + message: 'equipment.form' satisfies ErrorPath + } satisfies ActionsResponse); + } + + await conn + .updateTable('equipment') + .set({ + name, + type_id: Number(type), + link + }) + .where('id', '=', Number(id)) + .executeTakeFirst(); + + return { + status: true + } satisfies Response; + } catch (err) { + console.error(err); + return fail(500, { + status: false, + message: 'internal' satisfies ErrorPath + } satisfies ActionsResponse); + } + } + ), + loggedProcedure.DELETE.input(z.number()).query(async ({ input }) => { + if (!input || isNaN(Number(input))) { + return { + status: false, + code: 400, + message: 'internal' satisfies ErrorPath + } satisfies ErrorApiResponse; + } + + try { + await conn + .deleteFrom('equipment') + .where('id', '=', Number(input)) + .executeTakeFirst(); + + return { + status: true + } satisfies Response; + } catch (err) { + console.error(err); + return { + status: false, + code: 500, + message: 'internal' satisfies ErrorPath + } satisfies ErrorApiResponse; + } + }) +]; diff --git a/src/lib/server/_routes/login.ts b/src/lib/server/_routes/login.ts new file mode 100644 index 0000000..214ed41 --- /dev/null +++ b/src/lib/server/_routes/login.ts @@ -0,0 +1,58 @@ +import { FormDataInput } from '@patrick115/sveltekitapi'; +import { procedure } from '../api'; +import type { ErrorPath } from '$/lib/lang'; +import type { ActionsResponse, Response } from '$/types/types'; +import { fail } from '@sveltejs/kit'; +import { conn, jwt } from '../variables'; +import bcrypt from 'bcrypt'; +import { COOKIE_EXPIRE } from '$env/static/private'; + +export default procedure.POST.input(FormDataInput).query( + async ({ input, ev: { cookies } }) => { + const username = input.get('username') as string | null; + const password = input.get('password') as string | null; + + if (!username || !password) { + return fail(401, { + status: false, + message: 'login.form' satisfies ErrorPath + } satisfies ActionsResponse); + } + + const data = await conn + .selectFrom('account') + .selectAll() + .where('username', '=', username) + .executeTakeFirst(); + + if (!data) { + return fail(401, { + status: false, + message: 'login.username' satisfies ErrorPath + } satisfies ActionsResponse); + } + + if (!bcrypt.compareSync(password, data.password)) { + return fail(401, { + status: false, + message: 'login.password' satisfies ErrorPath + } satisfies ActionsResponse); + } + + const userData = { + ...data, + password: undefined + }; + + const session = jwt.setCookie(userData); + + cookies.set('session', session, { + path: '/', + maxAge: parseInt(COOKIE_EXPIRE) + }); + + return { + status: true + } satisfies Response; + } +); diff --git a/src/lib/server/_routes/types.ts b/src/lib/server/_routes/types.ts new file mode 100644 index 0000000..d77c880 --- /dev/null +++ b/src/lib/server/_routes/types.ts @@ -0,0 +1,120 @@ +import { loggedProcedure } from '../api'; +import { conn } from '../variables'; +import type { ActionsResponse, Response } from '$/types/types'; +import { FormDataInput, type ErrorApiResponse } from '@patrick115/sveltekitapi'; +import { fail } from '@sveltejs/kit'; +import type { ErrorPath } from '$/lib/lang'; +import { z } from 'zod'; + +export default [ + loggedProcedure.POST.input(FormDataInput).query(async ({ input }) => { + const lang_key = input.get('lang_key') as string | null; + const priority = input.get('priority') as string | null; + + if (!lang_key) { + return fail(401, { + status: false, + message: 'types.form' satisfies ErrorPath + } satisfies ActionsResponse); + } + if (!priority || isNaN(Number(priority))) { + return fail(401, { + status: false, + message: 'types.priority' satisfies ErrorPath + } satisfies ActionsResponse); + } + + try { + await conn + .insertInto('equipment_type') + .values({ + lang_key, + priority: Number(priority) + }) + .execute(); + + return { + status: true + } satisfies Response; + } catch (err) { + console.error(err); + return fail(500, { + status: false, + message: 'internal' satisfies ErrorPath + } satisfies ActionsResponse); + } + }), + loggedProcedure.PATCH.input(FormDataInput).query(async ({ input }) => { + const id = input.get('id') as string | null; + const lang_key = input.get('lang_key') as string | null; + const priority = input.get('priority') as string | null; + + if (!id || isNaN(Number(id))) { + return fail(401, { + status: false, + message: 'internal' satisfies ErrorPath + } satisfies ActionsResponse); + } + + if (!lang_key || lang_key.trim().length === 0) { + return fail(401, { + status: false, + message: 'types.empty' satisfies ErrorPath + } satisfies ActionsResponse); + } + + if (!priority || isNaN(Number(priority))) { + return fail(401, { + status: false, + message: 'types.priority' satisfies ErrorPath + } satisfies ActionsResponse); + } + + try { + await conn + .updateTable('equipment_type') + .set({ + lang_key, + priority: Number(priority) + }) + .where('id', '=', Number(id)) + .execute(); + + return { + status: true + } satisfies Response; + } catch (err) { + console.error(err); + return fail(500, { + status: false, + message: 'internal' satisfies ErrorPath + } satisfies ActionsResponse); + } + }), + loggedProcedure.DELETE.input(z.number()).query(async ({ input }) => { + const id = input; + + if (isNaN(Number(id))) { + return { + status: false, + code: 400, + message: 'internal' satisfies ErrorPath + } satisfies ErrorApiResponse; + } + + try { + await conn.deleteFrom('equipment_type').where('id', '=', Number(id)).execute(); + + return { + status: true + } satisfies Response; + } catch (err) { + console.error(err); + return { + status: false, + code: 500, + message: 'internal' satisfies ErrorPath + } satisfies ErrorApiResponse; + } + }) +]; diff --git a/src/lib/server/_routes/upload.ts b/src/lib/server/_routes/upload.ts new file mode 100644 index 0000000..aaa8e4a --- /dev/null +++ b/src/lib/server/_routes/upload.ts @@ -0,0 +1,72 @@ +import { FormDataInput, type ErrorApiResponse } from '@patrick115/sveltekitapi'; +import { loggedProcedure } from '../api'; +import type { ErrorPath } from '$/lib/lang'; +import { isFile, uploadFile } from '../functions'; +import type { Response, ResponseWithData } from '$/types/types'; +import { z } from 'zod'; +import fs from 'node:fs/promises'; +import Path from 'node:path'; +import { FILE_FOLDER } from '$env/static/private'; + +export default [ + loggedProcedure.POST.input(FormDataInput).query(async ({ input }) => { + const file = input.get('file'); + + if (file === null) { + return { + status: false, + code: 400, + message: 'upload.missing' satisfies ErrorPath + } satisfies ErrorApiResponse; + } + + if (!(file instanceof File)) { + return { + status: false, + code: 400, + message: 'upload.invalidFile' satisfies ErrorPath + } satisfies ErrorApiResponse; + } + + try { + const imageName = await uploadFile(file); + + if (!imageName) { + return { + status: false, + code: 400, + message: 'upload.invalidFile' satisfies ErrorPath + } satisfies ErrorApiResponse; + } + + return { + status: true, + data: imageName + } satisfies ResponseWithData; + } catch (e) { + console.error(e); + return { + status: false, + code: 500, + message: 'upload.error' satisfies ErrorPath + } satisfies ErrorApiResponse; + } + }), + loggedProcedure.DELETE.input(z.string()).query(async ({ input }) => { + const path = Path.join(FILE_FOLDER, input); + + if (!(await isFile(path))) { + return { + status: false, + code: 404, + message: 'upload.notFound' satisfies ErrorPath + } satisfies ErrorApiResponse; + } + + await fs.unlink(path); + + return { + status: true + } satisfies Response; + }) +]; diff --git a/src/lib/server/api.ts b/src/lib/server/api.ts index 25c73fd..8d70b39 100644 --- a/src/lib/server/api.ts +++ b/src/lib/server/api.ts @@ -1,21 +1,18 @@ import { APICreate, MiddleWareError } from '@patrick115/sveltekitapi'; import type { Context } from './context'; -export const t = new APICreate(); +export const api = new APICreate(); -export const router = t.router; -export const procedure = t.procedure; -export const adminProcedure = procedure.use(async ({ next, ctx }) => { - if (!ctx.logged) { - throw new MiddleWareError({ - status: false, - code: 401, - message: 'You need to sign in.' - }); - } - - return next({ - logged: true, - data: ctx.data +export const router = api.router; +export const procedure = api.procedure; +export const loggedProcedure = procedure.use(async ({ ctx, next }) => { + if (!ctx.logged) { + throw new MiddleWareError({ + status: false, + code: 401, + message: 'Unauthorized' }); + } + + return next(ctx.data); }); diff --git a/src/lib/server/context.ts b/src/lib/server/context.ts index 31f496f..eb254fc 100644 --- a/src/lib/server/context.ts +++ b/src/lib/server/context.ts @@ -1,38 +1,8 @@ -import type { RegularUser } from '$/types/types'; import type { AsyncReturnType, CreateContext } from '@patrick115/sveltekitapi'; -import { jwt } from './variables'; +import { getUserState } from './functions'; -export const createContext = (async ({ - cookies -}): Promise< - | { - logged: false; - } - | { - logged: true; - data: RegularUser; - } -> => { - const cookie = cookies.get('session'); - - if (!cookie) { - return { - logged: false - }; - } - - const data = jwt.getCookie(cookie); - - if (!data) { - return { - logged: false - }; - } - - return { - logged: true, - data - }; +export const context = (async ({ cookies }) => { + return getUserState(cookies); }) satisfies CreateContext; -export type Context = AsyncReturnType; +export type Context = AsyncReturnType; diff --git a/src/lib/server/cookies/main.ts b/src/lib/server/cookies/main.ts index ecc20b9..5a887f0 100644 --- a/src/lib/server/cookies/main.ts +++ b/src/lib/server/cookies/main.ts @@ -1,97 +1,105 @@ import jwt from 'jsonwebtoken'; -import path from 'path'; +import path, { dirname } from 'path'; import JSONdb from 'simple-json-db'; +import { fileURLToPath } from 'url'; import { v4 as uuid } from 'uuid'; /** - * @author patrick115 (Patrik Mintěl) + * @author patrick115 Patrik Mintěl * @license MIT - * @version 1.2.0 - * @description Cookie lib - * @homepage https://patrick115.eu + * @version 1.0.0 + * @description Cookie manager */ +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + interface cookie { - expires: number; - values: Type; + expires: number; + values: Type; } export class SessionCookies { - private db: JSONdb>; - - constructor(storage = './cookies.json') { - this.db = new JSONdb>(storage.startsWith('./') || storage.startsWith('../') ? path.join(__dirname, storage) : storage, { - syncOnWrite: true, - jsonSpaces: false, - asyncWrite: false - }); - } - - checkCookies() { - const cookies = Object.entries(this.db.JSON()); - cookies.forEach(([key, value]) => { - if (value.expires < Date.now()) { - this.db.delete(key); - } - }); - } + private db: JSONdb>; + + constructor(storage = './cookies.json') { + this.db = new JSONdb>( + storage.startsWith('./') || storage.startsWith('../') + ? path.join(__dirname, storage) + : storage, + { + syncOnWrite: true, + jsonSpaces: false, + asyncWrite: false + } + ); + } + + checkCookies() { + const cookies = Object.entries(this.db.JSON()); + cookies.forEach(([key, value]) => { + if (value.expires < Date.now()) { + this.db.delete(key); + } + }); + } - getCookie(key: string) { - this.checkCookies(); - return this.db.get(key) as cookie; - } + getCookie(key: string) { + this.checkCookies(); + return this.db.get(key) as cookie; + } - updateCookie(id: string, value: T, age: number) { - this.checkCookies(); + updateCookie(id: string, value: T, age: number) { + this.checkCookies(); - this.db.set(id, { - expires: Date.now() + age, - values: value - }); + this.db.set(id, { + expires: Date.now() + age, + values: value + }); - return id; - } + return id; + } - setCookie(value: T, age: number) { - this.checkCookies(); + setCookie(value: T, age: number) { + this.checkCookies(); - let id = uuid(); + let id = uuid(); - while (this.db.has(id)) { - id = uuid(); - } + while (this.db.has(id)) { + id = uuid(); + } - this.db.set(id, { - expires: Date.now() + age, - values: value - }); + this.db.set(id, { + expires: Date.now() + age, + values: value + }); - return id; - } + return id; + } - deleteCookie(key: string) { - this.checkCookies(); - this.db.delete(key); - } + deleteCookie(key: string) { + this.checkCookies(); + this.db.delete(key); + } } export class JWTCookies { - private key: string; - - constructor(key: string) { - this.key = key; - } - - setCookie(value: object | string | Buffer): string { - return jwt.sign(value, this.key); - } - - getCookie(token: string): T | null { - try { - return jwt.verify(token, this.key) as T; - } catch (e) { - console.error(e); - return null; - } + private key: string; + constructor(key: string) { + this.key = key; + } + + setCookie(value: object | string | Buffer) { + return jwt.sign(value, this.key); + } + + getCookie(token: string): T | null { + try { + return jwt.verify(token, this.key) as T; + } catch (error) { + //eslint-disable-next-line no-console + console.error('Invalid token:', error); + return null; } + } } diff --git a/src/lib/server/functions.ts b/src/lib/server/functions.ts index cf25128..cf25c9f 100644 --- a/src/lib/server/functions.ts +++ b/src/lib/server/functions.ts @@ -1,30 +1,205 @@ -import { json } from '@sveltejs/kit'; -import type { z } from 'zod'; - -export const checkData = async (request: Request, obj: z.ZodType): Promise> => { - let data; - - try { - data = await request.json(); - } catch (_) { - return json({ - status: false, - error: 'Invalid data' - }); +import type { DB, Translations } from '$/types/database'; +import { + extensions, + type DePromise, + type ImageExtension, + type UserData, + type UserState +} from '$/types/types'; +import { FILE_FOLDER } from '$env/static/private'; +import type { Cookies } from '@sveltejs/kit'; +import { redirect as _redirect } from '@sveltejs/kit'; +import type { ControlledTransaction, Insertable } from 'kysely'; +import crypto from 'node:crypto'; +import fs from 'node:fs/promises'; +import Path from 'node:path'; +import { promisify } from 'node:util'; +import { v4 } from 'uuid'; +import type z from 'zod'; +import { languages } from '../lang'; +import { getState } from '../state.svelte'; +import { conn, jwt } from './variables'; + +const randomBytesAsync = promisify(crypto.randomBytes); + +export const getUserState = (cookies: Cookies): UserState => { + const session = cookies.get('session'); + if (!session) { + return { + logged: false + }; + } + + const data = jwt.getCookie(session); + + if (!data) { + return { + logged: false + }; + } + + return { + logged: true, + data + }; +}; + +type Params = Parameters; + +export const redirect = (status: Params[0], location: Params[1]) => { + const state = getState(); + _redirect(status, `/${state.selectedLang || 'cs'}${location}`); +}; + +export const isDirectory = async (path: string) => { + try { + const stats = await fs.stat(path); + return stats.isDirectory(); + } catch { + return false; + } +}; + +export const isFile = async (path: string) => { + try { + const stats = await fs.stat(path); + return stats.isFile(); + } catch { + return false; + } +}; + +export const uploadFile = async (file: File) => { + const arrayBuffer = await file.arrayBuffer(); + const path = Path.parse(file.name); + + if (!extensions.includes(path.ext.substring(1) as ImageExtension)) { + return undefined; + } + + const name = (await randomBytesAsync(16)).toString('hex') + path.ext; + + if (!(await isDirectory(FILE_FOLDER))) { + await fs.mkdir(FILE_FOLDER, { recursive: true }); + } + + await fs.writeFile(Path.join(FILE_FOLDER, name), Buffer.from(arrayBuffer)); + return name; +}; + +export const gatherTranslations = async ( + uuids: string[], + lang: keyof typeof languages +) => { + const trans = await conn + .selectFrom('translations') + .select(['key', 'text']) + .where('lang', '=', lang) + .where('key', 'in', uuids) + .execute(); + return Object.fromEntries(trans.map((tran) => [tran.key, tran.text])); +}; + +export const gatherTranslationsAll = async (uuids: string[]) => { + const keys = Object.keys(languages); + + const resolved = await Promise.all( + keys.map(async (lang) => gatherTranslations(uuids, lang)) + ); + + return Object.fromEntries(resolved.map((tran, idx) => [keys[idx], tran])); +}; + +export const constructEmptyTranslations = () => { + const keys = Object.keys(languages); + //eslint-disable-next-line @typescript-eslint/no-explicit-any + const translations: Record<(typeof keys)[number], Record> = {} as any; + for (const lang of keys) { + translations[lang] = {}; + } + + return translations; +}; + +export type GatheredTranslationsAll = DePromise>; + +export const parseFormData = <$DataType>( + formData: FormData, + schema: z.ZodType<$DataType> +) => { + const object = Object.fromEntries(formData.entries()); + //parse objects/arrays + for (const key in object) { + const value = object[key]; + if ( + typeof value === 'string' && + (value.startsWith('{') || value.startsWith('[') || value.startsWith('"')) + ) { + try { + object[key] = JSON.parse(value); + } catch { + // If parsing fails, keep the original string + } } + } + + //resolve languagable + //language:key = value + + const groupped = {} as Record>; + const languagesKeys = Object.keys(languages); - const resp = obj.safeParse(data); + for (const lang of languagesKeys) { + groupped[lang] = {}; + } - if (resp.success) { - return resp.data; + for (const key in object) { + const [lang, subKey] = key.split(':'); + if (lang && subKey && (languagesKeys as string[]).includes(lang)) { + groupped[lang as keyof typeof languages][subKey] = object[key]; + } else { + for (const lang of languagesKeys) { + groupped[lang][key] = object[key]; + } } + } - return json({ - status: false, - error: resp.error - }); + //parse search sub-object + for (const lang in groupped) { + const subObject = groupped[lang]; + const parsed = schema.parse(subObject); + groupped[lang] = parsed as Record; + } + + return groupped as Record; }; -export const isOk = (data: Response | unknown): data is Response => { - return data instanceof Response; +export const insertTranslations = async < + $Record extends Record, + $Fields extends keyof $Record +>( + trx: ControlledTransaction, + obj: Record, + fields: $Fields[] +) => { + const result = {} as Record<$Fields, string>; + + const toInsert = [] as Insertable[]; + + const langs = Object.keys(obj); + for (const field of fields) { + const uuid = v4(); + for (const lang of langs) { + toInsert.push({ + key: uuid, + lang: lang, + text: obj[lang][field] as string + }); + result[field] = uuid; + } + } + + await trx.insertInto('translations').values(toInsert).execute(); + + return result; }; diff --git a/src/lib/server/routes.ts b/src/lib/server/routes.ts new file mode 100644 index 0000000..a1eb923 --- /dev/null +++ b/src/lib/server/routes.ts @@ -0,0 +1,16 @@ +import { router } from './api'; +import login from './_routes/login'; +import types from './_routes/types'; +import equipment from './_routes/equipment'; +import upload from './_routes/upload'; +import article from './_routes/article'; + +export const r = router({ + login, + types, + equipment, + upload, + article +}); + +export type AppRouter = typeof r; diff --git a/src/lib/server/routes/_app.ts b/src/lib/server/routes/_app.ts deleted file mode 100644 index c226edb..0000000 --- a/src/lib/server/routes/_app.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { router } from '../api'; -import { auth } from './auth'; -import details from './details'; -import equipment from './equipment'; -import gallery from './gallery/index'; -import { project } from './project'; -import { projects } from './projects'; -import stats from './stats'; -import { tag } from './tag'; -import { tags } from './tags'; -import { upload } from './upload'; - -export const Router = router({ - auth, - project, - projects, - upload, - tag, - tags, - gallery, - equipment, - stats, - details -}); - -export type AppRouter = typeof Router; diff --git a/src/lib/server/routes/auth/index.ts b/src/lib/server/routes/auth/index.ts deleted file mode 100644 index 8269c1e..0000000 --- a/src/lib/server/routes/auth/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { login } from './login'; -import { logout } from './logout'; - -export const auth = { - login, - logout -}; diff --git a/src/lib/server/routes/auth/login.ts b/src/lib/server/routes/auth/login.ts deleted file mode 100644 index 9c8ba83..0000000 --- a/src/lib/server/routes/auth/login.ts +++ /dev/null @@ -1,53 +0,0 @@ -import type { RegularUser, ResponseWithData } from '$/types/types'; -import { COOKIE_EXPIRE } from '$env/static/private'; -import type { ErrorApiResponse } from '@patrick115/sveltekitapi'; -import bcrypt from 'bcrypt'; -import { z } from 'zod'; -import { procedure } from '../../api'; -import { conn, jwt } from '../../variables'; - -export const login = procedure.POST.input( - z.object({ - username: z.string(), - password: z.string() - }) -).query(async ({ ev: { cookies }, input }) => { - const userData = await conn.selectFrom('user').selectAll().where('username', '=', input.username).executeTakeFirst(); - - if (!userData) { - return { - status: false, - code: 400, - message: 'Zadal jsi neplatné uživatelské jméno' - } satisfies ErrorApiResponse; - } - - const { password: hashedPassword } = userData; - - const result = bcrypt.compareSync(input.password, hashedPassword); - - if (!result) { - return { - status: false, - code: 400, - message: 'Zadal jsi neplatné heslo' - } satisfies ErrorApiResponse; - } - - const data = { - id: userData.id, - username: userData.username - }; - - const cookie = jwt.setCookie(data); - - cookies.set('session', cookie, { - path: '/', - maxAge: parseInt(COOKIE_EXPIRE) * 100 - }); - - return { - status: true, - data - } satisfies ResponseWithData; -}); diff --git a/src/lib/server/routes/auth/logout.ts b/src/lib/server/routes/auth/logout.ts deleted file mode 100644 index fc9e8f4..0000000 --- a/src/lib/server/routes/auth/logout.ts +++ /dev/null @@ -1,12 +0,0 @@ -import type { Response } from '$/types/types'; -import { adminProcedure } from '../../api'; - -export const logout = adminProcedure.GET.query(async ({ ev: { cookies } }) => { - cookies.delete('session', { - path: '/' - }); - - return { - status: true - } satisfies Response; -}); diff --git a/src/lib/server/routes/details/index.ts b/src/lib/server/routes/details/index.ts deleted file mode 100644 index 22bfef6..0000000 --- a/src/lib/server/routes/details/index.ts +++ /dev/null @@ -1,12 +0,0 @@ -import type { DetailTypes, ResponseWithData } from '$/types/types'; -import { adminProcedure } from '../../api'; -import { conn } from '../../variables'; - -const GET = adminProcedure.GET.query(async () => { - return { - status: true, - data: await conn.selectFrom('detail_types').selectAll().execute() - } satisfies ResponseWithData; -}); - -export default [GET]; diff --git a/src/lib/server/routes/equipment/index.ts b/src/lib/server/routes/equipment/index.ts deleted file mode 100644 index f31a994..0000000 --- a/src/lib/server/routes/equipment/index.ts +++ /dev/null @@ -1,100 +0,0 @@ -import type { Equipment, EquipmentInfo, Response, ResponseWithData } from '$/types/types'; -import type { ErrorApiResponse } from '@patrick115/sveltekitapi'; -import { z } from 'zod'; -import { adminProcedure, procedure } from '../../api'; -import { conn } from '../../variables'; -import t from './type'; - -const add = adminProcedure.PUT.input( - z.object({ - name: z.string(), - link: z.string().url(), - type: z.number() - }) -).query(async ({ input }) => { - try { - await conn.insertInto('equipment').values(input).execute(); - - return { - status: true - } satisfies Response; - } catch (_) { - return { - status: false, - code: 500, - message: 'Internal server error' - } satisfies ErrorApiResponse; - } -}); - -const get = procedure.GET.query(async () => { - const data = await conn - .selectFrom('equipment') - .innerJoin('equipment_type', 'equipment.type', 'equipment_type.id') - .select(['equipment.name', 'equipment_type.name as type', 'equipment.link', 'equipment.id', 'equipment_type.id as type_id']) - .execute(); - - return { - status: true, - data - } satisfies ResponseWithData; -}); - -const remove = adminProcedure.DELETE.input(z.number()).query(async ({ input }) => { - await conn.deleteFrom('equipment').where('id', '=', input).execute(); - - return { - status: true - } satisfies Response; -}); - -const single = adminProcedure.POST.input(z.number()).query(async ({ input }) => { - const item = await conn.selectFrom('equipment').selectAll().executeTakeFirst(); - - if (!item) { - return { - status: false, - code: 404, - message: 'Not found' - } satisfies ErrorApiResponse; - } - - return { - status: true, - data: item - } satisfies ResponseWithData; -}); - -const update = adminProcedure.PATCH.input( - z.object({ - id: z.number().min(1), - name: z.string(), - link: z.string().url(), - type: z.number() - }) -).query(async ({ input }) => { - const result = await conn.updateTable('equipment').set(input).where('id', '=', input.id).executeTakeFirst(); - - if (!result) { - return { - status: false, - code: 404, - message: 'Not found' - } satisfies ErrorApiResponse; - } - - return { - status: true - } satisfies Response; -}); - -export default [ - get, - add, - remove, - single, - update, - { - type: t - } -]; diff --git a/src/lib/server/routes/equipment/type.ts b/src/lib/server/routes/equipment/type.ts deleted file mode 100644 index 1e82964..0000000 --- a/src/lib/server/routes/equipment/type.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type { EquipmentType, ResponseWithData } from '$/types/types'; -import { adminProcedure } from '../../api'; -import { conn } from '../../variables'; - -const endpoint = adminProcedure.GET.query(async () => { - const types = await conn.selectFrom('equipment_type').selectAll().execute(); - - return { - status: true, - data: types - } satisfies ResponseWithData; -}); - -export default endpoint; diff --git a/src/lib/server/routes/gallery/index.ts b/src/lib/server/routes/gallery/index.ts deleted file mode 100644 index b077313..0000000 --- a/src/lib/server/routes/gallery/index.ts +++ /dev/null @@ -1,210 +0,0 @@ -import type { GalleryItem, Response, ResponseWithData } from '$/types/types'; -import { FormDataInput, type ErrorApiResponse } from '@patrick115/sveltekitapi'; -import crypto from 'node:crypto'; -import fs from 'node:fs'; -import Path from 'node:path'; -import { z } from 'zod'; -import { adminProcedure, procedure } from '../../api'; -import { conn } from '../../variables'; -import single from './single'; - -const extensions = ['.png', '.jpg', '.jpeg', '.gif', '.avif', '.tiff', '.webp', '.webm'] as const; - -const upload = adminProcedure.POST.input(FormDataInput).query(async ({ input }) => { - const file = input.get('file') as File | null; - if (!file) { - return { - status: false, - code: 400, - message: 'No file provided' - } satisfies ErrorApiResponse; - } - - const root = Path.join('projects', 'gallery'); - - if (!fs.existsSync(root)) { - fs.mkdirSync(root, { recursive: true }); - } - - let path = Path.join(root, file.name); - const fileName = Path.parse(file.name); - - while (fs.existsSync(path)) { - path = Path.join(root, fileName.name + crypto.randomBytes(4).toString('hex') + fileName.ext); - } - - if (!extensions.includes(fileName.ext as (typeof extensions)[number])) { - return { - status: false, - code: 400, - message: 'Nahrál jsi neplatný typ souboru' - } satisfies ErrorApiResponse; - } - - const buffer = await file.arrayBuffer(); - - fs.writeFileSync(path, Buffer.from(buffer)); - - return { - status: true, - data: file.name - } satisfies ResponseWithData; -}); - -const list = procedure.GET.query(async () => { - try { - const galleryItems = await conn.selectFrom('gallery').selectAll().orderBy('date', 'desc').execute(); - - const joinTable = await conn.selectFrom('gallery_equipment').selectAll().execute(); - - const equipments = await conn - .selectFrom('equipment') - .innerJoin('equipment_type', 'equipment.type', 'equipment_type.id') - .select(['equipment.id', 'equipment.link', 'equipment.name', 'equipment_type.name as type', 'equipment.id as type_id']) - .execute(); - - const data: GalleryItem[] = []; - - for (const item of galleryItems) { - const equipmentIds = joinTable.filter((join) => join.gallery_id === item.id).map((item) => item.equipment_id); - - data.push({ - ...item, - equipment: equipments.filter((equipment) => equipmentIds.includes(equipment.id)) - }); - } - - return { - status: true, - data - } satisfies ResponseWithData; - } catch (_) { - return { - status: false, - code: 500, - message: 'Internal server error' - } satisfies ErrorApiResponse; - } -}); - -const add = procedure.PUT.input( - z.object({ - id: z.number().optional(), - alt: z.string(), - date: z.coerce.date(), - name: z.string(), - equipment: z.array( - z.object({ - id: z.number(), - name: z.string(), - link: z.string(), - type: z.string(), - type_id: z.number() - }) - ) - }) -).query(async ({ input }) => { - try { - const result = await conn - .insertInto('gallery') - .values({ - name: input.name, - date: input.date, - alt: input.alt - }) - .executeTakeFirst(); - - //enter equipment - if (!result.insertId) { - throw new Error('Unable to get id'); - } - - await conn - .insertInto('gallery_equipment') - .values( - input.equipment.map((item) => { - return { - gallery_id: Number(result.insertId), - equipment_id: item.id - }; - }) - ) - .execute(); - - return { - status: true - } satisfies Response; - } catch (_) { - return { - status: false, - code: 500, - message: 'Internal server error' - } satisfies ErrorApiResponse; - } -}); - -const remove = adminProcedure.DELETE.input(z.number()).query(async ({ input }) => { - await conn.deleteFrom('gallery').where('id', '=', input).execute(); - - return { - status: true - } satisfies Response; -}); - -const edit = adminProcedure.PATCH.input( - z.object({ - id: z.number(), - alt: z.string(), - date: z.coerce.date(), - name: z.string(), - equipment: z.array( - z.object({ - id: z.number(), - name: z.string(), - link: z.string(), - type: z.string(), - type_id: z.number() - }) - ) - }) -).query(async ({ input }) => { - try { - await conn - .updateTable('gallery') - .set({ - name: input.name, - date: input.date, - alt: input.alt - }) - .where('id', '=', input.id) - .execute(); - - //delete old equipment - await conn.deleteFrom('gallery_equipment').where('gallery_id', '=', input.id).execute(); - - //add current equipment - await conn - .insertInto('gallery_equipment') - .values( - input.equipment.map((item) => { - return { - gallery_id: input.id, - equipment_id: item.id - }; - }) - ) - .execute(); - - return { - status: true - } satisfies Response; - } catch (_) { - return { - status: false, - code: 500, - message: 'Internal server error' - } satisfies ErrorApiResponse; - } -}); - -export default [upload, list, add, remove, edit, { single }]; diff --git a/src/lib/server/routes/gallery/single.ts b/src/lib/server/routes/gallery/single.ts deleted file mode 100644 index 0ba6765..0000000 --- a/src/lib/server/routes/gallery/single.ts +++ /dev/null @@ -1,61 +0,0 @@ -import type { GalleryItem, NoUndefinedField, ResponseWithData } from '$/types/types'; -import type { ErrorApiResponse } from '@patrick115/sveltekitapi'; -import { z } from 'zod'; -import { adminProcedure } from '../../api'; -import { conn } from '../../variables'; - -const endpoint = adminProcedure.POST.input(z.number()).query(async ({ input }) => { - const galleryItem = await conn - .selectFrom('gallery') - .leftJoin('gallery_equipment', 'gallery.id', 'gallery_equipment.gallery_id') - .leftJoin('equipment', 'gallery_equipment.equipment_id', 'equipment.id') - .leftJoin('equipment_type', 'equipment.type', 'equipment_type.id') - .select([ - 'gallery.id', - 'gallery.name', - 'gallery.date', - 'gallery.alt', - 'equipment.id as equipment_id', - 'equipment.name as equipment_name', - 'equipment.link', - 'equipment_type.id as type_id', - 'equipment_type.name as type_name' - ]) - .where('gallery.id', '=', input) - .execute(); - - if (galleryItem.length === 0) { - return { - status: false, - code: 404, - message: 'Not found' - } satisfies ErrorApiResponse; - } - - const first = galleryItem[0]; - - return { - status: true, - data: { - id: first.id, - alt: first.alt, - name: first.name, - date: first.date, - equipment: ( - galleryItem.filter((item) => { - return item.equipment_id && item.equipment_name && item.link && item.type_id && item.type_name; - }) as NoUndefinedField<(typeof galleryItem)[number]>[] - ).map((item) => { - return { - id: item.equipment_id, - name: item.equipment_name, - link: item.link, - type: item.type_name, - type_id: item.type_id - }; - }) - } - } satisfies ResponseWithData; -}); - -export default endpoint; diff --git a/src/lib/server/routes/project/get.ts b/src/lib/server/routes/project/get.ts deleted file mode 100644 index ff276d1..0000000 --- a/src/lib/server/routes/project/get.ts +++ /dev/null @@ -1,269 +0,0 @@ -import { projectDataSchema, type FullProjectData, type Response, type ResponseWithData } from '$/types/types'; -import type { ErrorApiResponse } from '@patrick115/sveltekitapi'; -import fs from 'node:fs'; -import path from 'node:path'; -import { v4 } from 'uuid'; -import { z } from 'zod'; -import { adminProcedure } from '../../api'; -import { conn } from '../../variables'; - -const schema = z.object({ - uuid: z.string() -}); - -const create = adminProcedure.PUT.input(projectDataSchema).query(async ({ input }) => { - const uuid = v4(); - - const imageData = path.parse(input.filePath); - - const result = await conn - .insertInto('project') - .values({ - uuid, - name: input.name, - description: input.description, - date: new Date(input.date), - preview: imageData.base, - image_count: input.images.length - }) - .executeTakeFirst(); - - if (result.numInsertedOrUpdatedRows == 0n) { - return { - status: false, - code: 500, - message: 'Nepodařilo se vložit projekt' - } satisfies ErrorApiResponse; - } - - const root = 'projects'; - - const images = [input.filePath, ...input.images]; - - fs.mkdirSync(path.join(root, uuid)); - - for (const imageId in images) { - const image = images[imageId]; - - const imageData = path.parse(image); - - fs.copyFileSync(path.join(root, imageData.base), path.join(root, uuid, imageData.base)); - fs.unlinkSync(path.join(root, imageData.base)); - - if (parseInt(imageId) == 0) continue; - - //insert - const result = await conn - .insertInto('project_image') - .values({ - project: uuid, - name: imageData.base - }) - .executeTakeFirst(); - - if (result.numInsertedOrUpdatedRows == 0n) { - return { - status: false, - code: 500, - message: 'Nepodařilo se vložit obrázek' - } satisfies ErrorApiResponse; - } - } - - return { - status: true, - data: uuid - } satisfies ResponseWithData; -}); - -const getData = adminProcedure.POST.input(schema).query(async ({ input: { uuid } }) => { - const mainData = await conn.selectFrom('project').selectAll().where('uuid', '=', uuid).executeTakeFirst(); - - if (!mainData) { - return { - status: false, - code: 404, - message: 'Project not found' - } satisfies ErrorApiResponse; - } - try { - const tags = await conn.selectFrom('project_tags').selectAll().where('uuid', '=', uuid).execute(); - - const images = await conn.selectFrom('project_image').selectAll().where('project', '=', uuid).execute(); - - return { - status: true, - data: { - ...mainData, - tags: tags.map((tag) => { - return { - ...tag, - id: undefined - }; - }), - images: images.map((image) => { - return { - ...image, - id: undefined - }; - }) - } - } satisfies ResponseWithData; - } catch (_) { - return { - status: false, - code: 500, - message: 'Nepodařilo se získat data o projektu' - } satisfies ErrorApiResponse; - } -}); - -const deleteData = adminProcedure.DELETE.input(schema).query(async ({ input: { uuid } }) => { - const result = await conn.deleteFrom('project').where('uuid', '=', uuid).executeTakeFirst(); - - if (result.numDeletedRows == 0n) { - return { - status: false, - code: 500, - message: 'Nepodařilo se smazat projekt' - } satisfies ErrorApiResponse; - } - - fs.rmSync(path.join('projects', uuid), { - recursive: true - }); - - return { - status: true - } satisfies Response; -}); - -const updateSchema = projectDataSchema.extend({ - uuid: z.string(), - previewChanged: z.boolean(), - tags: z.array( - z.object({ - uuid: z.string(), - tag: z.number() - }) - ) -}); - -const updateData = adminProcedure.PATCH.input(updateSchema).query(async ({ input }) => { - const originalData = await conn.selectFrom('project').selectAll().where('uuid', '=', input.uuid).executeTakeFirst(); - - if (!originalData) { - return { - status: false, - code: 404, - message: 'Projekt nebyl nalezen' - } satisfies ErrorApiResponse; - } - - const preview = path.parse(input.filePath); - if (preview.base != originalData.preview) { - fs.copyFileSync(path.join('projects', preview.base), path.join('projects', input.uuid, preview.base)); - fs.unlinkSync(path.join('projects', input.uuid, originalData.preview)); - } - - await conn - .updateTable('project') - .set({ - name: input.name, - description: input.description, - date: new Date(input.date), - preview: preview.base - }) - .where('uuid', '=', input.uuid) - .executeTakeFirst(); - - const images = await conn.selectFrom('project_image').selectAll().where('project', '=', input.uuid).execute(); - try { - //delete old images - for (const image of images) { - if (!input.images.includes(image.name)) { - fs.unlinkSync(path.join('projects', input.uuid, image.name)); - await conn.deleteFrom('project_image').where('id', '=', image.id).executeTakeFirstOrThrow(); - } - } - } catch (_) { - return { - status: false, - code: 500, - message: 'Nepodařilo se smazat staré obrázky' - } satisfies ErrorApiResponse; - } - - //insert new images - const newImages = input.images.filter((image) => { - const p = path.parse(image); - - if (p.dir !== '') { - return true; - } - - if (p.root !== '') { - return true; - } - return false; - }); - - try { - for (const image of newImages) { - const p = path.parse(image); - - fs.copyFileSync(path.join('projects', p.base), path.join('projects', input.uuid, p.base)); - fs.unlinkSync(path.join('projects', p.base)); - - await conn - .insertInto('project_image') - .values({ - project: input.uuid, - name: p.base - }) - .executeTakeFirstOrThrow(); - } - } catch (_) { - return { - status: false, - code: 500, - message: 'Nepodařilo se vložit nové obrázky' - } satisfies ErrorApiResponse; - } - - try { - //sync tags - const currentTags = await conn.selectFrom('project_tags').selectAll().where('uuid', '=', input.uuid).execute(); - - const ids = input.tags.map((tag) => tag.tag); - - //remove unused tags - for (const tag of currentTags.filter((tag) => !ids.includes(tag.tag))) { - await conn.deleteFrom('project_tags').where('tag', '=', tag.tag).executeTakeFirstOrThrow(); - } - - const current = currentTags.map((tag) => tag.tag); - - //add new tags - for (const tag of input.tags.filter((tag) => !current.includes(tag.tag))) { - await conn - .insertInto('project_tags') - .values({ - uuid: input.uuid, - tag: tag.tag - }) - .executeTakeFirstOrThrow(); - } - } catch (_) { - return { - status: false, - code: 500, - message: 'Nepodařilo se aktualizovat tagy' - } satisfies ErrorApiResponse; - } - return { - status: true - } satisfies Response; -}); - -export const get = [create, getData, deleteData, updateData]; diff --git a/src/lib/server/routes/project/index.ts b/src/lib/server/routes/project/index.ts deleted file mode 100644 index d567b83..0000000 --- a/src/lib/server/routes/project/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { get } from './get'; -import { list } from './list'; - -export const project = { - list, - get -}; diff --git a/src/lib/server/routes/project/list.ts b/src/lib/server/routes/project/list.ts deleted file mode 100644 index f9b1b2a..0000000 --- a/src/lib/server/routes/project/list.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { adminProcedure } from '../../api'; -import { conn } from '../../variables'; - -export const list = adminProcedure.GET.query(async () => { - const data = await conn.selectFrom('project').selectAll().execute(); - return data; -}); diff --git a/src/lib/server/routes/projects.ts b/src/lib/server/routes/projects.ts deleted file mode 100644 index 8fecce0..0000000 --- a/src/lib/server/routes/projects.ts +++ /dev/null @@ -1,80 +0,0 @@ -import type { PublicProjectData, ResponseWithData, Tag } from '$/types/types'; -import type { ErrorApiResponse } from '@patrick115/sveltekitapi'; -import { z } from 'zod'; -import { procedure } from '../api'; -import { conn } from '../variables'; - -const list = procedure.GET.query(async () => { - try { - const data = await conn.selectFrom('project').selectAll().execute(); - const tags = await conn.selectFrom('tag').selectAll().execute(); - - const returnData: PublicProjectData[] = []; - - for (const project of data) { - const result = await conn.selectFrom('project_tags').select('tag').where('uuid', '=', project.uuid).execute(); - const projectTags = result.map((tag) => tag.tag); - const images = await conn.selectFrom('project_image').select('name').where('project', '=', project.uuid).execute(); - - returnData.push({ - ...project, - tags: tags.filter((tag) => projectTags.includes(tag.id)), - images: images.map((image) => image.name) - }); - } - - return { - status: true, - data: returnData - } satisfies ResponseWithData; - } catch (_) { - return { - status: false, - code: 500, - message: 'Nepovedlo se načíst projekty' - } satisfies ErrorApiResponse; - } -}); - -const getOne = procedure.POST.input( - z.object({ - uuid: z.string() - }) -).query(async ({ input: { uuid } }) => { - try { - const project = await conn.selectFrom('project').selectAll().where('uuid', '=', uuid).executeTakeFirst(); - - if (!project) { - return { - status: false, - code: 404, - message: 'Projekt nebyl nalezen' - } satisfies ErrorApiResponse; - } - - const images = await conn.selectFrom('project_image').where('project', '=', uuid).select('name').execute(); - const tags = (await conn - .selectFrom('project_tags') - .innerJoin('tag', 'project_tags.tag', 'tag.id') - .select(['id', 'name', 'color']) - .where('uuid', '=', uuid) - .execute()) as Tag[]; - - return { - status: true, - data: { - ...project, - images: images.map((image) => image.name), - tags - } - } satisfies ResponseWithData; - } catch (e) { - return { - status: false, - code: 500, - message: 'Nepovedlo se načíst projekt' - } satisfies ErrorApiResponse; - } -}); - -export const projects = [list, getOne]; diff --git a/src/lib/server/routes/stats/getToday.ts b/src/lib/server/routes/stats/getToday.ts deleted file mode 100644 index b41ff3d..0000000 --- a/src/lib/server/routes/stats/getToday.ts +++ /dev/null @@ -1,29 +0,0 @@ -import type { ResponseWithData } from '$/types/types'; -import type { ErrorApiResponse } from '@patrick115/sveltekitapi'; -import { sql } from 'kysely'; -import { adminProcedure } from '../../api'; -import { conn } from '../../variables'; - -const endpoint = adminProcedure.GET.query(async () => { - try { - const data = await conn - .selectFrom('visitors') - .select('id') - .where(sql`DATE(date)`, '=', sql`CURDATE()`) - .groupBy('ip') - .execute(); - - return { - status: true, - data: data.length - } satisfies ResponseWithData; - } catch (_) { - return { - status: false, - code: 500, - message: 'Internal Server Error' - } satisfies ErrorApiResponse; - } -}); - -export default endpoint; diff --git a/src/lib/server/routes/stats/getWeek.ts b/src/lib/server/routes/stats/getWeek.ts deleted file mode 100644 index 592d7e9..0000000 --- a/src/lib/server/routes/stats/getWeek.ts +++ /dev/null @@ -1,29 +0,0 @@ -import type { ResponseWithData } from '$/types/types'; -import type { ErrorApiResponse } from '@patrick115/sveltekitapi'; -import { sql } from 'kysely'; -import { adminProcedure } from '../../api'; -import { conn } from '../../variables'; - -const endpoint = adminProcedure.GET.query(async () => { - try { - const data = await conn - .selectFrom('visitors') - .select('id') - .where(sql`WEEK(CURDATE(), 1)`, '=', sql`WEEK(date, 1)`) - .groupBy('ip') - .execute(); - - return { - status: true, - data: data.length - } satisfies ResponseWithData; - } catch (_) { - return { - status: false, - code: 500, - message: 'Internal Server Error' - } satisfies ErrorApiResponse; - } -}); - -export default endpoint; diff --git a/src/lib/server/routes/stats/index.ts b/src/lib/server/routes/stats/index.ts deleted file mode 100644 index 3fd1596..0000000 --- a/src/lib/server/routes/stats/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -import getToday from './getToday'; -import getWeek from './getWeek'; -import weekGraph from './weekGraph'; - -export default { - getToday, - getWeek, - weekGraph -}; diff --git a/src/lib/server/routes/stats/weekGraph.ts b/src/lib/server/routes/stats/weekGraph.ts deleted file mode 100644 index 68e8b82..0000000 --- a/src/lib/server/routes/stats/weekGraph.ts +++ /dev/null @@ -1,67 +0,0 @@ -import type { ResponseWithData } from '$/types/types'; -import type { AsyncReturnType, ErrorApiResponse } from '@patrick115/sveltekitapi'; -import { sql } from 'kysely'; -import { adminProcedure } from '../../api'; -import { conn } from '../../variables'; -import { format } from 'date-fns'; -import { z } from 'zod'; - -const getDataForYear = async (year?: number) => { - try { - //SELECT WEEK, COUNT(WEEK) AS COUNT FROM ( - //SELECT ip, WEEK(date, 1) as WEEK FROM visitors GROUP BY WEEK(date, 1), ip - //) AS t GROUP BY WEEK - - let query = conn - .selectFrom((eb) => - eb - .selectFrom('visitors') - .select(['ip', sql`WEEK(date, 1)`.as('WEEK'), sql`YEAR(date)`.as('YEAR')]) - .groupBy([sql`WEEK(date, 1)`, 'ip']) - .as('t') - ) - .select(['YEAR', 'WEEK', sql`COUNT(WEEK)`.as('COUNT')]); - - if (year) { - query = query.where('YEAR', '=', year); - } - - return await query.groupBy(['YEAR', 'WEEK']).execute(); - } catch (_) { - return false; - } -}; - -const GET = adminProcedure.GET.query(async () => { - const result = await getDataForYear(); - - if (!result) { - return { - status: false, - code: 500, - message: 'Internal Server Error' - } satisfies ErrorApiResponse; - } - return { - status: true, - data: result - } satisfies ResponseWithData>; -}); - -const POST = adminProcedure.POST.input(z.number()).query(async ({ input }) => { - const result = await getDataForYear(input); - - if (!result) { - return { - status: false, - code: 500, - message: 'Internal Server Error' - } satisfies ErrorApiResponse; - } - return { - status: true, - data: result - } satisfies ResponseWithData>; -}); - -export default [GET, POST]; diff --git a/src/lib/server/routes/tag.ts b/src/lib/server/routes/tag.ts deleted file mode 100644 index ac7c760..0000000 --- a/src/lib/server/routes/tag.ts +++ /dev/null @@ -1,121 +0,0 @@ -import type { Response, ResponseWithData, Tag } from '$/types/types'; -import type { ErrorApiResponse } from '@patrick115/sveltekitapi'; -import { z } from 'zod'; -import { adminProcedure } from '../api'; -import { conn } from '../variables'; - -export const list = adminProcedure.GET.query(async () => { - try { - const result = await conn.selectFrom('tag').selectAll().execute(); - - return { - status: true, - data: result - } satisfies ResponseWithData; - } catch (_) { - return { - status: false, - code: 500, - message: 'Nepodařilo se načíst tagy' - } satisfies ErrorApiResponse; - } -}); - -const create = adminProcedure.PUT.input( - z.object({ - text: z.string(), - color: z.string() - }) -).query(async ({ input }) => { - const result = await conn - .insertInto('tag') - .values({ - name: input.text, - color: input.color - }) - .executeTakeFirst(); - - if (result.numInsertedOrUpdatedRows == 0n) { - return { - status: false, - code: 500, - message: 'Nepovedlo se vytvořit tag' - } satisfies ErrorApiResponse; - } - - return { - status: true - } satisfies Response; -}); - -const load = adminProcedure.POST.input( - z.object({ - id: z.number() - }) -).query(async ({ input: { id } }) => { - const result = await conn.selectFrom('tag').selectAll().where('id', '=', id).executeTakeFirst(); - - if (!result) { - return { - status: false, - code: 404, - message: 'Tag nebyl nalezen' - } satisfies ErrorApiResponse; - } - - return { - status: true, - data: result - } satisfies ResponseWithData; -}); - -const update = adminProcedure.PATCH.input( - z.object({ - id: z.number(), - text: z.string(), - color: z.string() - }) -).query(async ({ input }) => { - try { - await conn - .updateTable('tag') - .set({ - name: input.text, - color: input.color - }) - .where('id', '=', input.id) - .execute(); - - return { - status: true - } satisfies Response; - } catch (_) { - return { - status: false, - code: 500, - message: 'Nepovedlo se upravit tag' - } satisfies ErrorApiResponse; - } -}); - -const remove = adminProcedure.DELETE.input( - z.object({ - id: z.number() - }) -).query(async ({ input: { id } }) => { - try { - await conn.deleteFrom('tag').where('id', '=', id).execute(); - - return { - status: true - } satisfies Response; - } catch (_) { - return { - status: false, - code: 500, - message: 'Nepovedlo se smazat tag' - } satisfies ErrorApiResponse; - } -}); - -export const tag = [list, create, load, update, remove]; diff --git a/src/lib/server/routes/tags.ts b/src/lib/server/routes/tags.ts deleted file mode 100644 index f158ed7..0000000 --- a/src/lib/server/routes/tags.ts +++ /dev/null @@ -1,21 +0,0 @@ -import type { ResponseWithData, Tag } from '$/types/types'; -import type { ErrorApiResponse } from '@patrick115/sveltekitapi'; -import { procedure } from '../api'; -import { conn } from '../variables'; - -export const tags = procedure.GET.query(async () => { - try { - const tags = await conn.selectFrom('tag').selectAll().execute(); - - return { - status: true, - data: tags - } satisfies ResponseWithData; - } catch (_) { - return { - status: false, - code: 500, - message: 'Nepodařilo se načíst tagy' - } satisfies ErrorApiResponse; - } -}); diff --git a/src/lib/server/routes/upload.ts b/src/lib/server/routes/upload.ts deleted file mode 100644 index d1058e5..0000000 --- a/src/lib/server/routes/upload.ts +++ /dev/null @@ -1,88 +0,0 @@ -import type { Response, ResponseWithData } from '$/types/types'; -import { FormDataInput, type ErrorApiResponse } from '@patrick115/sveltekitapi'; -import crypto from 'node:crypto'; -import fs from 'node:fs'; -import path from 'node:path'; -import { z } from 'zod'; -import { adminProcedure } from '../api'; - -const extensions = ['.png', '.jpg', '.jpeg', '.gif', '.avif', '.tiff', '.webp', '.webm'] as const; - -const create = adminProcedure.POST.input(FormDataInput).query(async ({ input }) => { - const file = input.get('file') as File | null; - - if (!file) { - return { - status: false, - code: 400, - message: 'Nenahrál jsi žádný soubor' - } satisfies ErrorApiResponse; - } - - const fileName = path.parse(file.name); - - if (!extensions.includes(fileName.ext as (typeof extensions)[number])) { - return { - status: false, - code: 400, - message: 'Nahrál jsi neplatný typ souboru' - } satisfies ErrorApiResponse; - } - - const buffer = await file.arrayBuffer(); - - if (!fs.existsSync('projects')) { - fs.mkdirSync('projects'); - } - - let filePath = path.join('projects', file.name); - let finalFileName = file.name; - - if (fs.existsSync(filePath)) { - const fileData = path.parse(file.name); - const bytes = crypto.randomBytes(4).toString('hex'); - - finalFileName = `${fileData.name}.${bytes}${fileData.ext}`; - - filePath = path.join('projects', finalFileName); - } - - fs.writeFileSync(filePath, Buffer.from(buffer)); - - return { - status: true, - data: path.join('/customImages', finalFileName) - } satisfies ResponseWithData; -}); - -const remove = adminProcedure.DELETE.input( - z.object({ - name: z.string() - }) -).query(async ({ input: { name } }) => { - const filePath = path.join('projects', name); - - if (!fs.existsSync(filePath)) { - return { - status: false, - code: 404, - message: 'Soubor neexistuje' - } satisfies ErrorApiResponse; - } - - try { - fs.unlinkSync(filePath); - - return { - status: true - } satisfies Response; - } catch (_) { - return { - status: false, - code: 500, - message: 'Nepodařilo se smazat soubor' - } satisfies ErrorApiResponse; - } -}); - -export const upload = [create, remove]; diff --git a/src/lib/server/server.ts b/src/lib/server/server.ts index 7ee5cd2..9440314 100644 --- a/src/lib/server/server.ts +++ b/src/lib/server/server.ts @@ -1,9 +1,9 @@ import { APIServer } from '@patrick115/sveltekitapi'; -import { createContext } from './context'; -import { Router } from './routes/_app'; +import { context } from './context'; +import { r } from './routes'; export const Server = new APIServer({ - router: Router, - context: createContext, - path: '/api' + router: r, + path: '/api', + context }); diff --git a/src/lib/server/variables.ts b/src/lib/server/variables.ts index 6d348cb..9822a9f 100644 --- a/src/lib/server/variables.ts +++ b/src/lib/server/variables.ts @@ -1,20 +1,27 @@ import type { DB } from '$/types/database'; -import { DATABASE_IP, DATABASE_NAME, DATABASE_PASSWORD, DATABASE_PORT, DATABASE_USER, JWT_SECRET } from '$env/static/private'; +import { + JWT_SECRET, + DATABASE_NAME, + DATABASE_IP, + DATABASE_PASSWORD, + DATABASE_PORT, + DATABASE_USER +} from '$env/static/private'; +import { JWTCookies } from './cookies/main'; import { Kysely, MysqlDialect } from 'kysely'; import { createPool } from 'mysql2'; -import { JWTCookies } from './cookies/main'; -export const jwt = new JWTCookies(JWT_SECRET); +export const jwt = new JWTCookies(JWT_SECRET); const dialect = new MysqlDialect({ - pool: createPool({ - host: DATABASE_IP, - port: parseInt(DATABASE_PORT), - user: DATABASE_USER, - password: DATABASE_PASSWORD, - database: DATABASE_NAME - }) + pool: createPool({ + host: DATABASE_IP, + port: parseInt(DATABASE_PORT), + user: DATABASE_USER, + password: DATABASE_PASSWORD, + database: DATABASE_NAME + }) }); export const conn = new Kysely({ - dialect + dialect }); diff --git a/src/lib/state.svelte.ts b/src/lib/state.svelte.ts new file mode 100644 index 0000000..c4279f9 --- /dev/null +++ b/src/lib/state.svelte.ts @@ -0,0 +1,29 @@ +import type { z } from 'zod'; +import template from './lang/_template'; +import type { UserState } from '$/types/types'; + +type Language = z.infer; + +type State = { + lang: Language; + selectedLang: string; + languages: Record< + string, + { + name: string; + flag: string; + } + >; + path: string; + userState: UserState; +}; + +const state = $state({}) as State; + +export const setState = (newState: Partial) => { + Object.assign(state, newState); +}; + +export const getState = () => { + return state as State; +}; diff --git a/src/params/lang.ts b/src/params/lang.ts new file mode 100644 index 0000000..35d34c4 --- /dev/null +++ b/src/params/lang.ts @@ -0,0 +1,6 @@ +import { languages } from '$/lib/lang'; +import type { ParamMatcher } from '@sveltejs/kit'; + +export const match = ((param: string): param is keyof typeof languages => { + return Object.keys(languages).includes(param as keyof typeof languages); +}) satisfies ParamMatcher; diff --git a/src/routes/(adminLayout)/+layout.server.ts b/src/routes/(adminLayout)/+layout.server.ts deleted file mode 100644 index 16521c8..0000000 --- a/src/routes/(adminLayout)/+layout.server.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { jwt } from '$/lib/server/variables'; -import { npm_package_version } from '$env/static/private'; -import { redirect } from '@sveltejs/kit'; -import type { LayoutServerLoad } from './$types'; - -export const load = (async ({ cookies }) => { - const session = cookies.get('session'); - - if (!session) { - throw redirect(302, '/admin'); - } - - const data = jwt.getCookie(session); - - if (!data) { - throw redirect(302, '/admin'); - } - - return { - version: npm_package_version - }; -}) satisfies LayoutServerLoad; diff --git a/src/routes/(adminLayout)/+layout.svelte b/src/routes/(adminLayout)/+layout.svelte deleted file mode 100644 index 00cb8bd..0000000 --- a/src/routes/(adminLayout)/+layout.svelte +++ /dev/null @@ -1,49 +0,0 @@ - - -{#if $userState.logged} -
-
- {#if folded} - - {:else} - - {/if} -

{navItem?.fullName ?? ''}

- -
-
- - - {@render children()} -
-
-{/if} diff --git a/src/routes/(adminLayout)/admin/equipment/+page.server.ts b/src/routes/(adminLayout)/admin/equipment/+page.server.ts deleted file mode 100644 index 23790e6..0000000 --- a/src/routes/(adminLayout)/admin/equipment/+page.server.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { Server } from '$/lib/server/server'; -import type { PageServerLoad } from './$types'; - -export const load = (async (ev) => { - return { - equipment: await Server.ssr.equipment.GET(ev) - }; -}) satisfies PageServerLoad; diff --git a/src/routes/(adminLayout)/admin/equipment/+page.svelte b/src/routes/(adminLayout)/admin/equipment/+page.svelte deleted file mode 100644 index a0ecdbb..0000000 --- a/src/routes/(adminLayout)/admin/equipment/+page.svelte +++ /dev/null @@ -1,71 +0,0 @@ - - -
- {#each equipments as equipment} -
- {equipment.name} - deleteEquipment(equipment.id)} class="cursor-pointer text-red-500" name="bi-trash-fill" /> -
- {/each} - -
diff --git a/src/routes/(adminLayout)/admin/equipment/[id]/+page.server.ts b/src/routes/(adminLayout)/admin/equipment/[id]/+page.server.ts deleted file mode 100644 index 2b267cc..0000000 --- a/src/routes/(adminLayout)/admin/equipment/[id]/+page.server.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Server } from '$/lib/server/server'; -import type { PageServerLoad } from './$types'; - -export const load = (async (ev) => { - return { - types: await Server.ssr.equipment.type(ev), - equipment: await Server.ssr.equipment.POST(ev, parseInt(ev.params.id)) - }; -}) satisfies PageServerLoad; diff --git a/src/routes/(adminLayout)/admin/equipment/[id]/+page.svelte b/src/routes/(adminLayout)/admin/equipment/[id]/+page.svelte deleted file mode 100644 index 704ebf1..0000000 --- a/src/routes/(adminLayout)/admin/equipment/[id]/+page.svelte +++ /dev/null @@ -1,145 +0,0 @@ - - -
- - - - - - - - - - - - - - - - -
diff --git a/src/routes/(adminLayout)/admin/equipment/new/+page.server.ts b/src/routes/(adminLayout)/admin/equipment/new/+page.server.ts deleted file mode 100644 index 4be1808..0000000 --- a/src/routes/(adminLayout)/admin/equipment/new/+page.server.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { Server } from '$/lib/server/server'; -import type { PageServerLoad } from './$types'; - -export const load = (async (ev) => { - return { - types: await Server.ssr.equipment.type(ev) - } -}) satisfies PageServerLoad; diff --git a/src/routes/(adminLayout)/admin/equipment/new/+page.svelte b/src/routes/(adminLayout)/admin/equipment/new/+page.svelte deleted file mode 100644 index 3853a4d..0000000 --- a/src/routes/(adminLayout)/admin/equipment/new/+page.svelte +++ /dev/null @@ -1,119 +0,0 @@ - - -
- - - - - - - - - - - - - - - - -
diff --git a/src/routes/(adminLayout)/admin/gallery/+page.server.ts b/src/routes/(adminLayout)/admin/gallery/+page.server.ts deleted file mode 100644 index a036b11..0000000 --- a/src/routes/(adminLayout)/admin/gallery/+page.server.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { Server } from '$/lib/server/server'; -import type { PageServerLoad } from './$types'; - -export const load = (async (ev) => { - return { - items: await Server.ssr.gallery.GET(ev) - }; -}) satisfies PageServerLoad; diff --git a/src/routes/(adminLayout)/admin/gallery/+page.svelte b/src/routes/(adminLayout)/admin/gallery/+page.svelte deleted file mode 100644 index a15309a..0000000 --- a/src/routes/(adminLayout)/admin/gallery/+page.svelte +++ /dev/null @@ -1,40 +0,0 @@ - - -
- - - {#if !items} - Načítání... - {:else if items.length == 0} - Přidej nějaký obrázky do galerie - {:else} - {#each items as item} - - {/each} - {/if} -
diff --git a/src/routes/(adminLayout)/admin/gallery/[id]/+page.server.ts b/src/routes/(adminLayout)/admin/gallery/[id]/+page.server.ts deleted file mode 100644 index 5a85463..0000000 --- a/src/routes/(adminLayout)/admin/gallery/[id]/+page.server.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Server } from '$/lib/server/server'; -import type { PageServerLoad } from './$types'; - -export const load = (async (ev) => { - return { - equipment: await Server.ssr.equipment.GET(ev), - data: await Server.ssr.gallery.single(ev, parseInt(ev.params.id)) - }; -}) satisfies PageServerLoad; diff --git a/src/routes/(adminLayout)/admin/gallery/[id]/+page.svelte b/src/routes/(adminLayout)/admin/gallery/[id]/+page.svelte deleted file mode 100644 index fdaa3aa..0000000 --- a/src/routes/(adminLayout)/admin/gallery/[id]/+page.svelte +++ /dev/null @@ -1,27 +0,0 @@ - - - diff --git a/src/routes/(adminLayout)/admin/gallery/new/+page.server.ts b/src/routes/(adminLayout)/admin/gallery/new/+page.server.ts deleted file mode 100644 index 83e911d..0000000 --- a/src/routes/(adminLayout)/admin/gallery/new/+page.server.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Server } from '$/lib/server/server'; -import type { PageServerLoad } from './$types'; - -export const load = (async (ev) => { - return { - equipment: await Server.ssr.equipment.GET(ev), - details: await Server.ssr.details.GET(ev) - }; -}) satisfies PageServerLoad; diff --git a/src/routes/(adminLayout)/admin/gallery/new/+page.svelte b/src/routes/(adminLayout)/admin/gallery/new/+page.svelte deleted file mode 100644 index 185a333..0000000 --- a/src/routes/(adminLayout)/admin/gallery/new/+page.svelte +++ /dev/null @@ -1,18 +0,0 @@ - - - diff --git a/src/routes/(adminLayout)/admin/main/+page.server.ts b/src/routes/(adminLayout)/admin/main/+page.server.ts deleted file mode 100644 index 0a277dc..0000000 --- a/src/routes/(adminLayout)/admin/main/+page.server.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { Server } from '$/lib/server/server'; -import type { PageServerLoad } from './$types'; - -export const load = (async (event) => { - const request = await event.fetch('https://api.thecatapi.com/v1/images/search'); - const json = await request.json(); - - return { - //SELECT COUNT(*), page FROM visitors WHERE DATE(date) = CURDATE() GROUP BY page; - - today: await Server.ssr.stats.getToday(event), - thisWeek: await Server.ssr.stats.getWeek(event), - yearData: await Server.ssr.stats.weekGraph.GET(event), - cat: json?.[0].url - }; -}) satisfies PageServerLoad; diff --git a/src/routes/(adminLayout)/admin/main/+page.svelte b/src/routes/(adminLayout)/admin/main/+page.svelte deleted file mode 100644 index 4816d6d..0000000 --- a/src/routes/(adminLayout)/admin/main/+page.svelte +++ /dev/null @@ -1,89 +0,0 @@ - - -
-
-

Statistiky

-

Unikátní návštěvníci dnes: {today}

-

Unikátní návštěvníci tento týden: {thisWeek}

-
- -
-
-
- {#if data.cat} - Random cat :) - {:else} - No cat found :( - {/if} -
-
diff --git a/src/routes/(adminLayout)/admin/projects/+page.server.ts b/src/routes/(adminLayout)/admin/projects/+page.server.ts deleted file mode 100644 index f5fabde..0000000 --- a/src/routes/(adminLayout)/admin/projects/+page.server.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { Server } from '$/lib/server/server'; -import type { PageServerLoad } from './$types'; - -export const load = (async (ev) => { - return { - projects: await Server.ssr.project.list(ev) - }; -}) satisfies PageServerLoad; diff --git a/src/routes/(adminLayout)/admin/projects/+page.svelte b/src/routes/(adminLayout)/admin/projects/+page.svelte deleted file mode 100644 index dc6b64e..0000000 --- a/src/routes/(adminLayout)/admin/projects/+page.svelte +++ /dev/null @@ -1,52 +0,0 @@ - - -
- {#if !projects} - Načítání... - {:else} - - {#if projects.length == 0} - Nebyly nalezeny žádné projekty, přidej nějaký. - {:else} - {#each projects as project} - - {/each} - {/if} - {/if} -
diff --git a/src/routes/(adminLayout)/admin/projects/[uuid]/+page.server.ts b/src/routes/(adminLayout)/admin/projects/[uuid]/+page.server.ts deleted file mode 100644 index 49f6b6a..0000000 --- a/src/routes/(adminLayout)/admin/projects/[uuid]/+page.server.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Server } from '$/lib/server/server'; -import { conn } from '$/lib/server/variables'; -import { redirect } from '@sveltejs/kit'; -import type { PageServerLoad } from './$types'; - -export const load = (async (ev) => { - const uuid = ev.params.uuid; - - const data = await conn.selectFrom('project').select('uuid').where('uuid', '=', uuid).executeTakeFirst(); - - if (!data) { - throw redirect(302, '/admin/projects'); - } - - return { - project: await Server.ssr.project.get.POST(ev, { uuid }), - tags: await Server.ssr.tag.GET(ev) - }; -}) satisfies PageServerLoad; diff --git a/src/routes/(adminLayout)/admin/projects/[uuid]/+page.svelte b/src/routes/(adminLayout)/admin/projects/[uuid]/+page.svelte deleted file mode 100644 index 784928a..0000000 --- a/src/routes/(adminLayout)/admin/projects/[uuid]/+page.svelte +++ /dev/null @@ -1,358 +0,0 @@ - - -
- {#if !projectData} - Načítání... - {:else} - - project preview - - - - - - - - - - - - - - - - - - - - - {#if unassignedTags.length == 0} - Nejsou žádné jiné tagy k přiřazení - {:else} - {#each unassignedTags as tag} - {tag.name} - {/each} - {/if} - - - - - - - {#if assignedTags.length == 0} - Nejsou žádné jiné tagy k přiřazení - {:else} - {#each assignedTags as tag} - {tag.name} - {/each} - {/if} - - - - - - + {:else} + + {#snippet right()} + (showPreview = false)} + /> + {/snippet} + + {/if} + + + {@render subTitle(_lang.equipment.title)} + + + {#if equipment.length === 0} +

{_lang.equipment.empty}

+ {:else} +
+ {#each data.equipment.filter( (item) => equipment.includes(item.id) ) as item (item.id)} +
+ {item.name} + + (equipment = equipment.filter((_item) => _item !== item.id))} + name="bi-trash-fill" + class="cursor-pointer text-red-500" + /> +
+ {/each} +
+ {/if} +
+ + {@render subTitle(_lang.images.title)} + + + + {@render subTitle(_lang.exposures.title)} +
+ + + + + + + + + + + + +
+ + { + ev.preventDefault(); + addExposure(); + }} + > + {_lang.exposures.button} + + +
+ + + + + + + + + + + + + + {#if exposures.length === 0} + + + + {:else} + {#each exposures as exposure, idx (`${exposure.date.toISOString()}-${exposure.type}-${exposure.count}-${exposure.exposure_time_s}`)} + + + + + + + + + {/each} + {/if} + +
{_lang.exposures.date}{_lang.exposures.type}{_lang.exposures.count}{_lang.exposures.time}{_lang.exposures.total}
{_lang.exposures.empty}
{formatDate(exposure.date, false)}{exposure.type}{exposure.count}{exposure.exposure_time_s}{exposure.count * exposure.exposure_time_s} + + (exposures = exposures.filter((_, _idx) => _idx !== idx))} + name="bi-trash-fill" + class="cursor-pointer text-2xl text-red-500" + /> +
+
+ +
+ {#each ['light', 'dark', 'flat', 'bias'] as const as type (type)} +
+ {_state.lang.frames[type]} {_lang.exposures.frames} + + {exposures + .filter((ex) => ex.type === type) + .reduce((acc, ex) => acc + ex.count * ex.exposure_time_s, 0)}s + +
+ {/each} +
+
+
+ goto(`/${_state.selectedLang}/admin/article`)}> + {_lang.cancel} + + +
+ +
diff --git a/src/routes/[[lang=lang]]/admin/equipment/+page.server.ts b/src/routes/[[lang=lang]]/admin/equipment/+page.server.ts new file mode 100644 index 0000000..4b141aa --- /dev/null +++ b/src/routes/[[lang=lang]]/admin/equipment/+page.server.ts @@ -0,0 +1,18 @@ +import { conn } from '$/lib/server/variables'; +import type { Actions } from '@sveltejs/kit'; +import type { PageServerLoad } from './$types'; +import { Server } from '$/lib/server/server'; + +export const load = (async () => { + return { + types: await conn.selectFrom('equipment_type').selectAll().execute(), + equipment: await conn.selectFrom('equipment').selectAll().execute() + }; +}) satisfies PageServerLoad; + +export const actions = { + typeAdd: Server.actions.types.POST, + typeEdit: Server.actions.types.PATCH, + equipmentAdd: Server.actions.equipment.POST, + equipmentEdit: Server.actions.equipment.PATCH +} satisfies Actions; diff --git a/src/routes/[[lang=lang]]/admin/equipment/+page.svelte b/src/routes/[[lang=lang]]/admin/equipment/+page.svelte new file mode 100644 index 0000000..b3c45d3 --- /dev/null +++ b/src/routes/[[lang=lang]]/admin/equipment/+page.svelte @@ -0,0 +1,404 @@ + + +{#snippet title(text: string)} +

{text}

+{/snippet} + +{#snippet info(text: string)} + {text} +{/snippet} + +{#snippet _switch(text: string, name: typeof section)} + +{/snippet} + + (typeEditing = null)}> + {@render title(replacePlaceholders(_lang.types.edit.title, typeEditing!.id.toString()))} +
(typeEditing = null) + )} + > + + + + + + + + +
+
+ + (equipmentEditing = null)}> + {@render title( + replacePlaceholders(_lang.equipment.edit.title, equipmentEditing!.id.toString()) + )} +
(equipmentEditing = null) + )} + > + + + + + + + + + + + +
+
+ + + {@render title(_lang.types.addTitle)} +
(openTypeAdd = false))} + > + + + + + + + +
+
+ + + {@render title(_lang.equipment.addTitle)} +
(openEquipmentAdd = false) + )} + > + + + + + + + + + + +
+
+ +
+
+ {@render _switch(_lang.types.title, 'types')} + {@render _switch(_lang.equipment.title, 'equipment')} +
+ + {#if section === 'types'} +
+
+ {@render title(_lang.types.title)} + +
+ {#if data.types.length === 0} + {@render info(_lang.types.empty)} + {:else} +
+ + + + + + + + + + + {#each data.types as type (type.id.toString())} + + + + + + + {/each} + +
Id{_lang.types.translateKey}{_lang.types.priority}{_lang.actions}
{type.id}{type.lang_key}{type.priority} + + (typeEditing = { + id: type.id, + lang_key: type.lang_key || '', + priority: type.priority || 0 + })} + name="bi-pencil-fill" + class="cursor-pointer" + /> + + typeDelete(type.id)} + name="bi-trash-fill" + class="cursor-pointer text-red-500" + /> +
+
+ {/if} +
+ {:else} +
+
+ {@render title(_lang.equipment.title)} + +
+ {#if data.equipment.length === 0} + {@render info(_lang.equipment.empty)} + {:else} +
+ + + + + + + + + + + + {#each data.equipment as equipment (equipment.id.toString())} + + + + + + + + {/each} + +
Id{_lang.equipment.name}{_lang.equipment.type}{_lang.equipment.link}{_lang.actions}
{equipment.id}{equipment.name}{resolveTranslation( + data.types.find((type) => type.id === equipment.type_id)!.lang_key, + _state.lang + )}{equipment.link} + { + equipmentEditing = { + id: equipment.id, + name: equipment.name, + type_id: equipment.type_id, + link: equipment.link + }; + }} + name="bi-pencil-fill" + class="cursor-pointer" + /> + + equipmentDelete(equipment.id)} + name="bi-trash-fill" + class="cursor-pointer text-red-500" + /> +
+
+ {/if} +
+ {/if} +
diff --git a/src/routes/[[lang=lang]]/contact/+page.svelte b/src/routes/[[lang=lang]]/contact/+page.svelte new file mode 100644 index 0000000..8914f94 --- /dev/null +++ b/src/routes/[[lang=lang]]/contact/+page.svelte @@ -0,0 +1,105 @@ + + +
+

{lang.title}

+
+ {#each platforms as platform (`${platform.name}-${platform.tag}`)} + + +

{platform.name}

+
+ {platform.tag} +
+

+ {resolveTranslation(platform.descriptionKey, _state.lang)} +

+
+ {/each} +
+
diff --git a/src/routes/[[lang=lang]]/gallery/+page.server.ts b/src/routes/[[lang=lang]]/gallery/+page.server.ts new file mode 100644 index 0000000..14deab8 --- /dev/null +++ b/src/routes/[[lang=lang]]/gallery/+page.server.ts @@ -0,0 +1,42 @@ +import { gatherTranslations } from '$/lib/server/functions'; +import { conn } from '$/lib/server/variables'; +import type { PageServerLoad } from './$types'; + +export const load = (async ({ parent }) => { + const parentData = await parent(); + + const posts = await conn + .selectFrom('article') + .select(['id', 'title', 'description', 'created_at', 'updated_at']) + .orderBy('created_at', 'desc') + .execute(); + + const postsEquipment = await conn + .selectFrom('article_equipment') + .innerJoin('equipment', 'article_equipment.equipment_id', 'equipment.id') + .innerJoin('equipment_type', 'equipment.type_id', 'equipment_type.id') + .selectAll(['equipment_type', 'equipment']) + .select('article_id') + .execute(); + + const images = await conn.selectFrom('gallery_image').selectAll().execute(); + const exposures = await conn.selectFrom('exposure').selectAll().execute(); + + return { + posts: posts.map((post) => ({ + ...post, + equipment: postsEquipment + .filter((eq) => eq.article_id === post.id) + .sort((a, b) => b.priority - a.priority), + images: images.filter((image) => image.article_id === post.id), + exposures: exposures.filter((exposure) => exposure.article_id === post.id) + })), + dynamicTranslations: await gatherTranslations( + [ + ...posts.map((post) => [post.title, post.description]).flat(), + ...images.map((image) => image.alt_text) + ], + parentData.selectedLang + ) + }; +}) satisfies PageServerLoad; diff --git a/src/routes/[[lang=lang]]/gallery/+page.svelte b/src/routes/[[lang=lang]]/gallery/+page.svelte new file mode 100644 index 0000000..6ab316f --- /dev/null +++ b/src/routes/[[lang=lang]]/gallery/+page.svelte @@ -0,0 +1,78 @@ + + +{#snippet badge(text: string)} +
+ {text} +
+{/snippet} + +
+

{_lang.title}

+ +
diff --git a/src/routes/[[lang=lang]]/gallery/[id]/+page.server.ts b/src/routes/[[lang=lang]]/gallery/[id]/+page.server.ts new file mode 100644 index 0000000..ec2e0b1 --- /dev/null +++ b/src/routes/[[lang=lang]]/gallery/[id]/+page.server.ts @@ -0,0 +1,54 @@ +import { gatherTranslations, redirect } from '$/lib/server/functions'; +import { conn } from '$/lib/server/variables'; +import type { PageServerLoad } from './$types'; + +export const load = (async ({ params, parent }) => { + const parentData = await parent(); + + const post = await conn + .selectFrom('article') + .selectAll() + .where('id', '=', params.id) + .executeTakeFirst(); + if (!post) { + return redirect(302, '/gallery'); + } + + const images = await conn + .selectFrom('gallery_image') + .selectAll() + .where('article_id', '=', post.id) + .orderBy('id', 'asc') + .execute(); + const exposures = await conn + .selectFrom('exposure') + .selectAll() + .where('article_id', '=', post.id) + .orderBy('date', 'asc') + .execute(); + const equipment = await conn + .selectFrom('article_equipment') + .innerJoin('equipment', 'equipment_id', 'equipment.id') + .innerJoin('equipment_type', 'type_id', 'equipment_type.id') + .selectAll() + .where('article_id', '=', post.id) + .execute(); + + return { + post: { + ...post, + images, + exposures, + equipment + }, + dynamicTranslations: await gatherTranslations( + [ + post.title, + post.description, + post.content_md, + ...images.map((image) => image.alt_text) + ], + parentData.selectedLang + ) + }; +}) satisfies PageServerLoad; diff --git a/src/routes/[[lang=lang]]/gallery/[id]/+page.svelte b/src/routes/[[lang=lang]]/gallery/[id]/+page.svelte new file mode 100644 index 0000000..ab4516f --- /dev/null +++ b/src/routes/[[lang=lang]]/gallery/[id]/+page.svelte @@ -0,0 +1,238 @@ + + +
+ {_lang.back} +

{_langDynamic[data.post.title]}

+
+
+ + {_lang.created} + {formatDate(data.post.created_at, false)} +
+
+ + {_lang.updated} + {formatDate(data.post.updated_at, false)} +
+
+ + {_lang.totalExposure}: {sToHHMM( + data.post.exposures + .filter((ex) => ex.type === 'light') + .reduce((acc, ex) => acc + ex.count * ex.exposure_time_s, 0) + )} +
+
+
+
+
+
+ {_langDynamic[data.post.images[selectedImage].alt_text]} + {#if data.post.images.length > 1} +
+ + +
+ {/if} +
+ {_langDynamic[data.post.images[selectedImage].alt_text]} + + {#if data.post.images.length > 1} + + {/if} +
+ +
+

{_lang.equipment}

+ +
+ +
+

{_lang.exposureSummary}

+
+ {#each ['light', 'dark', 'bias', 'flat'] as const as type (type)} + {@const filtered = data.post.exposures.filter((ex) => ex.type === type)} + {@const count = filtered.reduce((acc, ex) => acc + ex.count, 0)} +
+ {_state.lang.frames[type]} {_frames.frames} +

{sToHHMM( + filtered.reduce((acc, ex) => acc + ex.count * ex.exposure_time_s, 0) + )}

+ {count} {resolveLanguagable(_lang.framesCount, count)} +
+ {/each} +
+
+
+
+
+ + +
+ {#if section === 'article'} + +
+

{_lang.images}

+
+ {#each data.post.images as image (image.id)} + + {_langDynamic[image.alt_text]} + + {_langDynamic[image.alt_text]} + + + {/each} +
+ {:else} +
+

{_lang.exposureDetails}

+
+ + + + + + + + + + + + {#each data.post.exposures as exposure (exposure.id)} + + + + + + + + {/each} + +
{_frames.date}{_frames.type}{_frames.count}{_frames.seconds}{_frames.total}
{formatDate(exposure.date, false)}{_state.lang.frames[exposure.type as Frame]}{exposure.count}{exposure.exposure_time_s}{exposure.count * exposure.exposure_time_s}
+
+
+
+

{_lang.equipmentDetails}

+
+ {#each data.post.equipment as equipment (equipment.name)} +
+

{equipment.name}

+ {resolveTranslation(equipment.lang_key, _state.lang)} + +
+ {/each} +
+
+ {/if} +
+
+
diff --git a/src/routes/[[lang=lang]]/login/+page.server.ts b/src/routes/[[lang=lang]]/login/+page.server.ts new file mode 100644 index 0000000..08cba46 --- /dev/null +++ b/src/routes/[[lang=lang]]/login/+page.server.ts @@ -0,0 +1,19 @@ +import { Server } from '$/lib/server/server'; +import { type Actions, redirect as _redirect } from '@sveltejs/kit'; +import type { PageServerLoad } from './$types'; +import { getUserState, redirect } from '$/lib/server/functions'; + +export const load = (async ({ cookies, url }) => { + const userState = getUserState(cookies); + if (userState.logged) { + if (url.searchParams.get('next')) { + _redirect(302, url.searchParams.get('next')!); + } else { + redirect(302, '/admin'); + } + } +}) satisfies PageServerLoad; + +export const actions = { + default: Server.actions.login +} satisfies Actions; diff --git a/src/routes/[[lang=lang]]/login/+page.svelte b/src/routes/[[lang=lang]]/login/+page.svelte new file mode 100644 index 0000000..75028cc --- /dev/null +++ b/src/routes/[[lang=lang]]/login/+page.svelte @@ -0,0 +1,52 @@ + + +
+

{_state.lang.admin.login.title}

+ + + + +
diff --git a/src/routes/about/+page.svelte b/src/routes/about/+page.svelte deleted file mode 100644 index 8bd12fd..0000000 --- a/src/routes/about/+page.svelte +++ /dev/null @@ -1,113 +0,0 @@ - - -

- Zde nalezneš můj příběh, jak jsem se dostal k programování úplně od začátku seřazený chronologicky od nejnovějšího k nejstaršímu. -

- - - Po stření škole jsem se dal na Vysokou Školu Báňskou, přesněji - Fakultu Elektrotechniky a Informatiky - obor Informatika. Osobně jsem na Vysokou školu šel s tím cílem 1. získat titul a 2. se přiučit nové věci ohledně technologíí, které bych se sám nikdy nenaučil. Například - jsem se díky programovacímu jazyku C naučil lépe porozumět jak program pracuje s pamětí, jak jsou v ní data reprezentovaná a jak s ní správně nacházet. Dále jsem se naučil základy - dalších programovacích jazyků, jako je C, C++, C# a Python. Díky předmětu Databázové Systémy jsem se zase naučil líp navrhovat relační databáze. - - - - Mimo tvorbu webových stránek jsem také začal tvořit Discord Boty, mezi první větší discord boty je můj discord bot pro zobrazování statistik ze hry League of Legends - Miňonka - , který aktuálně prochází do nové verze 3. Dále jsem dělal i další discord boty pro Minecraft servery viz projekty. - - - - Ke konci střední jsem se seznámil s prvním fullstack frameworkem Next.JS, kdy jsem měl příležito se přidat k projeklu kamaráda - Skypada - , který pracoval na hostingu. Menší problém byl v tom, že jsem sice něco málo pochopil, ale většinu věcí v Reactu dělal jen copy-paste, takže jsem si moc React - neoblíbil. Na sítích (primárně u streamera Ottomated) jsem ale narazil na framework - SvelteKit - , který oproti Reactu prochází kompilací kódu, tedy z mého pohledu je pak syntaxe kódu hezčí a víc přívětivější. V tomto frameworku jsem poté začal dělat všechno spojené - s webovým vývojem. První projekt, který jsem v něm dělal, kromě mého webu byla wiki pro server - GameRealms. - - - - Během střední školy jsem se postupně začal učit i více JavaScript, nejvíce jsem na něho narazil při tvorbě již zmíněného informačního systému pro minecraft server, kde jsem - používal jQuery a Ajax pro komikaci s API a toho, aby stránka byla více plynulá a nemusela se po každé akci znovu načítat. Dále jsem zavítal i do NodeJS, kde jsem začínal s - tvorbou Discord botů, prvně jsem tvořil Self Bota, který ukazoval statistiky z League of Legends a později ho předělal na normálního Bota. Během střední školy jsem si tvořil - stránky na "ulehčení" některých počtů v chemii, první z nich je v PHP na počítání Q-Testu a druhá na počítání - rovnice z grafu - v JavaScriptu. - - - - Na střední školu jsem chodil do Ostravy Zabřehu na Chemickou průmyslovou školu akademika Heyrovského, tuto školu jsem si zvolil - hlavně z toho důvodu, že mě na základní škole bavila chemie a programování jsem se učil ve volném čase doma, proto jsem nešel na Elektrotechnickou školu a osobně si myslím, že - jsem se toho sám naučil víc, než bych se naučil na střední. Na střední jsem se s PHP setkával již pravidelně, ale vše si dělal sám, bez nějakého frameworku. Vytvořil jsem v něm - jednoduchý systém na vytváření Sinus botů, protože jejich - free verze umožňovala pouze dva boty, takže jsem si přes ní mohl vytvářet nové "boty", což se prakticky připojilo na SSH, zkopírovalo zip do nový složky, v configu upravilo - port a poté se přes danou administraci dali jednotlivé instance vypínat, zapínat, nebo restartovat. Poté jsem minecraft serveru SurprisePlay.eu vytvořil informační systém, kde hráči mohli psát Tickety, admin team jim mohl odpovídat, dále si - admin team mohl psát věci do Todo listu, mohli přes něho upravovat permise, přidávat hráčům prémiovou měnu a mnoho dalšího. (Screenshoty z něho lze naléz v kategorii Projekty) - - - - V 8. třídě na základní škole jsem se začal zajímat o tvorbu webových stránek. Začal jsem tvořit stránky přes WYSIWIG Web Builder, ale brzy jsem zjistil, že to není ono, - něco málo jsem zkoušel tvořit i ve Wordu, kde to ale vyplivlo do HTML souboru hromadu zbytečných tagů a obrázky se tam zobrazovaly špatně. V tento moment jsem se začal postupně - učit HTML a CSS, s tím že jsem si na svoje stránky stáhnul nějaký základní AUTH systém, kdy se dokáži přihlásit a poté z PHP session zjišťovat, kdo je přihlášený. Tento systém - jsem začal zkoumat a zjišťovat si více věci o PHP. - diff --git a/src/routes/admin/+page.server.ts b/src/routes/admin/+page.server.ts deleted file mode 100644 index 9256c89..0000000 --- a/src/routes/admin/+page.server.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { jwt } from '$/lib/server/variables'; -import { redirect } from '@sveltejs/kit'; -import type { PageServerLoad } from './$types'; - -export const load = (async ({ cookies }) => { - const session = cookies.get('session'); - - if (session) { - const data = jwt.getCookie(session); - - if (data) { - throw redirect(302, '/admin/main'); - } - } -}) satisfies PageServerLoad; diff --git a/src/routes/admin/+page.svelte b/src/routes/admin/+page.svelte deleted file mode 100644 index 0688433..0000000 --- a/src/routes/admin/+page.svelte +++ /dev/null @@ -1,52 +0,0 @@ - - -

Vítej na TOP secret přihlašovací stránce :)

-
- - - - - -
diff --git a/src/routes/api/[...data]/+server.ts b/src/routes/api/[...data]/+server.ts index 181feb8..42138c3 100644 --- a/src/routes/api/[...data]/+server.ts +++ b/src/routes/api/[...data]/+server.ts @@ -3,5 +3,5 @@ import { Server } from '$/lib/server/server'; export const GET = Server.handler; export const POST = Server.handler; export const PUT = Server.handler; -export const PATCH = Server.handler; export const DELETE = Server.handler; +export const PATCH = Server.handler; diff --git a/src/routes/contact/+page.svelte b/src/routes/contact/+page.svelte deleted file mode 100644 index 6dc08e3..0000000 --- a/src/routes/contact/+page.svelte +++ /dev/null @@ -1,62 +0,0 @@ - - -
-

Tady mě všude najdeš.

-
- {#each contacts as contact} - - - - {/each} -
-
diff --git a/src/routes/customImages/[...name]/+server.ts b/src/routes/customImages/[...name]/+server.ts deleted file mode 100644 index 29fa104..0000000 --- a/src/routes/customImages/[...name]/+server.ts +++ /dev/null @@ -1,114 +0,0 @@ -import { error, type RequestHandler } from '@sveltejs/kit'; -import fs from 'fs'; -import path from 'path'; -import sharp from 'sharp'; - -const formats = ['jpg', 'jpeg', 'png', 'webp', 'tiff']; - -export const GET = (async ({ params, setHeaders, url }) => { - if (params.name == null) { - throw error(404, 'Not found'); - } - - const fileName = params.name; - - let filePath = path.join('projects', fileName); - - if (!fs.existsSync(filePath)) { - throw error(404, 'Not found'); - } - - let file = fs.readFileSync(filePath); - - const searchParams = url.searchParams; - let fileExtension = path.extname(filePath); - let modified = false; - let scale = 100; - - if (searchParams.has('format')) { - const format = searchParams.get('format')!; - if (!formats.includes(format)) { - throw error(400, 'Bad request'); - } - - fileExtension = format; - modified = true; - } - - if (searchParams.has('scale')) { - const downscale = searchParams.get('scale')!; - try { - const downScale = parseInt(downscale); - - if (downScale > 100 || downScale < 0) { - throw Error('Invalid downscale value'); - } - - modified = true; - scale = downScale; - } catch (_) { - throw error(400, 'Bad request'); - } - } - - if (modified) { - if (!fs.existsSync('.cache')) { - fs.mkdirSync('.cache'); - } - - const cacheModifiedName = `${path.basename(fileName)}.scale-${scale}.${fileExtension}`; - const cachePath = path.join('.cache', cacheModifiedName); - - if (!fs.existsSync(cachePath)) { - let image = sharp(file); - - const imageOptions: sharp.JpegOptions & sharp.PngOptions & sharp.WebpOptions & sharp.TiffOptions = { - quality: 75 - }; - - switch (fileExtension) { - case 'jpeg': - case 'jpg': - image = image.jpeg(imageOptions); - break; - case 'png': - image = image.png(imageOptions); - break; - case 'webp': - image = image.webp(imageOptions); - break; - case 'tiff': - image = image.tiff(imageOptions); - break; - } - - const meta = await image.metadata(); - - const newWidth = meta.width ? Math.round(meta.width * (scale / 100)) : undefined; - const newHeight = meta.height ? Math.round(meta.height * (scale / 100)) : undefined; - - image = image.resize({ - width: newWidth, - height: newHeight - }); - - const imageBuffer = await image.toBuffer(); - - fs.writeFileSync(cachePath, imageBuffer); - } - - file = fs.readFileSync(cachePath); - filePath = cachePath; - } - - const fileInfo = fs.statSync(filePath); - - setHeaders({ - 'Content-Type': 'image/' + fileExtension.startsWith('.') ? fileExtension.slice(1) : fileExtension, - 'Content-Length': fileInfo.size.toString(), - 'Last-Modified': fileInfo.mtime.toUTCString(), - 'Cache-Control': 'public, max-age=86400' - }); - - return new Response(file); -}) satisfies RequestHandler; diff --git a/src/routes/gallery/+page.server.ts b/src/routes/gallery/+page.server.ts deleted file mode 100644 index c42dfb4..0000000 --- a/src/routes/gallery/+page.server.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { Server } from '$/lib/server/server'; -import type { PageServerLoad } from './$types'; - -export const load = (async (ev) => { - return { - images: await Server.ssr.gallery.GET(ev) - }; -}) satisfies PageServerLoad; diff --git a/src/routes/gallery/+page.svelte b/src/routes/gallery/+page.svelte deleted file mode 100644 index 28fa4bc..0000000 --- a/src/routes/gallery/+page.svelte +++ /dev/null @@ -1,49 +0,0 @@ - - -
-

Galerie z mého koníčku astrofotografie.

-
-
- {#if !images} - Načítání obrázků... - {:else if images.length == 0} - Žádné obrázky nenalezeny. - {:else} - {#each images as image} - - {/each} - {/if} -
-
-
diff --git a/src/routes/image/[name]/+server.ts b/src/routes/image/[name]/+server.ts new file mode 100644 index 0000000..cf9237f --- /dev/null +++ b/src/routes/image/[name]/+server.ts @@ -0,0 +1,241 @@ +import { error, type RequestHandler } from '@sveltejs/kit'; +import Path from 'node:path'; +import fs from 'node:fs/promises'; +import { createReadStream } from 'node:fs'; +import { Readable } from 'node:stream'; +import { FILE_FOLDER } from '$env/static/private'; +import sharp from 'sharp'; +import { isDirectory, isFile } from '$/lib/server/functions'; +import { extensions, type ImageExtension } from '$/types/types'; + +const CACHE_FOLDER = '.cache'; +const DEFAULT_IMAGE_QUALITY = 75; + +// Long cache duration - 1 year in seconds +const CACHE_MAX_AGE = 31536000; + +type CacheEntry = { + buffer: Buffer; + timestamp: number; +}; + +class MemoryCache { + private cache: Map = new Map(); + private maxEntries = 500; + private maxSize = 200 * 1024 * 1024; // 200 MB + + private getCacheSize(): number { + let size = 0; + for (const value of this.cache.values()) { + size += value.buffer.length; + } + return size; + } + + get(key: string) { + const entry = this.cache.get(key); + if (!entry) return undefined; + + entry.timestamp = Date.now(); + return entry.buffer; + } + + set(key: string, value: Buffer) { + if ( + this.getCacheSize() + value.length > this.maxSize || + this.cache.size >= this.maxEntries + ) { + const oldest = [...this.cache.entries()].sort( + (a, b) => a[1].timestamp - b[1].timestamp + )[0]; + if (oldest) { + this.cache.delete(oldest[0]); + } + } + + this.cache.set(key, { + buffer: value, + timestamp: Date.now() + }); + } +} + +const memoryCache = new MemoryCache(); + +/** + * Build common cache headers for image responses + */ +function getCacheHeaders(fileExtension: string, contentLength: number, etag: string) { + return { + 'Content-Type': `image/${fileExtension}`, + 'Content-Length': contentLength.toString(), + 'Cache-Control': `public, max-age=${CACHE_MAX_AGE}, immutable`, + ETag: etag, + Vary: 'Accept' + }; +} + +/** + * Convert Node.js readable stream to web ReadableStream + */ +function nodeStreamToWebStream(nodeStream: Readable): ReadableStream { + return new ReadableStream({ + start(controller) { + nodeStream.on('data', (chunk: Buffer) => { + controller.enqueue(new Uint8Array(chunk)); + }); + nodeStream.on('end', () => { + controller.close(); + }); + nodeStream.on('error', (err) => { + controller.error(err); + }); + }, + cancel() { + nodeStream.destroy(); + } + }); +} + +export const GET = (async ({ params, url, request }) => { + if (!params.name) { + error(400, 'Name is required'); + } + + if (params.name.includes('..')) { + error(400, 'Bad request'); + } + + const parsed = Path.parse(params.name); + if (!extensions.includes(parsed.ext.substring(1) as ImageExtension)) { + error(404, 'File not found'); + } + + if (!(await isDirectory(FILE_FOLDER))) { + await fs.mkdir(FILE_FOLDER, { recursive: true }); + } + + let filePath = Path.join(FILE_FOLDER, params.name); + + if (!(await isFile(filePath))) { + error(404, 'File not found'); + } + + const searchParams = url.searchParams; + let fileExtension = Path.extname(filePath).substring(1).toLowerCase(); + let modified = false; + let scale = 100; + + if (searchParams.has('format')) { + const format = searchParams.get('format')!; + if (!extensions.includes(format as ImageExtension)) { + error(400, 'Bad request'); + } + + fileExtension = format; + modified = true; + } + + if (searchParams.has('scale')) { + const downscale = searchParams.get('scale')!; + try { + const downScale = parseInt(downscale); + if (downScale > 100 || downScale < 0) { + error(400, 'Bad request'); + } + + modified = true; + scale = downScale; + //eslint-disable-next-line @typescript-eslint/no-unused-vars + } catch (_) { + error(400, 'Bad request'); + } + } + + if (modified) { + if (!(await isDirectory(CACHE_FOLDER))) { + await fs.mkdir(CACHE_FOLDER); + } + + const cacheModifiedName = `${Path.basename(params.name)}.scale-${scale}.${fileExtension}`; + const cachePath = Path.join(CACHE_FOLDER, cacheModifiedName); + + // Check if we need to generate the cached version + if (!(await isFile(cachePath))) { + const originalContent = await fs.readFile(filePath); + let image = sharp(originalContent); + + const imageOptions: sharp.JpegOptions & + sharp.PngOptions & + sharp.WebpOptions & + sharp.TiffOptions = { + quality: DEFAULT_IMAGE_QUALITY + }; + + switch (fileExtension) { + case 'jpeg': + case 'jpg': + image = image.jpeg(imageOptions); + break; + case 'png': + image = image.png(imageOptions); + break; + case 'webp': + image = image.webp(imageOptions); + break; + case 'tiff': + image = image.tiff(imageOptions); + break; + } + + const meta = await image.metadata(); + const newWidth = meta.width ? Math.round(meta.width * (scale / 100)) : undefined; + const newHeight = meta.height ? Math.round(meta.height * (scale / 100)) : undefined; + image = image.resize(newWidth, newHeight); + + const imageBuffer = await image.toBuffer(); + await fs.writeFile(cachePath, imageBuffer); + memoryCache.set(cachePath, imageBuffer); + } + + filePath = cachePath; + } + + // Get file info for ETag and Content-Length + const fileInfo = await fs.stat(filePath); + const etag = `"${fileInfo.mtime.getTime().toString(16)}-${fileInfo.size.toString(16)}"`; + + // Check for conditional request (If-None-Match) + const ifNoneMatch = request.headers.get('if-none-match'); + if (ifNoneMatch === etag) { + return new Response(null, { + status: 304, + headers: getCacheHeaders(fileExtension, fileInfo.size, etag) + }); + } + + // Check memory cache first + const cachedContent = memoryCache.get(filePath); + if (cachedContent) { + return new Response(cachedContent, { + headers: getCacheHeaders(fileExtension, cachedContent.length, etag) + }); + } + + // Stream the file for non-cached content + const fileStream = createReadStream(filePath); + const webStream = nodeStreamToWebStream(fileStream); + + // Read and cache the file in the background for future requests + fs.readFile(filePath) + .then((buffer) => { + memoryCache.set(filePath, buffer); + }) + .catch(() => { + // Silently ignore background caching errors - the file was already streamed successfully + }); + + return new Response(webStream, { + headers: getCacheHeaders(fileExtension, fileInfo.size, etag) + }); +}) satisfies RequestHandler; diff --git a/src/routes/projects/+page.server.ts b/src/routes/projects/+page.server.ts deleted file mode 100644 index 82dab17..0000000 --- a/src/routes/projects/+page.server.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Server } from '$/lib/server/server'; -import type { PageServerLoad } from './$types'; - -export const load = (async (ev) => { - return { - projects: await Server.ssr.projects.GET(ev), - tags: await Server.ssr.tags(ev) - }; -}) satisfies PageServerLoad; diff --git a/src/routes/projects/+page.svelte b/src/routes/projects/+page.svelte deleted file mode 100644 index dc29f35..0000000 --- a/src/routes/projects/+page.svelte +++ /dev/null @@ -1,75 +0,0 @@ - - -Kliknutím na tag můžeš podle něho filtrovat projekty -
- {#if !tags} - Nepovedlo se načíst tagy :/ - {:else} - {#each tags as tag} - selectTag(tag.id)} class={twMerge('border-2 border-transparent', tag.id == selectedTag ? 'border-text' : '')} color={tag.color}>{tag.name} - {/each} - {/if} -
- -
- {#if !projects} - Někde nastala chyba :/ - {:else if projects?.length == 0} - Nic tady není :( - {:else} - {#each projects.filter((project) => (selectedTag ? project.tags.map((tag) => tag.id).includes(selectedTag) : true)) as project} - - {/each} - {/if} -
diff --git a/src/routes/projects/[uuid]/+page.server.ts b/src/routes/projects/[uuid]/+page.server.ts deleted file mode 100644 index a28a8dd..0000000 --- a/src/routes/projects/[uuid]/+page.server.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Server } from '$/lib/server/server'; -import { conn } from '$/lib/server/variables'; -import { redirect } from '@sveltejs/kit'; -import type { PageServerLoad } from './$types'; - -export const load = (async (ev) => { - const data = await conn.selectFrom('project').select('uuid').where('uuid', '=', ev.params.uuid).executeTakeFirst(); - - if (!data) { - throw redirect(302, '/projects'); - } - - return { - project: await Server.ssr.projects.POST(ev, { - uuid: ev.params.uuid - }) - }; -}) satisfies PageServerLoad; diff --git a/src/routes/projects/[uuid]/+page.svelte b/src/routes/projects/[uuid]/+page.svelte deleted file mode 100644 index bf10db1..0000000 --- a/src/routes/projects/[uuid]/+page.svelte +++ /dev/null @@ -1,77 +0,0 @@ - - -
- - {#if !projectData} - Někde nastala chyba :( - {:else} -
-

{projectData.name}

-

{projectData.date.toLocaleDateString()}

-
- Project's preview -
- {#each projectData.tags as tag} - {tag.name} - {/each} -
- - - {#if renderMd} -
{@html createSimpleMarkDown(projectData.description)}
- {/if} -
- {#if projectData.images.length > 0} - - -
- {#each projectData.images as image} - - Project's preview - - {/each} -
-
- {/if} - {/if} -
diff --git a/src/routes/sitemap.xml/+server.ts b/src/routes/sitemap.xml/+server.ts deleted file mode 100644 index 449836f..0000000 --- a/src/routes/sitemap.xml/+server.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { ORIGIN } from '$env/static/private'; -import fs from 'node:fs'; -import type { RequestHandler } from './$types'; - -const date = fs.statSync('package.json').atime; - -const urls = ['/', '/projects', '/contact', '/about', '/gallery']; - -export const GET = (({ setHeaders }) => { - setHeaders({ 'Content-Type': 'text/xml' }); - - let XML = - '\n\n'; - - for (const url of urls) { - XML += `${ORIGIN}${url}${date.toISOString()}daily0.7\n`; - } - - XML += ''; - - return new Response(XML); -}) satisfies RequestHandler; diff --git a/src/types/bootstrap_icons.ts b/src/types/bootstrap_icons.ts index b823334..eeeb4c0 100644 --- a/src/types/bootstrap_icons.ts +++ b/src/types/bootstrap_icons.ts @@ -1,2054 +1,2054 @@ //source: node_modules/bootstrap-icons/font/bootstrap-icons.scss type BootstrapIconPart = - | '123' - | 'alarm-fill' - | 'alarm' - | 'align-bottom' - | 'align-center' - | 'align-end' - | 'align-middle' - | 'align-start' - | 'align-top' - | 'alt' - | 'app-indicator' - | 'app' - | 'archive-fill' - | 'archive' - | 'arrow-90deg-down' - | 'arrow-90deg-left' - | 'arrow-90deg-right' - | 'arrow-90deg-up' - | 'arrow-bar-down' - | 'arrow-bar-left' - | 'arrow-bar-right' - | 'arrow-bar-up' - | 'arrow-clockwise' - | 'arrow-counterclockwise' - | 'arrow-down-circle-fill' - | 'arrow-down-circle' - | 'arrow-down-left-circle-fill' - | 'arrow-down-left-circle' - | 'arrow-down-left-square-fill' - | 'arrow-down-left-square' - | 'arrow-down-left' - | 'arrow-down-right-circle-fill' - | 'arrow-down-right-circle' - | 'arrow-down-right-square-fill' - | 'arrow-down-right-square' - | 'arrow-down-right' - | 'arrow-down-short' - | 'arrow-down-square-fill' - | 'arrow-down-square' - | 'arrow-down-up' - | 'arrow-down' - | 'arrow-left-circle-fill' - | 'arrow-left-circle' - | 'arrow-left-right' - | 'arrow-left-short' - | 'arrow-left-square-fill' - | 'arrow-left-square' - | 'arrow-left' - | 'arrow-repeat' - | 'arrow-return-left' - | 'arrow-return-right' - | 'arrow-right-circle-fill' - | 'arrow-right-circle' - | 'arrow-right-short' - | 'arrow-right-square-fill' - | 'arrow-right-square' - | 'arrow-right' - | 'arrow-up-circle-fill' - | 'arrow-up-circle' - | 'arrow-up-left-circle-fill' - | 'arrow-up-left-circle' - | 'arrow-up-left-square-fill' - | 'arrow-up-left-square' - | 'arrow-up-left' - | 'arrow-up-right-circle-fill' - | 'arrow-up-right-circle' - | 'arrow-up-right-square-fill' - | 'arrow-up-right-square' - | 'arrow-up-right' - | 'arrow-up-short' - | 'arrow-up-square-fill' - | 'arrow-up-square' - | 'arrow-up' - | 'arrows-angle-contract' - | 'arrows-angle-expand' - | 'arrows-collapse' - | 'arrows-expand' - | 'arrows-fullscreen' - | 'arrows-move' - | 'aspect-ratio-fill' - | 'aspect-ratio' - | 'asterisk' - | 'at' - | 'award-fill' - | 'award' - | 'back' - | 'backspace-fill' - | 'backspace-reverse-fill' - | 'backspace-reverse' - | 'backspace' - | 'badge-3d-fill' - | 'badge-3d' - | 'badge-4k-fill' - | 'badge-4k' - | 'badge-8k-fill' - | 'badge-8k' - | 'badge-ad-fill' - | 'badge-ad' - | 'badge-ar-fill' - | 'badge-ar' - | 'badge-cc-fill' - | 'badge-cc' - | 'badge-hd-fill' - | 'badge-hd' - | 'badge-tm-fill' - | 'badge-tm' - | 'badge-vo-fill' - | 'badge-vo' - | 'badge-vr-fill' - | 'badge-vr' - | 'badge-wc-fill' - | 'badge-wc' - | 'bag-check-fill' - | 'bag-check' - | 'bag-dash-fill' - | 'bag-dash' - | 'bag-fill' - | 'bag-plus-fill' - | 'bag-plus' - | 'bag-x-fill' - | 'bag-x' - | 'bag' - | 'bar-chart-fill' - | 'bar-chart-line-fill' - | 'bar-chart-line' - | 'bar-chart-steps' - | 'bar-chart' - | 'basket-fill' - | 'basket' - | 'basket2-fill' - | 'basket2' - | 'basket3-fill' - | 'basket3' - | 'battery-charging' - | 'battery-full' - | 'battery-half' - | 'battery' - | 'bell-fill' - | 'bell' - | 'bezier' - | 'bezier2' - | 'bicycle' - | 'binoculars-fill' - | 'binoculars' - | 'blockquote-left' - | 'blockquote-right' - | 'book-fill' - | 'book-half' - | 'book' - | 'bookmark-check-fill' - | 'bookmark-check' - | 'bookmark-dash-fill' - | 'bookmark-dash' - | 'bookmark-fill' - | 'bookmark-heart-fill' - | 'bookmark-heart' - | 'bookmark-plus-fill' - | 'bookmark-plus' - | 'bookmark-star-fill' - | 'bookmark-star' - | 'bookmark-x-fill' - | 'bookmark-x' - | 'bookmark' - | 'bookmarks-fill' - | 'bookmarks' - | 'bookshelf' - | 'bootstrap-fill' - | 'bootstrap-reboot' - | 'bootstrap' - | 'border-all' - | 'border-bottom' - | 'border-center' - | 'border-inner' - | 'border-left' - | 'border-middle' - | 'border-outer' - | 'border-right' - | 'border-style' - | 'border-top' - | 'border-width' - | 'border' - | 'bounding-box-circles' - | 'bounding-box' - | 'box-arrow-down-left' - | 'box-arrow-down-right' - | 'box-arrow-down' - | 'box-arrow-in-down-left' - | 'box-arrow-in-down-right' - | 'box-arrow-in-down' - | 'box-arrow-in-left' - | 'box-arrow-in-right' - | 'box-arrow-in-up-left' - | 'box-arrow-in-up-right' - | 'box-arrow-in-up' - | 'box-arrow-left' - | 'box-arrow-right' - | 'box-arrow-up-left' - | 'box-arrow-up-right' - | 'box-arrow-up' - | 'box-seam' - | 'box' - | 'braces' - | 'bricks' - | 'briefcase-fill' - | 'briefcase' - | 'brightness-alt-high-fill' - | 'brightness-alt-high' - | 'brightness-alt-low-fill' - | 'brightness-alt-low' - | 'brightness-high-fill' - | 'brightness-high' - | 'brightness-low-fill' - | 'brightness-low' - | 'broadcast-pin' - | 'broadcast' - | 'brush-fill' - | 'brush' - | 'bucket-fill' - | 'bucket' - | 'bug-fill' - | 'bug' - | 'building' - | 'bullseye' - | 'calculator-fill' - | 'calculator' - | 'calendar-check-fill' - | 'calendar-check' - | 'calendar-date-fill' - | 'calendar-date' - | 'calendar-day-fill' - | 'calendar-day' - | 'calendar-event-fill' - | 'calendar-event' - | 'calendar-fill' - | 'calendar-minus-fill' - | 'calendar-minus' - | 'calendar-month-fill' - | 'calendar-month' - | 'calendar-plus-fill' - | 'calendar-plus' - | 'calendar-range-fill' - | 'calendar-range' - | 'calendar-week-fill' - | 'calendar-week' - | 'calendar-x-fill' - | 'calendar-x' - | 'calendar' - | 'calendar2-check-fill' - | 'calendar2-check' - | 'calendar2-date-fill' - | 'calendar2-date' - | 'calendar2-day-fill' - | 'calendar2-day' - | 'calendar2-event-fill' - | 'calendar2-event' - | 'calendar2-fill' - | 'calendar2-minus-fill' - | 'calendar2-minus' - | 'calendar2-month-fill' - | 'calendar2-month' - | 'calendar2-plus-fill' - | 'calendar2-plus' - | 'calendar2-range-fill' - | 'calendar2-range' - | 'calendar2-week-fill' - | 'calendar2-week' - | 'calendar2-x-fill' - | 'calendar2-x' - | 'calendar2' - | 'calendar3-event-fill' - | 'calendar3-event' - | 'calendar3-fill' - | 'calendar3-range-fill' - | 'calendar3-range' - | 'calendar3-week-fill' - | 'calendar3-week' - | 'calendar3' - | 'calendar4-event' - | 'calendar4-range' - | 'calendar4-week' - | 'calendar4' - | 'camera-fill' - | 'camera-reels-fill' - | 'camera-reels' - | 'camera-video-fill' - | 'camera-video-off-fill' - | 'camera-video-off' - | 'camera-video' - | 'camera' - | 'camera2' - | 'capslock-fill' - | 'capslock' - | 'card-checklist' - | 'card-heading' - | 'card-image' - | 'card-list' - | 'card-text' - | 'caret-down-fill' - | 'caret-down-square-fill' - | 'caret-down-square' - | 'caret-down' - | 'caret-left-fill' - | 'caret-left-square-fill' - | 'caret-left-square' - | 'caret-left' - | 'caret-right-fill' - | 'caret-right-square-fill' - | 'caret-right-square' - | 'caret-right' - | 'caret-up-fill' - | 'caret-up-square-fill' - | 'caret-up-square' - | 'caret-up' - | 'cart-check-fill' - | 'cart-check' - | 'cart-dash-fill' - | 'cart-dash' - | 'cart-fill' - | 'cart-plus-fill' - | 'cart-plus' - | 'cart-x-fill' - | 'cart-x' - | 'cart' - | 'cart2' - | 'cart3' - | 'cart4' - | 'cash-stack' - | 'cash' - | 'cast' - | 'chat-dots-fill' - | 'chat-dots' - | 'chat-fill' - | 'chat-left-dots-fill' - | 'chat-left-dots' - | 'chat-left-fill' - | 'chat-left-quote-fill' - | 'chat-left-quote' - | 'chat-left-text-fill' - | 'chat-left-text' - | 'chat-left' - | 'chat-quote-fill' - | 'chat-quote' - | 'chat-right-dots-fill' - | 'chat-right-dots' - | 'chat-right-fill' - | 'chat-right-quote-fill' - | 'chat-right-quote' - | 'chat-right-text-fill' - | 'chat-right-text' - | 'chat-right' - | 'chat-square-dots-fill' - | 'chat-square-dots' - | 'chat-square-fill' - | 'chat-square-quote-fill' - | 'chat-square-quote' - | 'chat-square-text-fill' - | 'chat-square-text' - | 'chat-square' - | 'chat-text-fill' - | 'chat-text' - | 'chat' - | 'check-all' - | 'check-circle-fill' - | 'check-circle' - | 'check-square-fill' - | 'check-square' - | 'check' - | 'check2-all' - | 'check2-circle' - | 'check2-square' - | 'check2' - | 'chevron-bar-contract' - | 'chevron-bar-down' - | 'chevron-bar-expand' - | 'chevron-bar-left' - | 'chevron-bar-right' - | 'chevron-bar-up' - | 'chevron-compact-down' - | 'chevron-compact-left' - | 'chevron-compact-right' - | 'chevron-compact-up' - | 'chevron-contract' - | 'chevron-double-down' - | 'chevron-double-left' - | 'chevron-double-right' - | 'chevron-double-up' - | 'chevron-down' - | 'chevron-expand' - | 'chevron-left' - | 'chevron-right' - | 'chevron-up' - | 'circle-fill' - | 'circle-half' - | 'circle-square' - | 'circle' - | 'clipboard-check' - | 'clipboard-data' - | 'clipboard-minus' - | 'clipboard-plus' - | 'clipboard-x' - | 'clipboard' - | 'clock-fill' - | 'clock-history' - | 'clock' - | 'cloud-arrow-down-fill' - | 'cloud-arrow-down' - | 'cloud-arrow-up-fill' - | 'cloud-arrow-up' - | 'cloud-check-fill' - | 'cloud-check' - | 'cloud-download-fill' - | 'cloud-download' - | 'cloud-drizzle-fill' - | 'cloud-drizzle' - | 'cloud-fill' - | 'cloud-fog-fill' - | 'cloud-fog' - | 'cloud-fog2-fill' - | 'cloud-fog2' - | 'cloud-hail-fill' - | 'cloud-hail' - | 'cloud-haze-fill' - | 'cloud-haze' - | 'cloud-haze2-fill' - | 'cloud-lightning-fill' - | 'cloud-lightning-rain-fill' - | 'cloud-lightning-rain' - | 'cloud-lightning' - | 'cloud-minus-fill' - | 'cloud-minus' - | 'cloud-moon-fill' - | 'cloud-moon' - | 'cloud-plus-fill' - | 'cloud-plus' - | 'cloud-rain-fill' - | 'cloud-rain-heavy-fill' - | 'cloud-rain-heavy' - | 'cloud-rain' - | 'cloud-slash-fill' - | 'cloud-slash' - | 'cloud-sleet-fill' - | 'cloud-sleet' - | 'cloud-snow-fill' - | 'cloud-snow' - | 'cloud-sun-fill' - | 'cloud-sun' - | 'cloud-upload-fill' - | 'cloud-upload' - | 'cloud' - | 'clouds-fill' - | 'clouds' - | 'cloudy-fill' - | 'cloudy' - | 'code-slash' - | 'code-square' - | 'code' - | 'collection-fill' - | 'collection-play-fill' - | 'collection-play' - | 'collection' - | 'columns-gap' - | 'columns' - | 'command' - | 'compass-fill' - | 'compass' - | 'cone-striped' - | 'cone' - | 'controller' - | 'cpu-fill' - | 'cpu' - | 'credit-card-2-back-fill' - | 'credit-card-2-back' - | 'credit-card-2-front-fill' - | 'credit-card-2-front' - | 'credit-card-fill' - | 'credit-card' - | 'crop' - | 'cup-fill' - | 'cup-straw' - | 'cup' - | 'cursor-fill' - | 'cursor-text' - | 'cursor' - | 'dash-circle-dotted' - | 'dash-circle-fill' - | 'dash-circle' - | 'dash-square-dotted' - | 'dash-square-fill' - | 'dash-square' - | 'dash' - | 'diagram-2-fill' - | 'diagram-2' - | 'diagram-3-fill' - | 'diagram-3' - | 'diamond-fill' - | 'diamond-half' - | 'diamond' - | 'dice-1-fill' - | 'dice-1' - | 'dice-2-fill' - | 'dice-2' - | 'dice-3-fill' - | 'dice-3' - | 'dice-4-fill' - | 'dice-4' - | 'dice-5-fill' - | 'dice-5' - | 'dice-6-fill' - | 'dice-6' - | 'disc-fill' - | 'disc' - | 'discord' - | 'display-fill' - | 'display' - | 'distribute-horizontal' - | 'distribute-vertical' - | 'door-closed-fill' - | 'door-closed' - | 'door-open-fill' - | 'door-open' - | 'dot' - | 'download' - | 'droplet-fill' - | 'droplet-half' - | 'droplet' - | 'earbuds' - | 'easel-fill' - | 'easel' - | 'egg-fill' - | 'egg-fried' - | 'egg' - | 'eject-fill' - | 'eject' - | 'emoji-angry-fill' - | 'emoji-angry' - | 'emoji-dizzy-fill' - | 'emoji-dizzy' - | 'emoji-expressionless-fill' - | 'emoji-expressionless' - | 'emoji-frown-fill' - | 'emoji-frown' - | 'emoji-heart-eyes-fill' - | 'emoji-heart-eyes' - | 'emoji-laughing-fill' - | 'emoji-laughing' - | 'emoji-neutral-fill' - | 'emoji-neutral' - | 'emoji-smile-fill' - | 'emoji-smile-upside-down-fill' - | 'emoji-smile-upside-down' - | 'emoji-smile' - | 'emoji-sunglasses-fill' - | 'emoji-sunglasses' - | 'emoji-wink-fill' - | 'emoji-wink' - | 'envelope-fill' - | 'envelope-open-fill' - | 'envelope-open' - | 'envelope' - | 'eraser-fill' - | 'eraser' - | 'exclamation-circle-fill' - | 'exclamation-circle' - | 'exclamation-diamond-fill' - | 'exclamation-diamond' - | 'exclamation-octagon-fill' - | 'exclamation-octagon' - | 'exclamation-square-fill' - | 'exclamation-square' - | 'exclamation-triangle-fill' - | 'exclamation-triangle' - | 'exclamation' - | 'exclude' - | 'eye-fill' - | 'eye-slash-fill' - | 'eye-slash' - | 'eye' - | 'eyedropper' - | 'eyeglasses' - | 'facebook' - | 'file-arrow-down-fill' - | 'file-arrow-down' - | 'file-arrow-up-fill' - | 'file-arrow-up' - | 'file-bar-graph-fill' - | 'file-bar-graph' - | 'file-binary-fill' - | 'file-binary' - | 'file-break-fill' - | 'file-break' - | 'file-check-fill' - | 'file-check' - | 'file-code-fill' - | 'file-code' - | 'file-diff-fill' - | 'file-diff' - | 'file-earmark-arrow-down-fill' - | 'file-earmark-arrow-down' - | 'file-earmark-arrow-up-fill' - | 'file-earmark-arrow-up' - | 'file-earmark-bar-graph-fill' - | 'file-earmark-bar-graph' - | 'file-earmark-binary-fill' - | 'file-earmark-binary' - | 'file-earmark-break-fill' - | 'file-earmark-break' - | 'file-earmark-check-fill' - | 'file-earmark-check' - | 'file-earmark-code-fill' - | 'file-earmark-code' - | 'file-earmark-diff-fill' - | 'file-earmark-diff' - | 'file-earmark-easel-fill' - | 'file-earmark-easel' - | 'file-earmark-excel-fill' - | 'file-earmark-excel' - | 'file-earmark-fill' - | 'file-earmark-font-fill' - | 'file-earmark-font' - | 'file-earmark-image-fill' - | 'file-earmark-image' - | 'file-earmark-lock-fill' - | 'file-earmark-lock' - | 'file-earmark-lock2-fill' - | 'file-earmark-lock2' - | 'file-earmark-medical-fill' - | 'file-earmark-medical' - | 'file-earmark-minus-fill' - | 'file-earmark-minus' - | 'file-earmark-music-fill' - | 'file-earmark-music' - | 'file-earmark-person-fill' - | 'file-earmark-person' - | 'file-earmark-play-fill' - | 'file-earmark-play' - | 'file-earmark-plus-fill' - | 'file-earmark-plus' - | 'file-earmark-post-fill' - | 'file-earmark-post' - | 'file-earmark-ppt-fill' - | 'file-earmark-ppt' - | 'file-earmark-richtext-fill' - | 'file-earmark-richtext' - | 'file-earmark-ruled-fill' - | 'file-earmark-ruled' - | 'file-earmark-slides-fill' - | 'file-earmark-slides' - | 'file-earmark-spreadsheet-fill' - | 'file-earmark-spreadsheet' - | 'file-earmark-text-fill' - | 'file-earmark-text' - | 'file-earmark-word-fill' - | 'file-earmark-word' - | 'file-earmark-x-fill' - | 'file-earmark-x' - | 'file-earmark-zip-fill' - | 'file-earmark-zip' - | 'file-earmark' - | 'file-easel-fill' - | 'file-easel' - | 'file-excel-fill' - | 'file-excel' - | 'file-fill' - | 'file-font-fill' - | 'file-font' - | 'file-image-fill' - | 'file-image' - | 'file-lock-fill' - | 'file-lock' - | 'file-lock2-fill' - | 'file-lock2' - | 'file-medical-fill' - | 'file-medical' - | 'file-minus-fill' - | 'file-minus' - | 'file-music-fill' - | 'file-music' - | 'file-person-fill' - | 'file-person' - | 'file-play-fill' - | 'file-play' - | 'file-plus-fill' - | 'file-plus' - | 'file-post-fill' - | 'file-post' - | 'file-ppt-fill' - | 'file-ppt' - | 'file-richtext-fill' - | 'file-richtext' - | 'file-ruled-fill' - | 'file-ruled' - | 'file-slides-fill' - | 'file-slides' - | 'file-spreadsheet-fill' - | 'file-spreadsheet' - | 'file-text-fill' - | 'file-text' - | 'file-word-fill' - | 'file-word' - | 'file-x-fill' - | 'file-x' - | 'file-zip-fill' - | 'file-zip' - | 'file' - | 'files-alt' - | 'files' - | 'film' - | 'filter-circle-fill' - | 'filter-circle' - | 'filter-left' - | 'filter-right' - | 'filter-square-fill' - | 'filter-square' - | 'filter' - | 'flag-fill' - | 'flag' - | 'flower1' - | 'flower2' - | 'flower3' - | 'folder-check' - | 'folder-fill' - | 'folder-minus' - | 'folder-plus' - | 'folder-symlink-fill' - | 'folder-symlink' - | 'folder-x' - | 'folder' - | 'folder2-open' - | 'folder2' - | 'fonts' - | 'forward-fill' - | 'forward' - | 'front' - | 'fullscreen-exit' - | 'fullscreen' - | 'funnel-fill' - | 'funnel' - | 'gear-fill' - | 'gear-wide-connected' - | 'gear-wide' - | 'gear' - | 'gem' - | 'geo-alt-fill' - | 'geo-alt' - | 'geo-fill' - | 'geo' - | 'gift-fill' - | 'gift' - | 'github' - | 'globe' - | 'globe2' - | 'google' - | 'graph-down' - | 'graph-up' - | 'grid-1x2-fill' - | 'grid-1x2' - | 'grid-3x2-gap-fill' - | 'grid-3x2-gap' - | 'grid-3x2' - | 'grid-3x3-gap-fill' - | 'grid-3x3-gap' - | 'grid-3x3' - | 'grid-fill' - | 'grid' - | 'grip-horizontal' - | 'grip-vertical' - | 'hammer' - | 'hand-index-fill' - | 'hand-index-thumb-fill' - | 'hand-index-thumb' - | 'hand-index' - | 'hand-thumbs-down-fill' - | 'hand-thumbs-down' - | 'hand-thumbs-up-fill' - | 'hand-thumbs-up' - | 'handbag-fill' - | 'handbag' - | 'hash' - | 'hdd-fill' - | 'hdd-network-fill' - | 'hdd-network' - | 'hdd-rack-fill' - | 'hdd-rack' - | 'hdd-stack-fill' - | 'hdd-stack' - | 'hdd' - | 'headphones' - | 'headset' - | 'heart-fill' - | 'heart-half' - | 'heart' - | 'heptagon-fill' - | 'heptagon-half' - | 'heptagon' - | 'hexagon-fill' - | 'hexagon-half' - | 'hexagon' - | 'hourglass-bottom' - | 'hourglass-split' - | 'hourglass-top' - | 'hourglass' - | 'house-door-fill' - | 'house-door' - | 'house-fill' - | 'house' - | 'hr' - | 'hurricane' - | 'image-alt' - | 'image-fill' - | 'image' - | 'images' - | 'inbox-fill' - | 'inbox' - | 'inboxes-fill' - | 'inboxes' - | 'info-circle-fill' - | 'info-circle' - | 'info-square-fill' - | 'info-square' - | 'info' - | 'input-cursor-text' - | 'input-cursor' - | 'instagram' - | 'intersect' - | 'journal-album' - | 'journal-arrow-down' - | 'journal-arrow-up' - | 'journal-bookmark-fill' - | 'journal-bookmark' - | 'journal-check' - | 'journal-code' - | 'journal-medical' - | 'journal-minus' - | 'journal-plus' - | 'journal-richtext' - | 'journal-text' - | 'journal-x' - | 'journal' - | 'journals' - | 'joystick' - | 'justify-left' - | 'justify-right' - | 'justify' - | 'kanban-fill' - | 'kanban' - | 'key-fill' - | 'key' - | 'keyboard-fill' - | 'keyboard' - | 'ladder' - | 'lamp-fill' - | 'lamp' - | 'laptop-fill' - | 'laptop' - | 'layer-backward' - | 'layer-forward' - | 'layers-fill' - | 'layers-half' - | 'layers' - | 'layout-sidebar-inset-reverse' - | 'layout-sidebar-inset' - | 'layout-sidebar-reverse' - | 'layout-sidebar' - | 'layout-split' - | 'layout-text-sidebar-reverse' - | 'layout-text-sidebar' - | 'layout-text-window-reverse' - | 'layout-text-window' - | 'layout-three-columns' - | 'layout-wtf' - | 'life-preserver' - | 'lightbulb-fill' - | 'lightbulb-off-fill' - | 'lightbulb-off' - | 'lightbulb' - | 'lightning-charge-fill' - | 'lightning-charge' - | 'lightning-fill' - | 'lightning' - | 'link-45deg' - | 'link' - | 'linkedin' - | 'list-check' - | 'list-nested' - | 'list-ol' - | 'list-stars' - | 'list-task' - | 'list-ul' - | 'list' - | 'lock-fill' - | 'lock' - | 'mailbox' - | 'mailbox2' - | 'map-fill' - | 'map' - | 'markdown-fill' - | 'markdown' - | 'mask' - | 'megaphone-fill' - | 'megaphone' - | 'menu-app-fill' - | 'menu-app' - | 'menu-button-fill' - | 'menu-button-wide-fill' - | 'menu-button-wide' - | 'menu-button' - | 'menu-down' - | 'menu-up' - | 'mic-fill' - | 'mic-mute-fill' - | 'mic-mute' - | 'mic' - | 'minecart-loaded' - | 'minecart' - | 'moisture' - | 'moon-fill' - | 'moon-stars-fill' - | 'moon-stars' - | 'moon' - | 'mouse-fill' - | 'mouse' - | 'mouse2-fill' - | 'mouse2' - | 'mouse3-fill' - | 'mouse3' - | 'music-note-beamed' - | 'music-note-list' - | 'music-note' - | 'music-player-fill' - | 'music-player' - | 'newspaper' - | 'node-minus-fill' - | 'node-minus' - | 'node-plus-fill' - | 'node-plus' - | 'nut-fill' - | 'nut' - | 'octagon-fill' - | 'octagon-half' - | 'octagon' - | 'option' - | 'outlet' - | 'paint-bucket' - | 'palette-fill' - | 'palette' - | 'palette2' - | 'paperclip' - | 'paragraph' - | 'patch-check-fill' - | 'patch-check' - | 'patch-exclamation-fill' - | 'patch-exclamation' - | 'patch-minus-fill' - | 'patch-minus' - | 'patch-plus-fill' - | 'patch-plus' - | 'patch-question-fill' - | 'patch-question' - | 'pause-btn-fill' - | 'pause-btn' - | 'pause-circle-fill' - | 'pause-circle' - | 'pause-fill' - | 'pause' - | 'peace-fill' - | 'peace' - | 'pen-fill' - | 'pen' - | 'pencil-fill' - | 'pencil-square' - | 'pencil' - | 'pentagon-fill' - | 'pentagon-half' - | 'pentagon' - | 'people-fill' - | 'people' - | 'percent' - | 'person-badge-fill' - | 'person-badge' - | 'person-bounding-box' - | 'person-check-fill' - | 'person-check' - | 'person-circle' - | 'person-dash-fill' - | 'person-dash' - | 'person-fill' - | 'person-lines-fill' - | 'person-plus-fill' - | 'person-plus' - | 'person-square' - | 'person-x-fill' - | 'person-x' - | 'person' - | 'phone-fill' - | 'phone-landscape-fill' - | 'phone-landscape' - | 'phone-vibrate-fill' - | 'phone-vibrate' - | 'phone' - | 'pie-chart-fill' - | 'pie-chart' - | 'pin-angle-fill' - | 'pin-angle' - | 'pin-fill' - | 'pin' - | 'pip-fill' - | 'pip' - | 'play-btn-fill' - | 'play-btn' - | 'play-circle-fill' - | 'play-circle' - | 'play-fill' - | 'play' - | 'plug-fill' - | 'plug' - | 'plus-circle-dotted' - | 'plus-circle-fill' - | 'plus-circle' - | 'plus-square-dotted' - | 'plus-square-fill' - | 'plus-square' - | 'plus' - | 'power' - | 'printer-fill' - | 'printer' - | 'puzzle-fill' - | 'puzzle' - | 'question-circle-fill' - | 'question-circle' - | 'question-diamond-fill' - | 'question-diamond' - | 'question-octagon-fill' - | 'question-octagon' - | 'question-square-fill' - | 'question-square' - | 'question' - | 'rainbow' - | 'receipt-cutoff' - | 'receipt' - | 'reception-0' - | 'reception-1' - | 'reception-2' - | 'reception-3' - | 'reception-4' - | 'record-btn-fill' - | 'record-btn' - | 'record-circle-fill' - | 'record-circle' - | 'record-fill' - | 'record' - | 'record2-fill' - | 'record2' - | 'reply-all-fill' - | 'reply-all' - | 'reply-fill' - | 'reply' - | 'rss-fill' - | 'rss' - | 'rulers' - | 'save-fill' - | 'save' - | 'save2-fill' - | 'save2' - | 'scissors' - | 'screwdriver' - | 'search' - | 'segmented-nav' - | 'server' - | 'share-fill' - | 'share' - | 'shield-check' - | 'shield-exclamation' - | 'shield-fill-check' - | 'shield-fill-exclamation' - | 'shield-fill-minus' - | 'shield-fill-plus' - | 'shield-fill-x' - | 'shield-fill' - | 'shield-lock-fill' - | 'shield-lock' - | 'shield-minus' - | 'shield-plus' - | 'shield-shaded' - | 'shield-slash-fill' - | 'shield-slash' - | 'shield-x' - | 'shield' - | 'shift-fill' - | 'shift' - | 'shop-window' - | 'shop' - | 'shuffle' - | 'signpost-2-fill' - | 'signpost-2' - | 'signpost-fill' - | 'signpost-split-fill' - | 'signpost-split' - | 'signpost' - | 'sim-fill' - | 'sim' - | 'skip-backward-btn-fill' - | 'skip-backward-btn' - | 'skip-backward-circle-fill' - | 'skip-backward-circle' - | 'skip-backward-fill' - | 'skip-backward' - | 'skip-end-btn-fill' - | 'skip-end-btn' - | 'skip-end-circle-fill' - | 'skip-end-circle' - | 'skip-end-fill' - | 'skip-end' - | 'skip-forward-btn-fill' - | 'skip-forward-btn' - | 'skip-forward-circle-fill' - | 'skip-forward-circle' - | 'skip-forward-fill' - | 'skip-forward' - | 'skip-start-btn-fill' - | 'skip-start-btn' - | 'skip-start-circle-fill' - | 'skip-start-circle' - | 'skip-start-fill' - | 'skip-start' - | 'slack' - | 'slash-circle-fill' - | 'slash-circle' - | 'slash-square-fill' - | 'slash-square' - | 'slash' - | 'sliders' - | 'smartwatch' - | 'snow' - | 'snow2' - | 'snow3' - | 'sort-alpha-down-alt' - | 'sort-alpha-down' - | 'sort-alpha-up-alt' - | 'sort-alpha-up' - | 'sort-down-alt' - | 'sort-down' - | 'sort-numeric-down-alt' - | 'sort-numeric-down' - | 'sort-numeric-up-alt' - | 'sort-numeric-up' - | 'sort-up-alt' - | 'sort-up' - | 'soundwave' - | 'speaker-fill' - | 'speaker' - | 'speedometer' - | 'speedometer2' - | 'spellcheck' - | 'square-fill' - | 'square-half' - | 'square' - | 'stack' - | 'star-fill' - | 'star-half' - | 'star' - | 'stars' - | 'stickies-fill' - | 'stickies' - | 'sticky-fill' - | 'sticky' - | 'stop-btn-fill' - | 'stop-btn' - | 'stop-circle-fill' - | 'stop-circle' - | 'stop-fill' - | 'stop' - | 'stoplights-fill' - | 'stoplights' - | 'stopwatch-fill' - | 'stopwatch' - | 'subtract' - | 'suit-club-fill' - | 'suit-club' - | 'suit-diamond-fill' - | 'suit-diamond' - | 'suit-heart-fill' - | 'suit-heart' - | 'suit-spade-fill' - | 'suit-spade' - | 'sun-fill' - | 'sun' - | 'sunglasses' - | 'sunrise-fill' - | 'sunrise' - | 'sunset-fill' - | 'sunset' - | 'symmetry-horizontal' - | 'symmetry-vertical' - | 'table' - | 'tablet-fill' - | 'tablet-landscape-fill' - | 'tablet-landscape' - | 'tablet' - | 'tag-fill' - | 'tag' - | 'tags-fill' - | 'tags' - | 'telegram' - | 'telephone-fill' - | 'telephone-forward-fill' - | 'telephone-forward' - | 'telephone-inbound-fill' - | 'telephone-inbound' - | 'telephone-minus-fill' - | 'telephone-minus' - | 'telephone-outbound-fill' - | 'telephone-outbound' - | 'telephone-plus-fill' - | 'telephone-plus' - | 'telephone-x-fill' - | 'telephone-x' - | 'telephone' - | 'terminal-fill' - | 'terminal' - | 'text-center' - | 'text-indent-left' - | 'text-indent-right' - | 'text-left' - | 'text-paragraph' - | 'text-right' - | 'textarea-resize' - | 'textarea-t' - | 'textarea' - | 'thermometer-half' - | 'thermometer-high' - | 'thermometer-low' - | 'thermometer-snow' - | 'thermometer-sun' - | 'thermometer' - | 'three-dots-vertical' - | 'three-dots' - | 'toggle-off' - | 'toggle-on' - | 'toggle2-off' - | 'toggle2-on' - | 'toggles' - | 'toggles2' - | 'tools' - | 'tornado' - | 'trash-fill' - | 'trash' - | 'trash2-fill' - | 'trash2' - | 'tree-fill' - | 'tree' - | 'triangle-fill' - | 'triangle-half' - | 'triangle' - | 'trophy-fill' - | 'trophy' - | 'tropical-storm' - | 'truck-flatbed' - | 'truck' - | 'tsunami' - | 'tv-fill' - | 'tv' - | 'twitch' - | 'twitter' - | 'type-bold' - | 'type-h1' - | 'type-h2' - | 'type-h3' - | 'type-italic' - | 'type-strikethrough' - | 'type-underline' - | 'type' - | 'ui-checks-grid' - | 'ui-checks' - | 'ui-radios-grid' - | 'ui-radios' - | 'umbrella-fill' - | 'umbrella' - | 'union' - | 'unlock-fill' - | 'unlock' - | 'upc-scan' - | 'upc' - | 'upload' - | 'vector-pen' - | 'view-list' - | 'view-stacked' - | 'vinyl-fill' - | 'vinyl' - | 'voicemail' - | 'volume-down-fill' - | 'volume-down' - | 'volume-mute-fill' - | 'volume-mute' - | 'volume-off-fill' - | 'volume-off' - | 'volume-up-fill' - | 'volume-up' - | 'vr' - | 'wallet-fill' - | 'wallet' - | 'wallet2' - | 'watch' - | 'water' - | 'whatsapp' - | 'wifi-1' - | 'wifi-2' - | 'wifi-off' - | 'wifi' - | 'wind' - | 'window-dock' - | 'window-sidebar' - | 'window' - | 'wrench' - | 'x-circle-fill' - | 'x-circle' - | 'x-diamond-fill' - | 'x-diamond' - | 'x-octagon-fill' - | 'x-octagon' - | 'x-square-fill' - | 'x-square' - | 'x' - | 'youtube' - | 'zoom-in' - | 'zoom-out' - | 'bank' - | 'bank2' - | 'bell-slash-fill' - | 'bell-slash' - | 'cash-coin' - | 'check-lg' - | 'coin' - | 'currency-bitcoin' - | 'currency-dollar' - | 'currency-euro' - | 'currency-exchange' - | 'currency-pound' - | 'currency-yen' - | 'dash-lg' - | 'exclamation-lg' - | 'file-earmark-pdf-fill' - | 'file-earmark-pdf' - | 'file-pdf-fill' - | 'file-pdf' - | 'gender-ambiguous' - | 'gender-female' - | 'gender-male' - | 'gender-trans' - | 'headset-vr' - | 'info-lg' - | 'mastodon' - | 'messenger' - | 'piggy-bank-fill' - | 'piggy-bank' - | 'pin-map-fill' - | 'pin-map' - | 'plus-lg' - | 'question-lg' - | 'recycle' - | 'reddit' - | 'safe-fill' - | 'safe2-fill' - | 'safe2' - | 'sd-card-fill' - | 'sd-card' - | 'skype' - | 'slash-lg' - | 'translate' - | 'x-lg' - | 'safe' - | 'apple' - | 'microsoft' - | 'windows' - | 'behance' - | 'dribbble' - | 'line' - | 'medium' - | 'paypal' - | 'pinterest' - | 'signal' - | 'snapchat' - | 'spotify' - | 'stack-overflow' - | 'strava' - | 'wordpress' - | 'vimeo' - | 'activity' - | 'easel2-fill' - | 'easel2' - | 'easel3-fill' - | 'easel3' - | 'fan' - | 'fingerprint' - | 'graph-down-arrow' - | 'graph-up-arrow' - | 'hypnotize' - | 'magic' - | 'person-rolodex' - | 'person-video' - | 'person-video2' - | 'person-video3' - | 'person-workspace' - | 'radioactive' - | 'webcam-fill' - | 'webcam' - | 'yin-yang' - | 'bandaid-fill' - | 'bandaid' - | 'bluetooth' - | 'body-text' - | 'boombox' - | 'boxes' - | 'dpad-fill' - | 'dpad' - | 'ear-fill' - | 'ear' - | 'envelope-check-fill' - | 'envelope-check' - | 'envelope-dash-fill' - | 'envelope-dash' - | 'envelope-exclamation-fill' - | 'envelope-exclamation' - | 'envelope-plus-fill' - | 'envelope-plus' - | 'envelope-slash-fill' - | 'envelope-slash' - | 'envelope-x-fill' - | 'envelope-x' - | 'explicit-fill' - | 'explicit' - | 'git' - | 'infinity' - | 'list-columns-reverse' - | 'list-columns' - | 'meta' - | 'nintendo-switch' - | 'pc-display-horizontal' - | 'pc-display' - | 'pc-horizontal' - | 'pc' - | 'playstation' - | 'plus-slash-minus' - | 'projector-fill' - | 'projector' - | 'qr-code-scan' - | 'qr-code' - | 'quora' - | 'quote' - | 'robot' - | 'send-check-fill' - | 'send-check' - | 'send-dash-fill' - | 'send-dash' - | 'send-exclamation-fill' - | 'send-exclamation' - | 'send-fill' - | 'send-plus-fill' - | 'send-plus' - | 'send-slash-fill' - | 'send-slash' - | 'send-x-fill' - | 'send-x' - | 'send' - | 'steam' - | 'terminal-dash' - | 'terminal-plus' - | 'terminal-split' - | 'ticket-detailed-fill' - | 'ticket-detailed' - | 'ticket-fill' - | 'ticket-perforated-fill' - | 'ticket-perforated' - | 'ticket' - | 'tiktok' - | 'window-dash' - | 'window-desktop' - | 'window-fullscreen' - | 'window-plus' - | 'window-split' - | 'window-stack' - | 'window-x' - | 'xbox' - | 'ethernet' - | 'hdmi-fill' - | 'hdmi' - | 'usb-c-fill' - | 'usb-c' - | 'usb-fill' - | 'usb-plug-fill' - | 'usb-plug' - | 'usb-symbol' - | 'usb' - | 'boombox-fill' - | 'displayport' - | 'gpu-card' - | 'memory' - | 'modem-fill' - | 'modem' - | 'motherboard-fill' - | 'motherboard' - | 'optical-audio-fill' - | 'optical-audio' - | 'pci-card' - | 'router-fill' - | 'router' - | 'thunderbolt-fill' - | 'thunderbolt' - | 'usb-drive-fill' - | 'usb-drive' - | 'usb-micro-fill' - | 'usb-micro' - | 'usb-mini-fill' - | 'usb-mini' - | 'cloud-haze2' - | 'device-hdd-fill' - | 'device-hdd' - | 'device-ssd-fill' - | 'device-ssd' - | 'displayport-fill' - | 'mortarboard-fill' - | 'mortarboard' - | 'terminal-x' - | 'arrow-through-heart-fill' - | 'arrow-through-heart' - | 'badge-sd-fill' - | 'badge-sd' - | 'bag-heart-fill' - | 'bag-heart' - | 'balloon-fill' - | 'balloon-heart-fill' - | 'balloon-heart' - | 'balloon' - | 'box2-fill' - | 'box2-heart-fill' - | 'box2-heart' - | 'box2' - | 'braces-asterisk' - | 'calendar-heart-fill' - | 'calendar-heart' - | 'calendar2-heart-fill' - | 'calendar2-heart' - | 'chat-heart-fill' - | 'chat-heart' - | 'chat-left-heart-fill' - | 'chat-left-heart' - | 'chat-right-heart-fill' - | 'chat-right-heart' - | 'chat-square-heart-fill' - | 'chat-square-heart' - | 'clipboard-check-fill' - | 'clipboard-data-fill' - | 'clipboard-fill' - | 'clipboard-heart-fill' - | 'clipboard-heart' - | 'clipboard-minus-fill' - | 'clipboard-plus-fill' - | 'clipboard-pulse' - | 'clipboard-x-fill' - | 'clipboard2-check-fill' - | 'clipboard2-check' - | 'clipboard2-data-fill' - | 'clipboard2-data' - | 'clipboard2-fill' - | 'clipboard2-heart-fill' - | 'clipboard2-heart' - | 'clipboard2-minus-fill' - | 'clipboard2-minus' - | 'clipboard2-plus-fill' - | 'clipboard2-plus' - | 'clipboard2-pulse-fill' - | 'clipboard2-pulse' - | 'clipboard2-x-fill' - | 'clipboard2-x' - | 'clipboard2' - | 'emoji-kiss-fill' - | 'emoji-kiss' - | 'envelope-heart-fill' - | 'envelope-heart' - | 'envelope-open-heart-fill' - | 'envelope-open-heart' - | 'envelope-paper-fill' - | 'envelope-paper-heart-fill' - | 'envelope-paper-heart' - | 'envelope-paper' - | 'filetype-aac' - | 'filetype-ai' - | 'filetype-bmp' - | 'filetype-cs' - | 'filetype-css' - | 'filetype-csv' - | 'filetype-doc' - | 'filetype-docx' - | 'filetype-exe' - | 'filetype-gif' - | 'filetype-heic' - | 'filetype-html' - | 'filetype-java' - | 'filetype-jpg' - | 'filetype-js' - | 'filetype-jsx' - | 'filetype-key' - | 'filetype-m4p' - | 'filetype-md' - | 'filetype-mdx' - | 'filetype-mov' - | 'filetype-mp3' - | 'filetype-mp4' - | 'filetype-otf' - | 'filetype-pdf' - | 'filetype-php' - | 'filetype-png' - | 'filetype-ppt' - | 'filetype-psd' - | 'filetype-py' - | 'filetype-raw' - | 'filetype-rb' - | 'filetype-sass' - | 'filetype-scss' - | 'filetype-sh' - | 'filetype-svg' - | 'filetype-tiff' - | 'filetype-tsx' - | 'filetype-ttf' - | 'filetype-txt' - | 'filetype-wav' - | 'filetype-woff' - | 'filetype-xls' - | 'filetype-xml' - | 'filetype-yml' - | 'heart-arrow' - | 'heart-pulse-fill' - | 'heart-pulse' - | 'heartbreak-fill' - | 'heartbreak' - | 'hearts' - | 'hospital-fill' - | 'hospital' - | 'house-heart-fill' - | 'house-heart' - | 'incognito' - | 'magnet-fill' - | 'magnet' - | 'person-heart' - | 'person-hearts' - | 'phone-flip' - | 'plugin' - | 'postage-fill' - | 'postage-heart-fill' - | 'postage-heart' - | 'postage' - | 'postcard-fill' - | 'postcard-heart-fill' - | 'postcard-heart' - | 'postcard' - | 'search-heart-fill' - | 'search-heart' - | 'sliders2-vertical' - | 'sliders2' - | 'trash3-fill' - | 'trash3' - | 'valentine' - | 'valentine2' - | 'wrench-adjustable-circle-fill' - | 'wrench-adjustable-circle' - | 'wrench-adjustable' - | 'filetype-json' - | 'filetype-pptx' - | 'filetype-xlsx' - | '1-circle-fill' - | '1-circle' - | '1-square-fill' - | '1-square' - | '2-circle-fill' - | '2-circle' - | '2-square-fill' - | '2-square' - | '3-circle-fill' - | '3-circle' - | '3-square-fill' - | '3-square' - | '4-circle-fill' - | '4-circle' - | '4-square-fill' - | '4-square' - | '5-circle-fill' - | '5-circle' - | '5-square-fill' - | '5-square' - | '6-circle-fill' - | '6-circle' - | '6-square-fill' - | '6-square' - | '7-circle-fill' - | '7-circle' - | '7-square-fill' - | '7-square' - | '8-circle-fill' - | '8-circle' - | '8-square-fill' - | '8-square' - | '9-circle-fill' - | '9-circle' - | '9-square-fill' - | '9-square' - | 'airplane-engines-fill' - | 'airplane-engines' - | 'airplane-fill' - | 'airplane' - | 'alexa' - | 'alipay' - | 'android' - | 'android2' - | 'box-fill' - | 'box-seam-fill' - | 'browser-chrome' - | 'browser-edge' - | 'browser-firefox' - | 'browser-safari' - | 'c-circle-fill' - | 'c-circle' - | 'c-square-fill' - | 'c-square' - | 'capsule-pill' - | 'capsule' - | 'car-front-fill' - | 'car-front' - | 'cassette-fill' - | 'cassette' - | 'cc-circle-fill' - | 'cc-circle' - | 'cc-square-fill' - | 'cc-square' - | 'cup-hot-fill' - | 'cup-hot' - | 'currency-rupee' - | 'dropbox' - | 'escape' - | 'fast-forward-btn-fill' - | 'fast-forward-btn' - | 'fast-forward-circle-fill' - | 'fast-forward-circle' - | 'fast-forward-fill' - | 'fast-forward' - | 'filetype-sql' - | 'fire' - | 'google-play' - | 'h-circle-fill' - | 'h-circle' - | 'h-square-fill' - | 'h-square' - | 'indent' - | 'lungs-fill' - | 'lungs' - | 'microsoft-teams' - | 'p-circle-fill' - | 'p-circle' - | 'p-square-fill' - | 'p-square' - | 'pass-fill' - | 'pass' - | 'prescription' - | 'prescription2' - | 'r-circle-fill' - | 'r-circle' - | 'r-square-fill' - | 'r-square' - | 'repeat-1' - | 'repeat' - | 'rewind-btn-fill' - | 'rewind-btn' - | 'rewind-circle-fill' - | 'rewind-circle' - | 'rewind-fill' - | 'rewind' - | 'train-freight-front-fill' - | 'train-freight-front' - | 'train-front-fill' - | 'train-front' - | 'train-lightrail-front-fill' - | 'train-lightrail-front' - | 'truck-front-fill' - | 'truck-front' - | 'ubuntu' - | 'unindent' - | 'unity' - | 'universal-access-circle' - | 'universal-access' - | 'virus' - | 'virus2' - | 'wechat' - | 'yelp' - | 'sign-stop-fill' - | 'sign-stop-lights-fill' - | 'sign-stop-lights' - | 'sign-stop' - | 'sign-turn-left-fill' - | 'sign-turn-left' - | 'sign-turn-right-fill' - | 'sign-turn-right' - | 'sign-turn-slight-left-fill' - | 'sign-turn-slight-left' - | 'sign-turn-slight-right-fill' - | 'sign-turn-slight-right' - | 'sign-yield-fill' - | 'sign-yield' - | 'ev-station-fill' - | 'ev-station' - | 'fuel-pump-diesel-fill' - | 'fuel-pump-diesel' - | 'fuel-pump-fill' - | 'fuel-pump' - | '0-circle-fill' - | '0-circle' - | '0-square-fill' - | '0-square' - | 'rocket-fill' - | 'rocket-takeoff-fill' - | 'rocket-takeoff' - | 'rocket' - | 'stripe' - | 'subscript' - | 'superscript' - | 'trello' - | 'envelope-at-fill' - | 'envelope-at' - | 'regex' - | 'text-wrap' - | 'sign-dead-end-fill' - | 'sign-dead-end' - | 'sign-do-not-enter-fill' - | 'sign-do-not-enter' - | 'sign-intersection-fill' - | 'sign-intersection-side-fill' - | 'sign-intersection-side' - | 'sign-intersection-t-fill' - | 'sign-intersection-t' - | 'sign-intersection-y-fill' - | 'sign-intersection-y' - | 'sign-intersection' - | 'sign-merge-left-fill' - | 'sign-merge-left' - | 'sign-merge-right-fill' - | 'sign-merge-right' - | 'sign-no-left-turn-fill' - | 'sign-no-left-turn' - | 'sign-no-parking-fill' - | 'sign-no-parking' - | 'sign-no-right-turn-fill' - | 'sign-no-right-turn' - | 'sign-railroad-fill' - | 'sign-railroad' - | 'building-add' - | 'building-check' - | 'building-dash' - | 'building-down' - | 'building-exclamation' - | 'building-fill-add' - | 'building-fill-check' - | 'building-fill-dash' - | 'building-fill-down' - | 'building-fill-exclamation' - | 'building-fill-gear' - | 'building-fill-lock' - | 'building-fill-slash' - | 'building-fill-up' - | 'building-fill-x' - | 'building-fill' - | 'building-gear' - | 'building-lock' - | 'building-slash' - | 'building-up' - | 'building-x' - | 'buildings-fill' - | 'buildings' - | 'bus-front-fill' - | 'bus-front' - | 'ev-front-fill' - | 'ev-front' - | 'globe-americas' - | 'globe-asia-australia' - | 'globe-central-south-asia' - | 'globe-europe-africa' - | 'house-add-fill' - | 'house-add' - | 'house-check-fill' - | 'house-check' - | 'house-dash-fill' - | 'house-dash' - | 'house-down-fill' - | 'house-down' - | 'house-exclamation-fill' - | 'house-exclamation' - | 'house-gear-fill' - | 'house-gear' - | 'house-lock-fill' - | 'house-lock' - | 'house-slash-fill' - | 'house-slash' - | 'house-up-fill' - | 'house-up' - | 'house-x-fill' - | 'house-x' - | 'person-add' - | 'person-down' - | 'person-exclamation' - | 'person-fill-add' - | 'person-fill-check' - | 'person-fill-dash' - | 'person-fill-down' - | 'person-fill-exclamation' - | 'person-fill-gear' - | 'person-fill-lock' - | 'person-fill-slash' - | 'person-fill-up' - | 'person-fill-x' - | 'person-gear' - | 'person-lock' - | 'person-slash' - | 'person-up' - | 'scooter' - | 'taxi-front-fill' - | 'taxi-front' - | 'amd' - | 'database-add' - | 'database-check' - | 'database-dash' - | 'database-down' - | 'database-exclamation' - | 'database-fill-add' - | 'database-fill-check' - | 'database-fill-dash' - | 'database-fill-down' - | 'database-fill-exclamation' - | 'database-fill-gear' - | 'database-fill-lock' - | 'database-fill-slash' - | 'database-fill-up' - | 'database-fill-x' - | 'database-fill' - | 'database-gear' - | 'database-lock' - | 'database-slash' - | 'database-up' - | 'database-x' - | 'database' - | 'houses-fill' - | 'houses' - | 'nvidia' - | 'person-vcard-fill' - | 'person-vcard' - | 'sina-weibo' - | 'tencent-qq' - | 'wikipedia' - | 'alphabet-uppercase' - | 'alphabet' - | 'amazon' - | 'arrows-collapse-vertical' - | 'arrows-expand-vertical' - | 'arrows-vertical' - | 'arrows' - | 'ban-fill' - | 'ban' - | 'bing' - | 'cake' - | 'cake2' - | 'cookie' - | 'copy' - | 'crosshair' - | 'crosshair2' - | 'emoji-astonished-fill' - | 'emoji-astonished' - | 'emoji-grimace-fill' - | 'emoji-grimace' - | 'emoji-grin-fill' - | 'emoji-grin' - | 'emoji-surprise-fill' - | 'emoji-surprise' - | 'emoji-tear-fill' - | 'emoji-tear' - | 'envelope-arrow-down-fill' - | 'envelope-arrow-down' - | 'envelope-arrow-up-fill' - | 'envelope-arrow-up' - | 'feather' - | 'feather2' - | 'floppy-fill' - | 'floppy' - | 'floppy2-fill' - | 'floppy2' - | 'gitlab' - | 'highlighter' - | 'marker-tip' - | 'nvme-fill' - | 'nvme' - | 'opencollective' - | 'pci-card-network' - | 'pci-card-sound' - | 'radar' - | 'send-arrow-down-fill' - | 'send-arrow-down' - | 'send-arrow-up-fill' - | 'send-arrow-up' - | 'sim-slash-fill' - | 'sim-slash' - | 'sourceforge' - | 'substack' - | 'threads-fill' - | 'threads' - | 'transparency' - | 'twitter-x' - | 'type-h4' - | 'type-h5' - | 'type-h6' - | 'backpack-fill' - | 'backpack' - | 'backpack2-fill' - | 'backpack2' - | 'backpack3-fill' - | 'backpack3' - | 'backpack4-fill' - | 'backpack4' - | 'brilliance' - | 'cake-fill' - | 'cake2-fill' - | 'duffle-fill' - | 'duffle' - | 'exposure' - | 'gender-neuter' - | 'highlights' - | 'luggage-fill' - | 'luggage' - | 'mailbox-flag' - | 'mailbox2-flag' - | 'noise-reduction' - | 'passport-fill' - | 'passport' - | 'person-arms-up' - | 'person-raised-hand' - | 'person-standing-dress' - | 'person-standing' - | 'person-walking' - | 'person-wheelchair' - | 'shadows' - | 'suitcase-fill' - | 'suitcase-lg-fill' - | 'suitcase-lg' - | 'suitcase' - | 'suitcase2-fill' - | 'suitcase2' - | 'vignette'; + | '123' + | 'alarm-fill' + | 'alarm' + | 'align-bottom' + | 'align-center' + | 'align-end' + | 'align-middle' + | 'align-start' + | 'align-top' + | 'alt' + | 'app-indicator' + | 'app' + | 'archive-fill' + | 'archive' + | 'arrow-90deg-down' + | 'arrow-90deg-left' + | 'arrow-90deg-right' + | 'arrow-90deg-up' + | 'arrow-bar-down' + | 'arrow-bar-left' + | 'arrow-bar-right' + | 'arrow-bar-up' + | 'arrow-clockwise' + | 'arrow-counterclockwise' + | 'arrow-down-circle-fill' + | 'arrow-down-circle' + | 'arrow-down-left-circle-fill' + | 'arrow-down-left-circle' + | 'arrow-down-left-square-fill' + | 'arrow-down-left-square' + | 'arrow-down-left' + | 'arrow-down-right-circle-fill' + | 'arrow-down-right-circle' + | 'arrow-down-right-square-fill' + | 'arrow-down-right-square' + | 'arrow-down-right' + | 'arrow-down-short' + | 'arrow-down-square-fill' + | 'arrow-down-square' + | 'arrow-down-up' + | 'arrow-down' + | 'arrow-left-circle-fill' + | 'arrow-left-circle' + | 'arrow-left-right' + | 'arrow-left-short' + | 'arrow-left-square-fill' + | 'arrow-left-square' + | 'arrow-left' + | 'arrow-repeat' + | 'arrow-return-left' + | 'arrow-return-right' + | 'arrow-right-circle-fill' + | 'arrow-right-circle' + | 'arrow-right-short' + | 'arrow-right-square-fill' + | 'arrow-right-square' + | 'arrow-right' + | 'arrow-up-circle-fill' + | 'arrow-up-circle' + | 'arrow-up-left-circle-fill' + | 'arrow-up-left-circle' + | 'arrow-up-left-square-fill' + | 'arrow-up-left-square' + | 'arrow-up-left' + | 'arrow-up-right-circle-fill' + | 'arrow-up-right-circle' + | 'arrow-up-right-square-fill' + | 'arrow-up-right-square' + | 'arrow-up-right' + | 'arrow-up-short' + | 'arrow-up-square-fill' + | 'arrow-up-square' + | 'arrow-up' + | 'arrows-angle-contract' + | 'arrows-angle-expand' + | 'arrows-collapse' + | 'arrows-expand' + | 'arrows-fullscreen' + | 'arrows-move' + | 'aspect-ratio-fill' + | 'aspect-ratio' + | 'asterisk' + | 'at' + | 'award-fill' + | 'award' + | 'back' + | 'backspace-fill' + | 'backspace-reverse-fill' + | 'backspace-reverse' + | 'backspace' + | 'badge-3d-fill' + | 'badge-3d' + | 'badge-4k-fill' + | 'badge-4k' + | 'badge-8k-fill' + | 'badge-8k' + | 'badge-ad-fill' + | 'badge-ad' + | 'badge-ar-fill' + | 'badge-ar' + | 'badge-cc-fill' + | 'badge-cc' + | 'badge-hd-fill' + | 'badge-hd' + | 'badge-tm-fill' + | 'badge-tm' + | 'badge-vo-fill' + | 'badge-vo' + | 'badge-vr-fill' + | 'badge-vr' + | 'badge-wc-fill' + | 'badge-wc' + | 'bag-check-fill' + | 'bag-check' + | 'bag-dash-fill' + | 'bag-dash' + | 'bag-fill' + | 'bag-plus-fill' + | 'bag-plus' + | 'bag-x-fill' + | 'bag-x' + | 'bag' + | 'bar-chart-fill' + | 'bar-chart-line-fill' + | 'bar-chart-line' + | 'bar-chart-steps' + | 'bar-chart' + | 'basket-fill' + | 'basket' + | 'basket2-fill' + | 'basket2' + | 'basket3-fill' + | 'basket3' + | 'battery-charging' + | 'battery-full' + | 'battery-half' + | 'battery' + | 'bell-fill' + | 'bell' + | 'bezier' + | 'bezier2' + | 'bicycle' + | 'binoculars-fill' + | 'binoculars' + | 'blockquote-left' + | 'blockquote-right' + | 'book-fill' + | 'book-half' + | 'book' + | 'bookmark-check-fill' + | 'bookmark-check' + | 'bookmark-dash-fill' + | 'bookmark-dash' + | 'bookmark-fill' + | 'bookmark-heart-fill' + | 'bookmark-heart' + | 'bookmark-plus-fill' + | 'bookmark-plus' + | 'bookmark-star-fill' + | 'bookmark-star' + | 'bookmark-x-fill' + | 'bookmark-x' + | 'bookmark' + | 'bookmarks-fill' + | 'bookmarks' + | 'bookshelf' + | 'bootstrap-fill' + | 'bootstrap-reboot' + | 'bootstrap' + | 'border-all' + | 'border-bottom' + | 'border-center' + | 'border-inner' + | 'border-left' + | 'border-middle' + | 'border-outer' + | 'border-right' + | 'border-style' + | 'border-top' + | 'border-width' + | 'border' + | 'bounding-box-circles' + | 'bounding-box' + | 'box-arrow-down-left' + | 'box-arrow-down-right' + | 'box-arrow-down' + | 'box-arrow-in-down-left' + | 'box-arrow-in-down-right' + | 'box-arrow-in-down' + | 'box-arrow-in-left' + | 'box-arrow-in-right' + | 'box-arrow-in-up-left' + | 'box-arrow-in-up-right' + | 'box-arrow-in-up' + | 'box-arrow-left' + | 'box-arrow-right' + | 'box-arrow-up-left' + | 'box-arrow-up-right' + | 'box-arrow-up' + | 'box-seam' + | 'box' + | 'braces' + | 'bricks' + | 'briefcase-fill' + | 'briefcase' + | 'brightness-alt-high-fill' + | 'brightness-alt-high' + | 'brightness-alt-low-fill' + | 'brightness-alt-low' + | 'brightness-high-fill' + | 'brightness-high' + | 'brightness-low-fill' + | 'brightness-low' + | 'broadcast-pin' + | 'broadcast' + | 'brush-fill' + | 'brush' + | 'bucket-fill' + | 'bucket' + | 'bug-fill' + | 'bug' + | 'building' + | 'bullseye' + | 'calculator-fill' + | 'calculator' + | 'calendar-check-fill' + | 'calendar-check' + | 'calendar-date-fill' + | 'calendar-date' + | 'calendar-day-fill' + | 'calendar-day' + | 'calendar-event-fill' + | 'calendar-event' + | 'calendar-fill' + | 'calendar-minus-fill' + | 'calendar-minus' + | 'calendar-month-fill' + | 'calendar-month' + | 'calendar-plus-fill' + | 'calendar-plus' + | 'calendar-range-fill' + | 'calendar-range' + | 'calendar-week-fill' + | 'calendar-week' + | 'calendar-x-fill' + | 'calendar-x' + | 'calendar' + | 'calendar2-check-fill' + | 'calendar2-check' + | 'calendar2-date-fill' + | 'calendar2-date' + | 'calendar2-day-fill' + | 'calendar2-day' + | 'calendar2-event-fill' + | 'calendar2-event' + | 'calendar2-fill' + | 'calendar2-minus-fill' + | 'calendar2-minus' + | 'calendar2-month-fill' + | 'calendar2-month' + | 'calendar2-plus-fill' + | 'calendar2-plus' + | 'calendar2-range-fill' + | 'calendar2-range' + | 'calendar2-week-fill' + | 'calendar2-week' + | 'calendar2-x-fill' + | 'calendar2-x' + | 'calendar2' + | 'calendar3-event-fill' + | 'calendar3-event' + | 'calendar3-fill' + | 'calendar3-range-fill' + | 'calendar3-range' + | 'calendar3-week-fill' + | 'calendar3-week' + | 'calendar3' + | 'calendar4-event' + | 'calendar4-range' + | 'calendar4-week' + | 'calendar4' + | 'camera-fill' + | 'camera-reels-fill' + | 'camera-reels' + | 'camera-video-fill' + | 'camera-video-off-fill' + | 'camera-video-off' + | 'camera-video' + | 'camera' + | 'camera2' + | 'capslock-fill' + | 'capslock' + | 'card-checklist' + | 'card-heading' + | 'card-image' + | 'card-list' + | 'card-text' + | 'caret-down-fill' + | 'caret-down-square-fill' + | 'caret-down-square' + | 'caret-down' + | 'caret-left-fill' + | 'caret-left-square-fill' + | 'caret-left-square' + | 'caret-left' + | 'caret-right-fill' + | 'caret-right-square-fill' + | 'caret-right-square' + | 'caret-right' + | 'caret-up-fill' + | 'caret-up-square-fill' + | 'caret-up-square' + | 'caret-up' + | 'cart-check-fill' + | 'cart-check' + | 'cart-dash-fill' + | 'cart-dash' + | 'cart-fill' + | 'cart-plus-fill' + | 'cart-plus' + | 'cart-x-fill' + | 'cart-x' + | 'cart' + | 'cart2' + | 'cart3' + | 'cart4' + | 'cash-stack' + | 'cash' + | 'cast' + | 'chat-dots-fill' + | 'chat-dots' + | 'chat-fill' + | 'chat-left-dots-fill' + | 'chat-left-dots' + | 'chat-left-fill' + | 'chat-left-quote-fill' + | 'chat-left-quote' + | 'chat-left-text-fill' + | 'chat-left-text' + | 'chat-left' + | 'chat-quote-fill' + | 'chat-quote' + | 'chat-right-dots-fill' + | 'chat-right-dots' + | 'chat-right-fill' + | 'chat-right-quote-fill' + | 'chat-right-quote' + | 'chat-right-text-fill' + | 'chat-right-text' + | 'chat-right' + | 'chat-square-dots-fill' + | 'chat-square-dots' + | 'chat-square-fill' + | 'chat-square-quote-fill' + | 'chat-square-quote' + | 'chat-square-text-fill' + | 'chat-square-text' + | 'chat-square' + | 'chat-text-fill' + | 'chat-text' + | 'chat' + | 'check-all' + | 'check-circle-fill' + | 'check-circle' + | 'check-square-fill' + | 'check-square' + | 'check' + | 'check2-all' + | 'check2-circle' + | 'check2-square' + | 'check2' + | 'chevron-bar-contract' + | 'chevron-bar-down' + | 'chevron-bar-expand' + | 'chevron-bar-left' + | 'chevron-bar-right' + | 'chevron-bar-up' + | 'chevron-compact-down' + | 'chevron-compact-left' + | 'chevron-compact-right' + | 'chevron-compact-up' + | 'chevron-contract' + | 'chevron-double-down' + | 'chevron-double-left' + | 'chevron-double-right' + | 'chevron-double-up' + | 'chevron-down' + | 'chevron-expand' + | 'chevron-left' + | 'chevron-right' + | 'chevron-up' + | 'circle-fill' + | 'circle-half' + | 'circle-square' + | 'circle' + | 'clipboard-check' + | 'clipboard-data' + | 'clipboard-minus' + | 'clipboard-plus' + | 'clipboard-x' + | 'clipboard' + | 'clock-fill' + | 'clock-history' + | 'clock' + | 'cloud-arrow-down-fill' + | 'cloud-arrow-down' + | 'cloud-arrow-up-fill' + | 'cloud-arrow-up' + | 'cloud-check-fill' + | 'cloud-check' + | 'cloud-download-fill' + | 'cloud-download' + | 'cloud-drizzle-fill' + | 'cloud-drizzle' + | 'cloud-fill' + | 'cloud-fog-fill' + | 'cloud-fog' + | 'cloud-fog2-fill' + | 'cloud-fog2' + | 'cloud-hail-fill' + | 'cloud-hail' + | 'cloud-haze-fill' + | 'cloud-haze' + | 'cloud-haze2-fill' + | 'cloud-lightning-fill' + | 'cloud-lightning-rain-fill' + | 'cloud-lightning-rain' + | 'cloud-lightning' + | 'cloud-minus-fill' + | 'cloud-minus' + | 'cloud-moon-fill' + | 'cloud-moon' + | 'cloud-plus-fill' + | 'cloud-plus' + | 'cloud-rain-fill' + | 'cloud-rain-heavy-fill' + | 'cloud-rain-heavy' + | 'cloud-rain' + | 'cloud-slash-fill' + | 'cloud-slash' + | 'cloud-sleet-fill' + | 'cloud-sleet' + | 'cloud-snow-fill' + | 'cloud-snow' + | 'cloud-sun-fill' + | 'cloud-sun' + | 'cloud-upload-fill' + | 'cloud-upload' + | 'cloud' + | 'clouds-fill' + | 'clouds' + | 'cloudy-fill' + | 'cloudy' + | 'code-slash' + | 'code-square' + | 'code' + | 'collection-fill' + | 'collection-play-fill' + | 'collection-play' + | 'collection' + | 'columns-gap' + | 'columns' + | 'command' + | 'compass-fill' + | 'compass' + | 'cone-striped' + | 'cone' + | 'controller' + | 'cpu-fill' + | 'cpu' + | 'credit-card-2-back-fill' + | 'credit-card-2-back' + | 'credit-card-2-front-fill' + | 'credit-card-2-front' + | 'credit-card-fill' + | 'credit-card' + | 'crop' + | 'cup-fill' + | 'cup-straw' + | 'cup' + | 'cursor-fill' + | 'cursor-text' + | 'cursor' + | 'dash-circle-dotted' + | 'dash-circle-fill' + | 'dash-circle' + | 'dash-square-dotted' + | 'dash-square-fill' + | 'dash-square' + | 'dash' + | 'diagram-2-fill' + | 'diagram-2' + | 'diagram-3-fill' + | 'diagram-3' + | 'diamond-fill' + | 'diamond-half' + | 'diamond' + | 'dice-1-fill' + | 'dice-1' + | 'dice-2-fill' + | 'dice-2' + | 'dice-3-fill' + | 'dice-3' + | 'dice-4-fill' + | 'dice-4' + | 'dice-5-fill' + | 'dice-5' + | 'dice-6-fill' + | 'dice-6' + | 'disc-fill' + | 'disc' + | 'discord' + | 'display-fill' + | 'display' + | 'distribute-horizontal' + | 'distribute-vertical' + | 'door-closed-fill' + | 'door-closed' + | 'door-open-fill' + | 'door-open' + | 'dot' + | 'download' + | 'droplet-fill' + | 'droplet-half' + | 'droplet' + | 'earbuds' + | 'easel-fill' + | 'easel' + | 'egg-fill' + | 'egg-fried' + | 'egg' + | 'eject-fill' + | 'eject' + | 'emoji-angry-fill' + | 'emoji-angry' + | 'emoji-dizzy-fill' + | 'emoji-dizzy' + | 'emoji-expressionless-fill' + | 'emoji-expressionless' + | 'emoji-frown-fill' + | 'emoji-frown' + | 'emoji-heart-eyes-fill' + | 'emoji-heart-eyes' + | 'emoji-laughing-fill' + | 'emoji-laughing' + | 'emoji-neutral-fill' + | 'emoji-neutral' + | 'emoji-smile-fill' + | 'emoji-smile-upside-down-fill' + | 'emoji-smile-upside-down' + | 'emoji-smile' + | 'emoji-sunglasses-fill' + | 'emoji-sunglasses' + | 'emoji-wink-fill' + | 'emoji-wink' + | 'envelope-fill' + | 'envelope-open-fill' + | 'envelope-open' + | 'envelope' + | 'eraser-fill' + | 'eraser' + | 'exclamation-circle-fill' + | 'exclamation-circle' + | 'exclamation-diamond-fill' + | 'exclamation-diamond' + | 'exclamation-octagon-fill' + | 'exclamation-octagon' + | 'exclamation-square-fill' + | 'exclamation-square' + | 'exclamation-triangle-fill' + | 'exclamation-triangle' + | 'exclamation' + | 'exclude' + | 'eye-fill' + | 'eye-slash-fill' + | 'eye-slash' + | 'eye' + | 'eyedropper' + | 'eyeglasses' + | 'facebook' + | 'file-arrow-down-fill' + | 'file-arrow-down' + | 'file-arrow-up-fill' + | 'file-arrow-up' + | 'file-bar-graph-fill' + | 'file-bar-graph' + | 'file-binary-fill' + | 'file-binary' + | 'file-break-fill' + | 'file-break' + | 'file-check-fill' + | 'file-check' + | 'file-code-fill' + | 'file-code' + | 'file-diff-fill' + | 'file-diff' + | 'file-earmark-arrow-down-fill' + | 'file-earmark-arrow-down' + | 'file-earmark-arrow-up-fill' + | 'file-earmark-arrow-up' + | 'file-earmark-bar-graph-fill' + | 'file-earmark-bar-graph' + | 'file-earmark-binary-fill' + | 'file-earmark-binary' + | 'file-earmark-break-fill' + | 'file-earmark-break' + | 'file-earmark-check-fill' + | 'file-earmark-check' + | 'file-earmark-code-fill' + | 'file-earmark-code' + | 'file-earmark-diff-fill' + | 'file-earmark-diff' + | 'file-earmark-easel-fill' + | 'file-earmark-easel' + | 'file-earmark-excel-fill' + | 'file-earmark-excel' + | 'file-earmark-fill' + | 'file-earmark-font-fill' + | 'file-earmark-font' + | 'file-earmark-image-fill' + | 'file-earmark-image' + | 'file-earmark-lock-fill' + | 'file-earmark-lock' + | 'file-earmark-lock2-fill' + | 'file-earmark-lock2' + | 'file-earmark-medical-fill' + | 'file-earmark-medical' + | 'file-earmark-minus-fill' + | 'file-earmark-minus' + | 'file-earmark-music-fill' + | 'file-earmark-music' + | 'file-earmark-person-fill' + | 'file-earmark-person' + | 'file-earmark-play-fill' + | 'file-earmark-play' + | 'file-earmark-plus-fill' + | 'file-earmark-plus' + | 'file-earmark-post-fill' + | 'file-earmark-post' + | 'file-earmark-ppt-fill' + | 'file-earmark-ppt' + | 'file-earmark-richtext-fill' + | 'file-earmark-richtext' + | 'file-earmark-ruled-fill' + | 'file-earmark-ruled' + | 'file-earmark-slides-fill' + | 'file-earmark-slides' + | 'file-earmark-spreadsheet-fill' + | 'file-earmark-spreadsheet' + | 'file-earmark-text-fill' + | 'file-earmark-text' + | 'file-earmark-word-fill' + | 'file-earmark-word' + | 'file-earmark-x-fill' + | 'file-earmark-x' + | 'file-earmark-zip-fill' + | 'file-earmark-zip' + | 'file-earmark' + | 'file-easel-fill' + | 'file-easel' + | 'file-excel-fill' + | 'file-excel' + | 'file-fill' + | 'file-font-fill' + | 'file-font' + | 'file-image-fill' + | 'file-image' + | 'file-lock-fill' + | 'file-lock' + | 'file-lock2-fill' + | 'file-lock2' + | 'file-medical-fill' + | 'file-medical' + | 'file-minus-fill' + | 'file-minus' + | 'file-music-fill' + | 'file-music' + | 'file-person-fill' + | 'file-person' + | 'file-play-fill' + | 'file-play' + | 'file-plus-fill' + | 'file-plus' + | 'file-post-fill' + | 'file-post' + | 'file-ppt-fill' + | 'file-ppt' + | 'file-richtext-fill' + | 'file-richtext' + | 'file-ruled-fill' + | 'file-ruled' + | 'file-slides-fill' + | 'file-slides' + | 'file-spreadsheet-fill' + | 'file-spreadsheet' + | 'file-text-fill' + | 'file-text' + | 'file-word-fill' + | 'file-word' + | 'file-x-fill' + | 'file-x' + | 'file-zip-fill' + | 'file-zip' + | 'file' + | 'files-alt' + | 'files' + | 'film' + | 'filter-circle-fill' + | 'filter-circle' + | 'filter-left' + | 'filter-right' + | 'filter-square-fill' + | 'filter-square' + | 'filter' + | 'flag-fill' + | 'flag' + | 'flower1' + | 'flower2' + | 'flower3' + | 'folder-check' + | 'folder-fill' + | 'folder-minus' + | 'folder-plus' + | 'folder-symlink-fill' + | 'folder-symlink' + | 'folder-x' + | 'folder' + | 'folder2-open' + | 'folder2' + | 'fonts' + | 'forward-fill' + | 'forward' + | 'front' + | 'fullscreen-exit' + | 'fullscreen' + | 'funnel-fill' + | 'funnel' + | 'gear-fill' + | 'gear-wide-connected' + | 'gear-wide' + | 'gear' + | 'gem' + | 'geo-alt-fill' + | 'geo-alt' + | 'geo-fill' + | 'geo' + | 'gift-fill' + | 'gift' + | 'github' + | 'globe' + | 'globe2' + | 'google' + | 'graph-down' + | 'graph-up' + | 'grid-1x2-fill' + | 'grid-1x2' + | 'grid-3x2-gap-fill' + | 'grid-3x2-gap' + | 'grid-3x2' + | 'grid-3x3-gap-fill' + | 'grid-3x3-gap' + | 'grid-3x3' + | 'grid-fill' + | 'grid' + | 'grip-horizontal' + | 'grip-vertical' + | 'hammer' + | 'hand-index-fill' + | 'hand-index-thumb-fill' + | 'hand-index-thumb' + | 'hand-index' + | 'hand-thumbs-down-fill' + | 'hand-thumbs-down' + | 'hand-thumbs-up-fill' + | 'hand-thumbs-up' + | 'handbag-fill' + | 'handbag' + | 'hash' + | 'hdd-fill' + | 'hdd-network-fill' + | 'hdd-network' + | 'hdd-rack-fill' + | 'hdd-rack' + | 'hdd-stack-fill' + | 'hdd-stack' + | 'hdd' + | 'headphones' + | 'headset' + | 'heart-fill' + | 'heart-half' + | 'heart' + | 'heptagon-fill' + | 'heptagon-half' + | 'heptagon' + | 'hexagon-fill' + | 'hexagon-half' + | 'hexagon' + | 'hourglass-bottom' + | 'hourglass-split' + | 'hourglass-top' + | 'hourglass' + | 'house-door-fill' + | 'house-door' + | 'house-fill' + | 'house' + | 'hr' + | 'hurricane' + | 'image-alt' + | 'image-fill' + | 'image' + | 'images' + | 'inbox-fill' + | 'inbox' + | 'inboxes-fill' + | 'inboxes' + | 'info-circle-fill' + | 'info-circle' + | 'info-square-fill' + | 'info-square' + | 'info' + | 'input-cursor-text' + | 'input-cursor' + | 'instagram' + | 'intersect' + | 'journal-album' + | 'journal-arrow-down' + | 'journal-arrow-up' + | 'journal-bookmark-fill' + | 'journal-bookmark' + | 'journal-check' + | 'journal-code' + | 'journal-medical' + | 'journal-minus' + | 'journal-plus' + | 'journal-richtext' + | 'journal-text' + | 'journal-x' + | 'journal' + | 'journals' + | 'joystick' + | 'justify-left' + | 'justify-right' + | 'justify' + | 'kanban-fill' + | 'kanban' + | 'key-fill' + | 'key' + | 'keyboard-fill' + | 'keyboard' + | 'ladder' + | 'lamp-fill' + | 'lamp' + | 'laptop-fill' + | 'laptop' + | 'layer-backward' + | 'layer-forward' + | 'layers-fill' + | 'layers-half' + | 'layers' + | 'layout-sidebar-inset-reverse' + | 'layout-sidebar-inset' + | 'layout-sidebar-reverse' + | 'layout-sidebar' + | 'layout-split' + | 'layout-text-sidebar-reverse' + | 'layout-text-sidebar' + | 'layout-text-window-reverse' + | 'layout-text-window' + | 'layout-three-columns' + | 'layout-wtf' + | 'life-preserver' + | 'lightbulb-fill' + | 'lightbulb-off-fill' + | 'lightbulb-off' + | 'lightbulb' + | 'lightning-charge-fill' + | 'lightning-charge' + | 'lightning-fill' + | 'lightning' + | 'link-45deg' + | 'link' + | 'linkedin' + | 'list-check' + | 'list-nested' + | 'list-ol' + | 'list-stars' + | 'list-task' + | 'list-ul' + | 'list' + | 'lock-fill' + | 'lock' + | 'mailbox' + | 'mailbox2' + | 'map-fill' + | 'map' + | 'markdown-fill' + | 'markdown' + | 'mask' + | 'megaphone-fill' + | 'megaphone' + | 'menu-app-fill' + | 'menu-app' + | 'menu-button-fill' + | 'menu-button-wide-fill' + | 'menu-button-wide' + | 'menu-button' + | 'menu-down' + | 'menu-up' + | 'mic-fill' + | 'mic-mute-fill' + | 'mic-mute' + | 'mic' + | 'minecart-loaded' + | 'minecart' + | 'moisture' + | 'moon-fill' + | 'moon-stars-fill' + | 'moon-stars' + | 'moon' + | 'mouse-fill' + | 'mouse' + | 'mouse2-fill' + | 'mouse2' + | 'mouse3-fill' + | 'mouse3' + | 'music-note-beamed' + | 'music-note-list' + | 'music-note' + | 'music-player-fill' + | 'music-player' + | 'newspaper' + | 'node-minus-fill' + | 'node-minus' + | 'node-plus-fill' + | 'node-plus' + | 'nut-fill' + | 'nut' + | 'octagon-fill' + | 'octagon-half' + | 'octagon' + | 'option' + | 'outlet' + | 'paint-bucket' + | 'palette-fill' + | 'palette' + | 'palette2' + | 'paperclip' + | 'paragraph' + | 'patch-check-fill' + | 'patch-check' + | 'patch-exclamation-fill' + | 'patch-exclamation' + | 'patch-minus-fill' + | 'patch-minus' + | 'patch-plus-fill' + | 'patch-plus' + | 'patch-question-fill' + | 'patch-question' + | 'pause-btn-fill' + | 'pause-btn' + | 'pause-circle-fill' + | 'pause-circle' + | 'pause-fill' + | 'pause' + | 'peace-fill' + | 'peace' + | 'pen-fill' + | 'pen' + | 'pencil-fill' + | 'pencil-square' + | 'pencil' + | 'pentagon-fill' + | 'pentagon-half' + | 'pentagon' + | 'people-fill' + | 'people' + | 'percent' + | 'person-badge-fill' + | 'person-badge' + | 'person-bounding-box' + | 'person-check-fill' + | 'person-check' + | 'person-circle' + | 'person-dash-fill' + | 'person-dash' + | 'person-fill' + | 'person-lines-fill' + | 'person-plus-fill' + | 'person-plus' + | 'person-square' + | 'person-x-fill' + | 'person-x' + | 'person' + | 'phone-fill' + | 'phone-landscape-fill' + | 'phone-landscape' + | 'phone-vibrate-fill' + | 'phone-vibrate' + | 'phone' + | 'pie-chart-fill' + | 'pie-chart' + | 'pin-angle-fill' + | 'pin-angle' + | 'pin-fill' + | 'pin' + | 'pip-fill' + | 'pip' + | 'play-btn-fill' + | 'play-btn' + | 'play-circle-fill' + | 'play-circle' + | 'play-fill' + | 'play' + | 'plug-fill' + | 'plug' + | 'plus-circle-dotted' + | 'plus-circle-fill' + | 'plus-circle' + | 'plus-square-dotted' + | 'plus-square-fill' + | 'plus-square' + | 'plus' + | 'power' + | 'printer-fill' + | 'printer' + | 'puzzle-fill' + | 'puzzle' + | 'question-circle-fill' + | 'question-circle' + | 'question-diamond-fill' + | 'question-diamond' + | 'question-octagon-fill' + | 'question-octagon' + | 'question-square-fill' + | 'question-square' + | 'question' + | 'rainbow' + | 'receipt-cutoff' + | 'receipt' + | 'reception-0' + | 'reception-1' + | 'reception-2' + | 'reception-3' + | 'reception-4' + | 'record-btn-fill' + | 'record-btn' + | 'record-circle-fill' + | 'record-circle' + | 'record-fill' + | 'record' + | 'record2-fill' + | 'record2' + | 'reply-all-fill' + | 'reply-all' + | 'reply-fill' + | 'reply' + | 'rss-fill' + | 'rss' + | 'rulers' + | 'save-fill' + | 'save' + | 'save2-fill' + | 'save2' + | 'scissors' + | 'screwdriver' + | 'search' + | 'segmented-nav' + | 'server' + | 'share-fill' + | 'share' + | 'shield-check' + | 'shield-exclamation' + | 'shield-fill-check' + | 'shield-fill-exclamation' + | 'shield-fill-minus' + | 'shield-fill-plus' + | 'shield-fill-x' + | 'shield-fill' + | 'shield-lock-fill' + | 'shield-lock' + | 'shield-minus' + | 'shield-plus' + | 'shield-shaded' + | 'shield-slash-fill' + | 'shield-slash' + | 'shield-x' + | 'shield' + | 'shift-fill' + | 'shift' + | 'shop-window' + | 'shop' + | 'shuffle' + | 'signpost-2-fill' + | 'signpost-2' + | 'signpost-fill' + | 'signpost-split-fill' + | 'signpost-split' + | 'signpost' + | 'sim-fill' + | 'sim' + | 'skip-backward-btn-fill' + | 'skip-backward-btn' + | 'skip-backward-circle-fill' + | 'skip-backward-circle' + | 'skip-backward-fill' + | 'skip-backward' + | 'skip-end-btn-fill' + | 'skip-end-btn' + | 'skip-end-circle-fill' + | 'skip-end-circle' + | 'skip-end-fill' + | 'skip-end' + | 'skip-forward-btn-fill' + | 'skip-forward-btn' + | 'skip-forward-circle-fill' + | 'skip-forward-circle' + | 'skip-forward-fill' + | 'skip-forward' + | 'skip-start-btn-fill' + | 'skip-start-btn' + | 'skip-start-circle-fill' + | 'skip-start-circle' + | 'skip-start-fill' + | 'skip-start' + | 'slack' + | 'slash-circle-fill' + | 'slash-circle' + | 'slash-square-fill' + | 'slash-square' + | 'slash' + | 'sliders' + | 'smartwatch' + | 'snow' + | 'snow2' + | 'snow3' + | 'sort-alpha-down-alt' + | 'sort-alpha-down' + | 'sort-alpha-up-alt' + | 'sort-alpha-up' + | 'sort-down-alt' + | 'sort-down' + | 'sort-numeric-down-alt' + | 'sort-numeric-down' + | 'sort-numeric-up-alt' + | 'sort-numeric-up' + | 'sort-up-alt' + | 'sort-up' + | 'soundwave' + | 'speaker-fill' + | 'speaker' + | 'speedometer' + | 'speedometer2' + | 'spellcheck' + | 'square-fill' + | 'square-half' + | 'square' + | 'stack' + | 'star-fill' + | 'star-half' + | 'star' + | 'stars' + | 'stickies-fill' + | 'stickies' + | 'sticky-fill' + | 'sticky' + | 'stop-btn-fill' + | 'stop-btn' + | 'stop-circle-fill' + | 'stop-circle' + | 'stop-fill' + | 'stop' + | 'stoplights-fill' + | 'stoplights' + | 'stopwatch-fill' + | 'stopwatch' + | 'subtract' + | 'suit-club-fill' + | 'suit-club' + | 'suit-diamond-fill' + | 'suit-diamond' + | 'suit-heart-fill' + | 'suit-heart' + | 'suit-spade-fill' + | 'suit-spade' + | 'sun-fill' + | 'sun' + | 'sunglasses' + | 'sunrise-fill' + | 'sunrise' + | 'sunset-fill' + | 'sunset' + | 'symmetry-horizontal' + | 'symmetry-vertical' + | 'table' + | 'tablet-fill' + | 'tablet-landscape-fill' + | 'tablet-landscape' + | 'tablet' + | 'tag-fill' + | 'tag' + | 'tags-fill' + | 'tags' + | 'telegram' + | 'telephone-fill' + | 'telephone-forward-fill' + | 'telephone-forward' + | 'telephone-inbound-fill' + | 'telephone-inbound' + | 'telephone-minus-fill' + | 'telephone-minus' + | 'telephone-outbound-fill' + | 'telephone-outbound' + | 'telephone-plus-fill' + | 'telephone-plus' + | 'telephone-x-fill' + | 'telephone-x' + | 'telephone' + | 'terminal-fill' + | 'terminal' + | 'text-center' + | 'text-indent-left' + | 'text-indent-right' + | 'text-left' + | 'text-paragraph' + | 'text-right' + | 'textarea-resize' + | 'textarea-t' + | 'textarea' + | 'thermometer-half' + | 'thermometer-high' + | 'thermometer-low' + | 'thermometer-snow' + | 'thermometer-sun' + | 'thermometer' + | 'three-dots-vertical' + | 'three-dots' + | 'toggle-off' + | 'toggle-on' + | 'toggle2-off' + | 'toggle2-on' + | 'toggles' + | 'toggles2' + | 'tools' + | 'tornado' + | 'trash-fill' + | 'trash' + | 'trash2-fill' + | 'trash2' + | 'tree-fill' + | 'tree' + | 'triangle-fill' + | 'triangle-half' + | 'triangle' + | 'trophy-fill' + | 'trophy' + | 'tropical-storm' + | 'truck-flatbed' + | 'truck' + | 'tsunami' + | 'tv-fill' + | 'tv' + | 'twitch' + | 'twitter' + | 'type-bold' + | 'type-h1' + | 'type-h2' + | 'type-h3' + | 'type-italic' + | 'type-strikethrough' + | 'type-underline' + | 'type' + | 'ui-checks-grid' + | 'ui-checks' + | 'ui-radios-grid' + | 'ui-radios' + | 'umbrella-fill' + | 'umbrella' + | 'union' + | 'unlock-fill' + | 'unlock' + | 'upc-scan' + | 'upc' + | 'upload' + | 'vector-pen' + | 'view-list' + | 'view-stacked' + | 'vinyl-fill' + | 'vinyl' + | 'voicemail' + | 'volume-down-fill' + | 'volume-down' + | 'volume-mute-fill' + | 'volume-mute' + | 'volume-off-fill' + | 'volume-off' + | 'volume-up-fill' + | 'volume-up' + | 'vr' + | 'wallet-fill' + | 'wallet' + | 'wallet2' + | 'watch' + | 'water' + | 'whatsapp' + | 'wifi-1' + | 'wifi-2' + | 'wifi-off' + | 'wifi' + | 'wind' + | 'window-dock' + | 'window-sidebar' + | 'window' + | 'wrench' + | 'x-circle-fill' + | 'x-circle' + | 'x-diamond-fill' + | 'x-diamond' + | 'x-octagon-fill' + | 'x-octagon' + | 'x-square-fill' + | 'x-square' + | 'x' + | 'youtube' + | 'zoom-in' + | 'zoom-out' + | 'bank' + | 'bank2' + | 'bell-slash-fill' + | 'bell-slash' + | 'cash-coin' + | 'check-lg' + | 'coin' + | 'currency-bitcoin' + | 'currency-dollar' + | 'currency-euro' + | 'currency-exchange' + | 'currency-pound' + | 'currency-yen' + | 'dash-lg' + | 'exclamation-lg' + | 'file-earmark-pdf-fill' + | 'file-earmark-pdf' + | 'file-pdf-fill' + | 'file-pdf' + | 'gender-ambiguous' + | 'gender-female' + | 'gender-male' + | 'gender-trans' + | 'headset-vr' + | 'info-lg' + | 'mastodon' + | 'messenger' + | 'piggy-bank-fill' + | 'piggy-bank' + | 'pin-map-fill' + | 'pin-map' + | 'plus-lg' + | 'question-lg' + | 'recycle' + | 'reddit' + | 'safe-fill' + | 'safe2-fill' + | 'safe2' + | 'sd-card-fill' + | 'sd-card' + | 'skype' + | 'slash-lg' + | 'translate' + | 'x-lg' + | 'safe' + | 'apple' + | 'microsoft' + | 'windows' + | 'behance' + | 'dribbble' + | 'line' + | 'medium' + | 'paypal' + | 'pinterest' + | 'signal' + | 'snapchat' + | 'spotify' + | 'stack-overflow' + | 'strava' + | 'wordpress' + | 'vimeo' + | 'activity' + | 'easel2-fill' + | 'easel2' + | 'easel3-fill' + | 'easel3' + | 'fan' + | 'fingerprint' + | 'graph-down-arrow' + | 'graph-up-arrow' + | 'hypnotize' + | 'magic' + | 'person-rolodex' + | 'person-video' + | 'person-video2' + | 'person-video3' + | 'person-workspace' + | 'radioactive' + | 'webcam-fill' + | 'webcam' + | 'yin-yang' + | 'bandaid-fill' + | 'bandaid' + | 'bluetooth' + | 'body-text' + | 'boombox' + | 'boxes' + | 'dpad-fill' + | 'dpad' + | 'ear-fill' + | 'ear' + | 'envelope-check-fill' + | 'envelope-check' + | 'envelope-dash-fill' + | 'envelope-dash' + | 'envelope-exclamation-fill' + | 'envelope-exclamation' + | 'envelope-plus-fill' + | 'envelope-plus' + | 'envelope-slash-fill' + | 'envelope-slash' + | 'envelope-x-fill' + | 'envelope-x' + | 'explicit-fill' + | 'explicit' + | 'git' + | 'infinity' + | 'list-columns-reverse' + | 'list-columns' + | 'meta' + | 'nintendo-switch' + | 'pc-display-horizontal' + | 'pc-display' + | 'pc-horizontal' + | 'pc' + | 'playstation' + | 'plus-slash-minus' + | 'projector-fill' + | 'projector' + | 'qr-code-scan' + | 'qr-code' + | 'quora' + | 'quote' + | 'robot' + | 'send-check-fill' + | 'send-check' + | 'send-dash-fill' + | 'send-dash' + | 'send-exclamation-fill' + | 'send-exclamation' + | 'send-fill' + | 'send-plus-fill' + | 'send-plus' + | 'send-slash-fill' + | 'send-slash' + | 'send-x-fill' + | 'send-x' + | 'send' + | 'steam' + | 'terminal-dash' + | 'terminal-plus' + | 'terminal-split' + | 'ticket-detailed-fill' + | 'ticket-detailed' + | 'ticket-fill' + | 'ticket-perforated-fill' + | 'ticket-perforated' + | 'ticket' + | 'tiktok' + | 'window-dash' + | 'window-desktop' + | 'window-fullscreen' + | 'window-plus' + | 'window-split' + | 'window-stack' + | 'window-x' + | 'xbox' + | 'ethernet' + | 'hdmi-fill' + | 'hdmi' + | 'usb-c-fill' + | 'usb-c' + | 'usb-fill' + | 'usb-plug-fill' + | 'usb-plug' + | 'usb-symbol' + | 'usb' + | 'boombox-fill' + | 'displayport' + | 'gpu-card' + | 'memory' + | 'modem-fill' + | 'modem' + | 'motherboard-fill' + | 'motherboard' + | 'optical-audio-fill' + | 'optical-audio' + | 'pci-card' + | 'router-fill' + | 'router' + | 'thunderbolt-fill' + | 'thunderbolt' + | 'usb-drive-fill' + | 'usb-drive' + | 'usb-micro-fill' + | 'usb-micro' + | 'usb-mini-fill' + | 'usb-mini' + | 'cloud-haze2' + | 'device-hdd-fill' + | 'device-hdd' + | 'device-ssd-fill' + | 'device-ssd' + | 'displayport-fill' + | 'mortarboard-fill' + | 'mortarboard' + | 'terminal-x' + | 'arrow-through-heart-fill' + | 'arrow-through-heart' + | 'badge-sd-fill' + | 'badge-sd' + | 'bag-heart-fill' + | 'bag-heart' + | 'balloon-fill' + | 'balloon-heart-fill' + | 'balloon-heart' + | 'balloon' + | 'box2-fill' + | 'box2-heart-fill' + | 'box2-heart' + | 'box2' + | 'braces-asterisk' + | 'calendar-heart-fill' + | 'calendar-heart' + | 'calendar2-heart-fill' + | 'calendar2-heart' + | 'chat-heart-fill' + | 'chat-heart' + | 'chat-left-heart-fill' + | 'chat-left-heart' + | 'chat-right-heart-fill' + | 'chat-right-heart' + | 'chat-square-heart-fill' + | 'chat-square-heart' + | 'clipboard-check-fill' + | 'clipboard-data-fill' + | 'clipboard-fill' + | 'clipboard-heart-fill' + | 'clipboard-heart' + | 'clipboard-minus-fill' + | 'clipboard-plus-fill' + | 'clipboard-pulse' + | 'clipboard-x-fill' + | 'clipboard2-check-fill' + | 'clipboard2-check' + | 'clipboard2-data-fill' + | 'clipboard2-data' + | 'clipboard2-fill' + | 'clipboard2-heart-fill' + | 'clipboard2-heart' + | 'clipboard2-minus-fill' + | 'clipboard2-minus' + | 'clipboard2-plus-fill' + | 'clipboard2-plus' + | 'clipboard2-pulse-fill' + | 'clipboard2-pulse' + | 'clipboard2-x-fill' + | 'clipboard2-x' + | 'clipboard2' + | 'emoji-kiss-fill' + | 'emoji-kiss' + | 'envelope-heart-fill' + | 'envelope-heart' + | 'envelope-open-heart-fill' + | 'envelope-open-heart' + | 'envelope-paper-fill' + | 'envelope-paper-heart-fill' + | 'envelope-paper-heart' + | 'envelope-paper' + | 'filetype-aac' + | 'filetype-ai' + | 'filetype-bmp' + | 'filetype-cs' + | 'filetype-css' + | 'filetype-csv' + | 'filetype-doc' + | 'filetype-docx' + | 'filetype-exe' + | 'filetype-gif' + | 'filetype-heic' + | 'filetype-html' + | 'filetype-java' + | 'filetype-jpg' + | 'filetype-js' + | 'filetype-jsx' + | 'filetype-key' + | 'filetype-m4p' + | 'filetype-md' + | 'filetype-mdx' + | 'filetype-mov' + | 'filetype-mp3' + | 'filetype-mp4' + | 'filetype-otf' + | 'filetype-pdf' + | 'filetype-php' + | 'filetype-png' + | 'filetype-ppt' + | 'filetype-psd' + | 'filetype-py' + | 'filetype-raw' + | 'filetype-rb' + | 'filetype-sass' + | 'filetype-scss' + | 'filetype-sh' + | 'filetype-svg' + | 'filetype-tiff' + | 'filetype-tsx' + | 'filetype-ttf' + | 'filetype-txt' + | 'filetype-wav' + | 'filetype-woff' + | 'filetype-xls' + | 'filetype-xml' + | 'filetype-yml' + | 'heart-arrow' + | 'heart-pulse-fill' + | 'heart-pulse' + | 'heartbreak-fill' + | 'heartbreak' + | 'hearts' + | 'hospital-fill' + | 'hospital' + | 'house-heart-fill' + | 'house-heart' + | 'incognito' + | 'magnet-fill' + | 'magnet' + | 'person-heart' + | 'person-hearts' + | 'phone-flip' + | 'plugin' + | 'postage-fill' + | 'postage-heart-fill' + | 'postage-heart' + | 'postage' + | 'postcard-fill' + | 'postcard-heart-fill' + | 'postcard-heart' + | 'postcard' + | 'search-heart-fill' + | 'search-heart' + | 'sliders2-vertical' + | 'sliders2' + | 'trash3-fill' + | 'trash3' + | 'valentine' + | 'valentine2' + | 'wrench-adjustable-circle-fill' + | 'wrench-adjustable-circle' + | 'wrench-adjustable' + | 'filetype-json' + | 'filetype-pptx' + | 'filetype-xlsx' + | '1-circle-fill' + | '1-circle' + | '1-square-fill' + | '1-square' + | '2-circle-fill' + | '2-circle' + | '2-square-fill' + | '2-square' + | '3-circle-fill' + | '3-circle' + | '3-square-fill' + | '3-square' + | '4-circle-fill' + | '4-circle' + | '4-square-fill' + | '4-square' + | '5-circle-fill' + | '5-circle' + | '5-square-fill' + | '5-square' + | '6-circle-fill' + | '6-circle' + | '6-square-fill' + | '6-square' + | '7-circle-fill' + | '7-circle' + | '7-square-fill' + | '7-square' + | '8-circle-fill' + | '8-circle' + | '8-square-fill' + | '8-square' + | '9-circle-fill' + | '9-circle' + | '9-square-fill' + | '9-square' + | 'airplane-engines-fill' + | 'airplane-engines' + | 'airplane-fill' + | 'airplane' + | 'alexa' + | 'alipay' + | 'android' + | 'android2' + | 'box-fill' + | 'box-seam-fill' + | 'browser-chrome' + | 'browser-edge' + | 'browser-firefox' + | 'browser-safari' + | 'c-circle-fill' + | 'c-circle' + | 'c-square-fill' + | 'c-square' + | 'capsule-pill' + | 'capsule' + | 'car-front-fill' + | 'car-front' + | 'cassette-fill' + | 'cassette' + | 'cc-circle-fill' + | 'cc-circle' + | 'cc-square-fill' + | 'cc-square' + | 'cup-hot-fill' + | 'cup-hot' + | 'currency-rupee' + | 'dropbox' + | 'escape' + | 'fast-forward-btn-fill' + | 'fast-forward-btn' + | 'fast-forward-circle-fill' + | 'fast-forward-circle' + | 'fast-forward-fill' + | 'fast-forward' + | 'filetype-sql' + | 'fire' + | 'google-play' + | 'h-circle-fill' + | 'h-circle' + | 'h-square-fill' + | 'h-square' + | 'indent' + | 'lungs-fill' + | 'lungs' + | 'microsoft-teams' + | 'p-circle-fill' + | 'p-circle' + | 'p-square-fill' + | 'p-square' + | 'pass-fill' + | 'pass' + | 'prescription' + | 'prescription2' + | 'r-circle-fill' + | 'r-circle' + | 'r-square-fill' + | 'r-square' + | 'repeat-1' + | 'repeat' + | 'rewind-btn-fill' + | 'rewind-btn' + | 'rewind-circle-fill' + | 'rewind-circle' + | 'rewind-fill' + | 'rewind' + | 'train-freight-front-fill' + | 'train-freight-front' + | 'train-front-fill' + | 'train-front' + | 'train-lightrail-front-fill' + | 'train-lightrail-front' + | 'truck-front-fill' + | 'truck-front' + | 'ubuntu' + | 'unindent' + | 'unity' + | 'universal-access-circle' + | 'universal-access' + | 'virus' + | 'virus2' + | 'wechat' + | 'yelp' + | 'sign-stop-fill' + | 'sign-stop-lights-fill' + | 'sign-stop-lights' + | 'sign-stop' + | 'sign-turn-left-fill' + | 'sign-turn-left' + | 'sign-turn-right-fill' + | 'sign-turn-right' + | 'sign-turn-slight-left-fill' + | 'sign-turn-slight-left' + | 'sign-turn-slight-right-fill' + | 'sign-turn-slight-right' + | 'sign-yield-fill' + | 'sign-yield' + | 'ev-station-fill' + | 'ev-station' + | 'fuel-pump-diesel-fill' + | 'fuel-pump-diesel' + | 'fuel-pump-fill' + | 'fuel-pump' + | '0-circle-fill' + | '0-circle' + | '0-square-fill' + | '0-square' + | 'rocket-fill' + | 'rocket-takeoff-fill' + | 'rocket-takeoff' + | 'rocket' + | 'stripe' + | 'subscript' + | 'superscript' + | 'trello' + | 'envelope-at-fill' + | 'envelope-at' + | 'regex' + | 'text-wrap' + | 'sign-dead-end-fill' + | 'sign-dead-end' + | 'sign-do-not-enter-fill' + | 'sign-do-not-enter' + | 'sign-intersection-fill' + | 'sign-intersection-side-fill' + | 'sign-intersection-side' + | 'sign-intersection-t-fill' + | 'sign-intersection-t' + | 'sign-intersection-y-fill' + | 'sign-intersection-y' + | 'sign-intersection' + | 'sign-merge-left-fill' + | 'sign-merge-left' + | 'sign-merge-right-fill' + | 'sign-merge-right' + | 'sign-no-left-turn-fill' + | 'sign-no-left-turn' + | 'sign-no-parking-fill' + | 'sign-no-parking' + | 'sign-no-right-turn-fill' + | 'sign-no-right-turn' + | 'sign-railroad-fill' + | 'sign-railroad' + | 'building-add' + | 'building-check' + | 'building-dash' + | 'building-down' + | 'building-exclamation' + | 'building-fill-add' + | 'building-fill-check' + | 'building-fill-dash' + | 'building-fill-down' + | 'building-fill-exclamation' + | 'building-fill-gear' + | 'building-fill-lock' + | 'building-fill-slash' + | 'building-fill-up' + | 'building-fill-x' + | 'building-fill' + | 'building-gear' + | 'building-lock' + | 'building-slash' + | 'building-up' + | 'building-x' + | 'buildings-fill' + | 'buildings' + | 'bus-front-fill' + | 'bus-front' + | 'ev-front-fill' + | 'ev-front' + | 'globe-americas' + | 'globe-asia-australia' + | 'globe-central-south-asia' + | 'globe-europe-africa' + | 'house-add-fill' + | 'house-add' + | 'house-check-fill' + | 'house-check' + | 'house-dash-fill' + | 'house-dash' + | 'house-down-fill' + | 'house-down' + | 'house-exclamation-fill' + | 'house-exclamation' + | 'house-gear-fill' + | 'house-gear' + | 'house-lock-fill' + | 'house-lock' + | 'house-slash-fill' + | 'house-slash' + | 'house-up-fill' + | 'house-up' + | 'house-x-fill' + | 'house-x' + | 'person-add' + | 'person-down' + | 'person-exclamation' + | 'person-fill-add' + | 'person-fill-check' + | 'person-fill-dash' + | 'person-fill-down' + | 'person-fill-exclamation' + | 'person-fill-gear' + | 'person-fill-lock' + | 'person-fill-slash' + | 'person-fill-up' + | 'person-fill-x' + | 'person-gear' + | 'person-lock' + | 'person-slash' + | 'person-up' + | 'scooter' + | 'taxi-front-fill' + | 'taxi-front' + | 'amd' + | 'database-add' + | 'database-check' + | 'database-dash' + | 'database-down' + | 'database-exclamation' + | 'database-fill-add' + | 'database-fill-check' + | 'database-fill-dash' + | 'database-fill-down' + | 'database-fill-exclamation' + | 'database-fill-gear' + | 'database-fill-lock' + | 'database-fill-slash' + | 'database-fill-up' + | 'database-fill-x' + | 'database-fill' + | 'database-gear' + | 'database-lock' + | 'database-slash' + | 'database-up' + | 'database-x' + | 'database' + | 'houses-fill' + | 'houses' + | 'nvidia' + | 'person-vcard-fill' + | 'person-vcard' + | 'sina-weibo' + | 'tencent-qq' + | 'wikipedia' + | 'alphabet-uppercase' + | 'alphabet' + | 'amazon' + | 'arrows-collapse-vertical' + | 'arrows-expand-vertical' + | 'arrows-vertical' + | 'arrows' + | 'ban-fill' + | 'ban' + | 'bing' + | 'cake' + | 'cake2' + | 'cookie' + | 'copy' + | 'crosshair' + | 'crosshair2' + | 'emoji-astonished-fill' + | 'emoji-astonished' + | 'emoji-grimace-fill' + | 'emoji-grimace' + | 'emoji-grin-fill' + | 'emoji-grin' + | 'emoji-surprise-fill' + | 'emoji-surprise' + | 'emoji-tear-fill' + | 'emoji-tear' + | 'envelope-arrow-down-fill' + | 'envelope-arrow-down' + | 'envelope-arrow-up-fill' + | 'envelope-arrow-up' + | 'feather' + | 'feather2' + | 'floppy-fill' + | 'floppy' + | 'floppy2-fill' + | 'floppy2' + | 'gitlab' + | 'highlighter' + | 'marker-tip' + | 'nvme-fill' + | 'nvme' + | 'opencollective' + | 'pci-card-network' + | 'pci-card-sound' + | 'radar' + | 'send-arrow-down-fill' + | 'send-arrow-down' + | 'send-arrow-up-fill' + | 'send-arrow-up' + | 'sim-slash-fill' + | 'sim-slash' + | 'sourceforge' + | 'substack' + | 'threads-fill' + | 'threads' + | 'transparency' + | 'twitter-x' + | 'type-h4' + | 'type-h5' + | 'type-h6' + | 'backpack-fill' + | 'backpack' + | 'backpack2-fill' + | 'backpack2' + | 'backpack3-fill' + | 'backpack3' + | 'backpack4-fill' + | 'backpack4' + | 'brilliance' + | 'cake-fill' + | 'cake2-fill' + | 'duffle-fill' + | 'duffle' + | 'exposure' + | 'gender-neuter' + | 'highlights' + | 'luggage-fill' + | 'luggage' + | 'mailbox-flag' + | 'mailbox2-flag' + | 'noise-reduction' + | 'passport-fill' + | 'passport' + | 'person-arms-up' + | 'person-raised-hand' + | 'person-standing-dress' + | 'person-standing' + | 'person-walking' + | 'person-wheelchair' + | 'shadows' + | 'suitcase-fill' + | 'suitcase-lg-fill' + | 'suitcase-lg' + | 'suitcase' + | 'suitcase2-fill' + | 'suitcase2' + | 'vignette'; export type BootstrapIcon = `bi-${BootstrapIconPart}`; diff --git a/src/types/database.ts b/src/types/database.ts index 5ffdf2b..422efee 100644 --- a/src/types/database.ts +++ b/src/types/database.ts @@ -1,75 +1,69 @@ -import type { ColumnType } from "kysely"; +/** + * This file was generated by kysely-codegen. + * Please do not edit it manually. + */ -export type Generated = T extends ColumnType - ? ColumnType - : ColumnType; +import type { ColumnType } from 'kysely'; -export interface Details { +export type Generated = + T extends ColumnType + ? ColumnType + : ColumnType; + +export interface Account { id: Generated; - image: number; - text: string; - type: number; + password: string; + username: string; } -export interface DetailTypes { +export interface Article { + content_md: string; + created_at: Generated; + description: string; + id: Generated; + title: string; + updated_at: Generated; +} + +export interface ArticleEquipment { + article_id: string; + equipment_id: number; id: Generated; - name: string; } export interface Equipment { id: Generated; link: string; name: string; - type: number; + type_id: number; } export interface EquipmentType { id: Generated; - name: string; + lang_key: string; + priority: Generated; } -export interface Gallery { - alt: string; +export interface Exposure { + article_id: string; + count: number; date: Date; + exposure_time_s: number; id: Generated; - name: string; + type: string; } -export interface GalleryEquipment { - equipment_id: number; - gallery_id: number; -} - -export interface Project { - date: Date; - description: string; - image_count: number; - name: string; - preview: string; - uuid: string; -} - -export interface ProjectImage { - id: Generated; - name: string; - project: string; -} - -export interface ProjectTags { - tag: number; - uuid: string; -} - -export interface Tag { - color: string; +export interface GalleryImage { + alt_text: string; + article_id: string; id: Generated; name: string; } -export interface User { - id: Generated; - password: string; - username: string; +export interface Translations { + key: string; + lang: string; + text: string; } export interface Visitors { @@ -77,19 +71,17 @@ export interface Visitors { id: Generated; ip: string; page: string; + user_agent: string; } export interface DB { - detail_types: DetailTypes; - details: Details; + account: Account; + article: Article; + article_equipment: ArticleEquipment; equipment: Equipment; equipment_type: EquipmentType; - gallery: Gallery; - gallery_equipment: GalleryEquipment; - project: Project; - project_image: ProjectImage; - project_tags: ProjectTags; - tag: Tag; - user: User; + exposure: Exposure; + gallery_image: GalleryImage; + translations: Translations; visitors: Visitors; } diff --git a/src/types/global.d.ts b/src/types/global.d.ts new file mode 100644 index 0000000..bcce256 --- /dev/null +++ b/src/types/global.d.ts @@ -0,0 +1,15 @@ +export {}; + +declare global { + interface ObjectConstructor { + keys<$Object extends object>(o: $Object): (keyof $Object)[]; + entries<$Object extends object>( + o: $Object + ): { [$Key in keyof $Object]: [$Key, $Object[$Key]] }[keyof $Object][]; + fromEntries<$Array extends readonly (readonly [PropertyKey, unknown])[]>( + entries: $Array + ): { + [$Key in $Array[number] as $Key[0]]: $Key[1]; + }; + } +} diff --git a/src/types/modules.d.ts b/src/types/modules.d.ts deleted file mode 100644 index 74294fc..0000000 --- a/src/types/modules.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -declare module 'dompurify' { - function sanitize(data: string): string; - function addHook(name: string, callback: (node: HTMLElement) => void): void; -} diff --git a/src/types/schemes.ts b/src/types/schemes.ts new file mode 100644 index 0000000..d6a92be --- /dev/null +++ b/src/types/schemes.ts @@ -0,0 +1,30 @@ +import { languages } from '$/lib/lang'; +import { z } from 'zod'; + +const articleImage = z.object({ + alt_text: z.string().min(1, 'article.noAltText'), + article_id: z.string(), + id: z.number().optional(), + name: z.string() +}); +const articleExposure = z.object({ + article_id: z.string(), + count: z.number(), + date: z.coerce.date(), + exposure_time_s: z.number(), + id: z.number().optional(), + type: z.string() +}); + +export const articleSchema = (lang: keyof typeof languages) => + z.object({ + id: z.string().optional(), + title: z.string().min(1, languages[lang].t.errors.article.noTitle), + description: z.string().min(1, languages[lang].t.errors.article.noDescrption), + content_md: z.string().min(1, languages[lang].t.errors.article.noContent), + created_at: z.coerce.date().optional(), + updated_at: z.coerce.date().optional(), + images: z.array(articleImage), + exposures: z.array(articleExposure), + equipment: z.array(z.number()) + }); diff --git a/src/types/types.ts b/src/types/types.ts index 20ebc7b..7a41015 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -1,103 +1,65 @@ -import { z } from 'zod'; -import type { - Equipment as DBEquipment, - EquipmentType as DBEquipmentType, - Tag as DBTag, - User as DBUser, - Gallery, - Project, - ProjectImage, - ProjectTags, - DetailTypes as DBDetailTypes -} from './database'; -import type { Snippet } from 'svelte'; +import type { ErrorApiResponse } from '@patrick115/sveltekitapi'; +import type { Selectable } from 'kysely'; +import type { ClassValue } from 'svelte/elements'; +import type { Account } from './database'; -export type User = Omit & { - id: number; -}; - -export type ProjectType = Omit & { - uuid: string; -}; +export type FormElement<$Attrs> = { + class?: ClassValue; + error?: string; +} & $Attrs; -export type RegularUser = Omit; +export type UserData = Omit, 'password'>; -export type Tag = Omit & { - id: number; -}; - -export type LoginData = - | { - logged: false; +export type UserState = + | { + logged: false; } - | { - logged: true; - data: RegularUser; + | { + logged: true; + data: UserData; }; -export type Response = { - status: true; -}; - -export type ResponseWithData = { - status: true; - data: T; -}; - -export const projectDataSchema = z.object({ - filePath: z.string({ - required_error: 'Nenahrál jsi náhled projektu' - }), - name: z.string().min(3, 'Jméno projektu musí mít minimálně 3 znaky').max(255, 'Jméno projektu musí mít maximálně 255 znaků'), - description: z.string().min(3, 'Popis projektu musí mít minimálně 3 znaky').max(1024, 'Popis projektu musí mít maximálně 1024 znaků'), - images: z.array(z.string()), - date: z.coerce.date({ - required_error: 'Vyplň datum, kdy byl projekt dokončen' - }) -}); - -export type ProjectData = z.infer; - -export type FullProjectData = ProjectType & { - images: Omit[]; - tags: Omit[]; -}; +export type ActionsResponse = Omit; -export type PublicProjectData = ProjectType & { - images: string[]; - tags: Tag[]; -}; - -export type DeArray = T extends (infer U)[] ? U : never; - -export type GalleryItem = Omit & { - id: number; - equipment: EquipmentInfo[]; - // details_categories: DetailTypes[]; -}; - -export type EquipmentType = Omit & { - id: number; -}; - -export type EquipmentInfo = Omit & { - id: number; - type_id: number; - type: string; -}; - -export type Equipment = Omit & { - id: number; +export type Response = { + status: true; }; -export type DetailTypes = Omit & { - id: number; +export type ResponseWithData<$Data> = Response & { + data: $Data; }; -export type Idk = 'string'; - -//https://stackoverflow.com/a/54178819 -export type PartialBy = Omit & Partial>; - -//https://stackoverflow.com/a/53050575 -export type NoUndefinedField = { [P in keyof T]-?: NoUndefinedField> }; +export type ImageExtension = 'jpg' | 'jpeg' | 'png' | 'webp' | 'tiff'; +export const extensions = [ + 'jpg', + 'jpeg', + 'png', + 'webp', + 'tiff' +] satisfies ImageExtension[]; + +export type DePromise<$Promise> = + $Promise extends Promise ? $Inner : $Promise; + +//Ty chatGPT :) +export type Path< + T, + P extends string = '', + Depth extends number[] = [] // depth counter +> = Depth['length'] extends 5 // <-- limit depth to 5 levels (adjust as needed) + ? P + : T extends string | number | boolean + ? P + : //eslint-disable-next-line @typescript-eslint/no-explicit-any + T extends Record + ? { + [K in keyof T]: Path< + T[K], + `${P}${P extends '' ? '' : '.'}${K & string}`, + [...Depth, 1] // increase depth + >; + }[keyof T] + : P; + +//eslint-disable-next-line @typescript-eslint/no-explicit-any +export const NROT = <$Type extends readonly any[]>(array: $Type) => array as [...$Type]; diff --git a/static/apple-touch-icon-114x114.png b/static/apple-touch-icon-114x114.png deleted file mode 100644 index d13c365..0000000 Binary files a/static/apple-touch-icon-114x114.png and /dev/null differ diff --git a/static/apple-touch-icon-120x120.png b/static/apple-touch-icon-120x120.png deleted file mode 100644 index 3e38089..0000000 Binary files a/static/apple-touch-icon-120x120.png and /dev/null differ diff --git a/static/apple-touch-icon-144x144.png b/static/apple-touch-icon-144x144.png deleted file mode 100644 index 9bb9f8b..0000000 Binary files a/static/apple-touch-icon-144x144.png and /dev/null differ diff --git a/static/apple-touch-icon-152x152.png b/static/apple-touch-icon-152x152.png deleted file mode 100644 index c153c30..0000000 Binary files a/static/apple-touch-icon-152x152.png and /dev/null differ diff --git a/static/apple-touch-icon-57x57.png b/static/apple-touch-icon-57x57.png deleted file mode 100644 index 2e030f8..0000000 Binary files a/static/apple-touch-icon-57x57.png and /dev/null differ diff --git a/static/apple-touch-icon-60x60.png b/static/apple-touch-icon-60x60.png deleted file mode 100644 index 0c91a66..0000000 Binary files a/static/apple-touch-icon-60x60.png and /dev/null differ diff --git a/static/apple-touch-icon-72x72.png b/static/apple-touch-icon-72x72.png deleted file mode 100644 index 4e843ab..0000000 Binary files a/static/apple-touch-icon-72x72.png and /dev/null differ diff --git a/static/apple-touch-icon-76x76.png b/static/apple-touch-icon-76x76.png deleted file mode 100644 index 5bec982..0000000 Binary files a/static/apple-touch-icon-76x76.png and /dev/null differ diff --git a/static/favicon-128.png b/static/favicon-128.png deleted file mode 100644 index b0be8ce..0000000 Binary files a/static/favicon-128.png and /dev/null differ diff --git a/static/favicon-16x16.png b/static/favicon-16x16.png deleted file mode 100644 index 857f7f4..0000000 Binary files a/static/favicon-16x16.png and /dev/null differ diff --git a/static/favicon-196x196.png b/static/favicon-196x196.png deleted file mode 100644 index d8c7b4c..0000000 Binary files a/static/favicon-196x196.png and /dev/null differ diff --git a/static/favicon-32x32.png b/static/favicon-32x32.png deleted file mode 100644 index 4b6f07f..0000000 Binary files a/static/favicon-32x32.png and /dev/null differ diff --git a/static/favicon-96x96.png b/static/favicon-96x96.png deleted file mode 100644 index 3f0c677..0000000 Binary files a/static/favicon-96x96.png and /dev/null differ diff --git a/static/favicon.ico b/static/favicon.ico index 3372784..e4c8dfe 100644 Binary files a/static/favicon.ico and b/static/favicon.ico differ diff --git a/static/favicon/apple-touch-icon-114x114.png b/static/favicon/apple-touch-icon-114x114.png new file mode 100644 index 0000000..5a7d6fe Binary files /dev/null and b/static/favicon/apple-touch-icon-114x114.png differ diff --git a/static/favicon/apple-touch-icon-120x120.png b/static/favicon/apple-touch-icon-120x120.png new file mode 100644 index 0000000..49f2708 Binary files /dev/null and b/static/favicon/apple-touch-icon-120x120.png differ diff --git a/static/favicon/apple-touch-icon-144x144.png b/static/favicon/apple-touch-icon-144x144.png new file mode 100644 index 0000000..fdc1b03 Binary files /dev/null and b/static/favicon/apple-touch-icon-144x144.png differ diff --git a/static/favicon/apple-touch-icon-152x152.png b/static/favicon/apple-touch-icon-152x152.png new file mode 100644 index 0000000..fc42cce Binary files /dev/null and b/static/favicon/apple-touch-icon-152x152.png differ diff --git a/static/favicon/apple-touch-icon-57x57.png b/static/favicon/apple-touch-icon-57x57.png new file mode 100644 index 0000000..5493606 Binary files /dev/null and b/static/favicon/apple-touch-icon-57x57.png differ diff --git a/static/favicon/apple-touch-icon-60x60.png b/static/favicon/apple-touch-icon-60x60.png new file mode 100644 index 0000000..2f8d6d8 Binary files /dev/null and b/static/favicon/apple-touch-icon-60x60.png differ diff --git a/static/favicon/apple-touch-icon-72x72.png b/static/favicon/apple-touch-icon-72x72.png new file mode 100644 index 0000000..61c5ed6 Binary files /dev/null and b/static/favicon/apple-touch-icon-72x72.png differ diff --git a/static/favicon/apple-touch-icon-76x76.png b/static/favicon/apple-touch-icon-76x76.png new file mode 100644 index 0000000..3d4fe47 Binary files /dev/null and b/static/favicon/apple-touch-icon-76x76.png differ diff --git a/static/favicon/favicon-128.png b/static/favicon/favicon-128.png new file mode 100644 index 0000000..2ce3f9c Binary files /dev/null and b/static/favicon/favicon-128.png differ diff --git a/static/favicon/favicon-16x16.png b/static/favicon/favicon-16x16.png new file mode 100644 index 0000000..96d615d Binary files /dev/null and b/static/favicon/favicon-16x16.png differ diff --git a/static/favicon/favicon-196x196.png b/static/favicon/favicon-196x196.png new file mode 100644 index 0000000..984852f Binary files /dev/null and b/static/favicon/favicon-196x196.png differ diff --git a/static/favicon/favicon-32x32.png b/static/favicon/favicon-32x32.png new file mode 100644 index 0000000..90755d6 Binary files /dev/null and b/static/favicon/favicon-32x32.png differ diff --git a/static/favicon/favicon-96x96.png b/static/favicon/favicon-96x96.png new file mode 100644 index 0000000..f09012b Binary files /dev/null and b/static/favicon/favicon-96x96.png differ diff --git a/static/favicon/mstile-144x144.png b/static/favicon/mstile-144x144.png new file mode 100644 index 0000000..fdc1b03 Binary files /dev/null and b/static/favicon/mstile-144x144.png differ diff --git a/static/favicon/mstile-150x150.png b/static/favicon/mstile-150x150.png new file mode 100644 index 0000000..03e2fa1 Binary files /dev/null and b/static/favicon/mstile-150x150.png differ diff --git a/static/favicon/mstile-310x150.png b/static/favicon/mstile-310x150.png new file mode 100644 index 0000000..28e6999 Binary files /dev/null and b/static/favicon/mstile-310x150.png differ diff --git a/static/favicon/mstile-310x310.png b/static/favicon/mstile-310x310.png new file mode 100644 index 0000000..9395c57 Binary files /dev/null and b/static/favicon/mstile-310x310.png differ diff --git a/static/favicon/mstile-70x70.png b/static/favicon/mstile-70x70.png new file mode 100644 index 0000000..2ce3f9c Binary files /dev/null and b/static/favicon/mstile-70x70.png differ diff --git a/static/images/Image-400.jpg b/static/images/Image-400.jpg new file mode 100644 index 0000000..00a50de Binary files /dev/null and b/static/images/Image-400.jpg differ diff --git a/static/images/Image-400.webp b/static/images/Image-400.webp new file mode 100644 index 0000000..bc8596f Binary files /dev/null and b/static/images/Image-400.webp differ diff --git a/static/images/Image-800.jpg b/static/images/Image-800.jpg new file mode 100644 index 0000000..b6efc56 Binary files /dev/null and b/static/images/Image-800.jpg differ diff --git a/static/images/Image-800.webp b/static/images/Image-800.webp new file mode 100644 index 0000000..c1ef42e Binary files /dev/null and b/static/images/Image-800.webp differ diff --git a/static/images/PFP.jpg b/static/images/PFP.jpg new file mode 100644 index 0000000..3d4d9c7 Binary files /dev/null and b/static/images/PFP.jpg differ diff --git a/static/images/code.png b/static/images/code.png deleted file mode 100644 index a4738c4..0000000 Binary files a/static/images/code.png and /dev/null differ diff --git a/static/images/code_hr.png b/static/images/code_hr.png deleted file mode 100644 index 052dac2..0000000 Binary files a/static/images/code_hr.png and /dev/null differ diff --git a/static/images/djs.png b/static/images/djs.png deleted file mode 100644 index f83d9fa..0000000 Binary files a/static/images/djs.png and /dev/null differ diff --git a/static/images/djs_hr.png b/static/images/djs_hr.png deleted file mode 100644 index dbc59fe..0000000 Binary files a/static/images/djs_hr.png and /dev/null differ diff --git a/static/images/example_project.png b/static/images/example_project.png deleted file mode 100644 index 45de8cd..0000000 Binary files a/static/images/example_project.png and /dev/null differ diff --git a/static/images/fei.png b/static/images/fei.png deleted file mode 100644 index 3104e53..0000000 Binary files a/static/images/fei.png and /dev/null differ diff --git a/static/images/fei_hr.png b/static/images/fei_hr.png deleted file mode 100644 index 0cfa20e..0000000 Binary files a/static/images/fei_hr.png and /dev/null differ diff --git a/static/images/icon.webp b/static/images/icon.webp deleted file mode 100644 index fffcf8e..0000000 Binary files a/static/images/icon.webp and /dev/null differ diff --git a/static/images/oldPage.png b/static/images/oldPage.png deleted file mode 100644 index cf77520..0000000 Binary files a/static/images/oldPage.png and /dev/null differ diff --git a/static/images/oldPage_hr.png b/static/images/oldPage_hr.png deleted file mode 100644 index 7394910..0000000 Binary files a/static/images/oldPage_hr.png and /dev/null differ diff --git a/static/images/pfp.jpeg b/static/images/pfp.jpeg deleted file mode 100644 index e75d334..0000000 Binary files a/static/images/pfp.jpeg and /dev/null differ diff --git a/static/images/sveltekit.png b/static/images/sveltekit.png deleted file mode 100644 index c8e1db9..0000000 Binary files a/static/images/sveltekit.png and /dev/null differ diff --git a/static/images/sveltekit_hr.png b/static/images/sveltekit_hr.png deleted file mode 100644 index 4031d05..0000000 Binary files a/static/images/sveltekit_hr.png and /dev/null differ diff --git a/static/images/webBuilder.jpg b/static/images/webBuilder.jpg deleted file mode 100644 index 6c9bfd9..0000000 Binary files a/static/images/webBuilder.jpg and /dev/null differ diff --git a/static/images/webBuilder_hr.jpg b/static/images/webBuilder_hr.jpg deleted file mode 100644 index a988782..0000000 Binary files a/static/images/webBuilder_hr.jpg and /dev/null differ diff --git a/static/mstile-144x144.png b/static/mstile-144x144.png deleted file mode 100644 index 9bb9f8b..0000000 Binary files a/static/mstile-144x144.png and /dev/null differ diff --git a/static/mstile-150x150.png b/static/mstile-150x150.png deleted file mode 100644 index 39d2939..0000000 Binary files a/static/mstile-150x150.png and /dev/null differ diff --git a/static/mstile-310x150.png b/static/mstile-310x150.png deleted file mode 100644 index 44244be..0000000 Binary files a/static/mstile-310x150.png and /dev/null differ diff --git a/static/mstile-310x310.png b/static/mstile-310x310.png deleted file mode 100644 index 4ef3764..0000000 Binary files a/static/mstile-310x310.png and /dev/null differ diff --git a/static/mstile-70x70.png b/static/mstile-70x70.png deleted file mode 100644 index b0be8ce..0000000 Binary files a/static/mstile-70x70.png and /dev/null differ diff --git a/svelte.config.js b/svelte.config.js index 44766ce..c73ea59 100644 --- a/svelte.config.js +++ b/svelte.config.js @@ -3,22 +3,22 @@ import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; /** @type {import('@sveltejs/kit').Config} */ const config = { - // Consult https://kit.svelte.dev/docs/integrations#preprocessors - // for more information about preprocessors - preprocess: vitePreprocess(), + // Consult https://kit.svelte.dev/docs/integrations#preprocessors + // for more information about preprocessors + preprocess: vitePreprocess(), - kit: { - // adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list. - // If your environment is not supported or you settled on a specific environment, switch out the adapter. - // See https://kit.svelte.dev/docs/adapters for more information about adapters. - adapter: adapter(), - alias: { - '$/*': 'src/*' - } + kit: { + // adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list. + // If your environment is not supported or you settled on a specific environment, switch out the adapter. + // See https://kit.svelte.dev/docs/adapters for more information about adapters. + adapter: adapter(), + alias: { + '$/*': 'src/*' }, - compilerOptions: { - runes: true + csrf: { + checkOrigin: process.env.NODE_ENV === 'production' } + } }; export default config; diff --git a/tailwind.config.js b/tailwind.config.js deleted file mode 100644 index c40e0a3..0000000 --- a/tailwind.config.js +++ /dev/null @@ -1,50 +0,0 @@ -/** @type {import('tailwindcss').Config} */ -export default { - content: ['./src/**/*.{html,js,svelte,ts}'], - theme: { - extend: { - fontFamily: { - 'fira-sans': ['Fira Sans', 'sans-serif'], - ubuntu: ['Ubuntu', 'sans-serif'], - oswald: ['Oswald', 'sans-serif'], - inter: ['Inter', 'sans-serif'], - poppins: ['Poppins', 'sans-serif'], - roboto: ['Roboto', 'sans-serif'] - }, - colors: { - text: '#e5f9fd', - background: '#01090c', - 'background-light': '#04222e', - primary: '#7be6f5', - secondary: '#991515', - accent: '#ef8537' - }, - - screens: { - '3xl': '1920px', - xsm: '480px' - }, - width: { - 112: '28rem' - }, - maxWidth: { - 112: '28rem' - }, - maxHeight: { - 112: '28rem', - 128: '32rem', - 144: '36rem' - }, - borderWidth: { - 1: '1px' - }, - lineClamp: { - 8: '8' - }, - scale: { - 102: '1.02' - } - } - }, - plugins: [] -}; diff --git a/tailwindcss.config.js b/tailwindcss.config.js new file mode 100644 index 0000000..646f5da --- /dev/null +++ b/tailwindcss.config.js @@ -0,0 +1,45 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + theme: { + extend: { + typography: () => ({ + theme: { + css: { + '--tw-prose-body': 'var(--color-text)', + '--tw-prose-headings': 'var(--color-text-strong)', + '--tw-prose-lead': 'var(--color-text-muted)', + '--tw-prose-links': 'var(--color-primary)', + '--tw-prose-bold': 'var(--color-text-strong)', + '--tw-prose-counters': 'var(--color-text-muted)', + '--tw-prose-bullets': 'var(--color-text-muted)', + '--tw-prose-hr': 'var(--color-divider)', + '--tw-prose-quotes': 'var(--color-text-muted)', + '--tw-prose-quote-borders': 'var(--color-border)', + '--tw-prose-captions': 'var(--color-text-muted)', + '--tw-prose-code': 'var(--color-primary-text)', + '--tw-prose-pre-code': 'var(--color-secondary-text)', + '--tw-prose-pre-bg': 'var(--color-surface)', + '--tw-prose-th-borders': 'var(--color-divider)', + '--tw-prose-td-borders': 'var(--color-border)', + '--tw-prose-invert-body': 'var(--color-text-inverse)', + '--tw-prose-invert-headings': 'var(--color-text-strong)', + '--tw-prose-invert-lead': 'var(--color-text-muted)', + '--tw-prose-invert-links': 'var(--color-primary-text)', + '--tw-prose-invert-bold': 'var(--color-text)', + '--tw-prose-invert-counters': 'var(--color-gray-600)', + '--tw-prose-invert-bullets': 'var(--color-gray-600)', + '--tw-prose-invert-hr': 'var(--color-border)', + '--tw-prose-invert-quotes': 'var(--color-text-muted)', + '--tw-prose-invert-quote-borders': 'var(--color-divider)', + '--tw-prose-invert-captions': 'var(--color-text-muted)', + '--tw-prose-invert-code': 'var(--color-primary-text)', + '--tw-prose-invert-pre-code': 'var(--color-text-inverse)', + '--tw-prose-invert-pre-bg': 'oklch(0.05 0.003 260)', + '--tw-prose-invert-th-borders': 'var(--color-divider)', + '--tw-prose-invert-td-borders': 'var(--color-border)' + } + } + }) + } + } +}; diff --git a/tsconfig.json b/tsconfig.json index 9960ed4..e2c1de2 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -9,9 +9,11 @@ "skipLibCheck": true, "sourceMap": true, "strict": true, - "moduleResolution": "bundler" + "moduleResolution": "bundler", + "types": ["./src/types/global.d.ts"] } - // Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias + // Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias + // except $lib which is handled by https://svelte.dev/docs/kit/configuration#files // // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes // from the referenced tsconfig.json - TypeScript does not merge them in diff --git a/vite.config.ts b/vite.config.ts index a3eb481..9e06dc7 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,6 +1,10 @@ +import tailwindcss from '@tailwindcss/vite'; import { sveltekit } from '@sveltejs/kit/vite'; import { defineConfig } from 'vite'; export default defineConfig({ - plugins: [sveltekit()] + plugins: [tailwindcss(), sveltekit()], + server: { + allowedHosts: ['pc.patrick115.eu', 'patrick115.eu'] + } });