This commit is contained in:
core 2023-02-19 23:23:19 -05:00
parent ab366664f5
commit 4311b82976
Signed by: core
GPG Key ID: FDBF740DADDCEECF
33 changed files with 2305 additions and 0 deletions

13
tfweb/.eslintignore Normal file
View File

@ -0,0 +1,13 @@
.DS_Store
node_modules
/build
/.svelte-kit
/package
.env
.env.*
!.env.example
# Ignore files for PNPM, NPM and YARN
pnpm-lock.yaml
package-lock.json
yarn.lock

20
tfweb/.eslintrc.cjs Normal file
View File

@ -0,0 +1,20 @@
module.exports = {
root: true,
parser: '@typescript-eslint/parser',
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'],
plugins: ['svelte3', '@typescript-eslint'],
ignorePatterns: ['*.cjs'],
overrides: [{ files: ['*.svelte'], processor: 'svelte3/svelte3' }],
settings: {
'svelte3/typescript': () => require('typescript')
},
parserOptions: {
sourceType: 'module',
ecmaVersion: 2020
},
env: {
browser: true,
es2017: true,
node: true
}
};

10
tfweb/.gitignore vendored Normal file
View File

@ -0,0 +1,10 @@
.DS_Store
node_modules
/build
/.svelte-kit
/package
.env
.env.*
!.env.example
vite.config.js.timestamp-*
vite.config.ts.timestamp-*

5
tfweb/.idea/.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/

View File

@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
</profile>
</component>

8
tfweb/.idea/modules.xml Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/tfweb.iml" filepath="$PROJECT_DIR$/.idea/tfweb.iml" />
</modules>
</component>
</project>

12
tfweb/.idea/tfweb.iml Normal file
View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/temp" />
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
<excludeFolder url="file://$MODULE_DIR$/tmp" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

6
tfweb/.idea/vcs.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
</component>
</project>

1
tfweb/.npmrc Normal file
View File

@ -0,0 +1 @@
engine-strict=true

34
tfweb/package.json Normal file
View File

@ -0,0 +1,34 @@
{
"name": "tfweb",
"version": "0.0.1",
"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": "eslint .",
"typesafe-i18n": "typesafe-i18n"
},
"devDependencies": {
"@sveltejs/adapter-auto": "^2.0.0",
"@sveltejs/kit": "^1.5.0",
"@typescript-eslint/eslint-plugin": "^5.45.0",
"@typescript-eslint/parser": "^5.45.0",
"autoprefixer": "^10.4.13",
"eslint": "^8.28.0",
"eslint-plugin-svelte3": "^4.0.0",
"postcss": "^8.4.21",
"svelte": "^3.54.0",
"svelte-check": "^3.0.1",
"tailwindcss": "^3.2.7",
"tslib": "^2.4.1",
"typescript": "^4.9.5",
"vite": "^4.0.0"
},
"type": "module",
"dependencies": {
"a17t": "^0.10.1"
}
}

6
tfweb/postcss.config.cjs Normal file
View File

@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

3
tfweb/src/app.css Normal file
View File

@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

12
tfweb/src/app.d.ts vendored Normal file
View File

@ -0,0 +1,12 @@
// See https://kit.svelte.dev/docs/types#app
// for information about these interfaces
declare global {
namespace App {
// interface Error {}
// interface Locals {}
// interface PageData {}
// interface Platform {}
}
}
export {};

12
tfweb/src/app.html Normal file
View File

@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
<meta name="viewport" content="width=device-width" />
%sveltekit.head%
</head>
<body data-sveltekit-preload-data="hover" class="dark:bg-slate-900 bg-white text-zinc-900 dark:text-zinc-50">
<div style="display: contents;">%sveltekit.body%</div>
</body>
</html>

View File

@ -0,0 +1,6 @@
<script lang="ts">
export let tone = "~neutral";
export let priority = "";
</script>
<span class="badge {tone} {priority}"><slot/></span>

View File

@ -0,0 +1,93 @@
<script lang="ts">
import {t} from "../scripts/i18n";
import {theme} from "../stores/ThemeStore.js";
import {get} from "svelte/store";
import {onMount} from "svelte";
import {getCurrentLocale, locales} from "../scripts/i18n.js";
import {locale} from "../stores/LocaleStore";
function toggleTheme() {
if ($theme === "dark") {
theme.set("light");
} else {
theme.set("dark");
}
}
function setLocale(newLocale: string) {
locale.set(newLocale);
window.location.reload();
}
let dropdownOpen = false;
function toggleDropdown() {
dropdownOpen = !dropdownOpen;
}
function closeIfOpen() {
if (dropdownOpen) {
dropdownOpen = !dropdownOpen;
}
}
</script>
<header>
<nav class="flex items-center justify-between flex-wrap slate-300 dark:bg-slate-800 p-6 shadow-md">
<span class="font-semibold text-xl tracking-tight">{t("common.appName")}</span>
<div class="inline-block text-sm leading-none lg:m-0">
<div class="relative inline-block">
<div>
<button on:click={toggleDropdown} type="button"
class="inline-flex w-full justify-center rounded-md px-4 py-2" id="menu-button"
aria-expanded="true" aria-haspopup="true" title="{t('header.changeLang')}">
<i class="fa-solid fa-language"></i>
</button>
</div>
<div class:hidden={!dropdownOpen}
class="transition absolute right-0 z-10 mt-2 w-56 origin-top-right rounded-md bg-slate-100 dark:bg-slate-700 shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none"
role="menu" aria-orientation="vertical" aria-labelledby="menu-button" tabindex="-1" on:mouseleave={closeIfOpen}>
<div class="p-2" role="none">
<div class="rounded p-2 bg-slate-200 dark:bg-slate-600" role="menuitem" tabindex="-1">
<div>
<span class="mr-3 fi fi-{t('common.flag')}"></span>
<span class="mt-0.2">{t("common.localeName")} - {t("common.selected")}</span>
</div>
</div>
{#each Object.keys(locales) as locale, i}
{#if getCurrentLocale() != locale}
<div class="rounded p-2 mt-2 hover:bg-slate-300 hover:dark:bg-slate-800 transition"
role="menuitem" tabindex="-1" on:click={() => {setLocale(locale)}}>
<div>
<span class="mr-3 fi fi-{t('common.flag', locale)}"></span>
<span class="mt-0.2">{t("common.localeName", locale)}</span>
</div>
</div>
{/if}
{/each}
</div>
</div>
</div>
{#if $theme === "dark"}
<button title="{t('header.lightMode')}" class="inline-block text-sm px-4 leading-none mt-4 lg:mt-0"
on:click={toggleTheme}>
<i class="fa-solid fa-sun"></i>
</button>
{:else}
<button title="{t('header.darkMode')}" class="inline-block text-sm px-4 leading-none mt-4 lg:mt-0"
on:click={toggleTheme}>
<i class="fa-solid fa-moon"></i>
</button>
{/if}
</div>
</nav>
</header>

View File

@ -0,0 +1,24 @@
<script lang="ts">
import {onMount} from "svelte";
import {Logger, logSetup} from "../scripts/logger";
import {theme} from "../stores/ThemeStore";
import {get} from "svelte/store";
onMount(() => {
let logger = new Logger("Theme.svelte");
logSetup();
theme.subscribe((newTheme) => {
if (newTheme === "dark") {
document.documentElement.classList.add("dark");
} else {
document.documentElement.classList.remove("dark");
}
});
});
</script>
<svelte:head>
<link rel="stylesheet" href="https://cdn.e3t.cc/fa/6.2.0/css/all.min.css"/>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/lipis/flag-icons@6.6.6/css/flag-icons.min.css"/>
</svelte:head>

13
tfweb/src/i18n/en.json Normal file
View File

@ -0,0 +1,13 @@
{
"common": {
"localeName": "English",
"appName": "trifid",
"flag": "gb",
"selected": "selected"
},
"header": {
"lightMode": "Light mode",
"darkMode": "Dark mode",
"changeLang": "Change language"
}
}

13
tfweb/src/i18n/jp.json Normal file
View File

@ -0,0 +1,13 @@
{
"common": {
"localeName": "日本語",
"appName": "trifid",
"flag": "jp",
"selected": "選択済み"
},
"header": {
"lightMode": "ライトモード",
"darkMode": "ダークモード",
"changeLang": "言語の変更"
}
}

13
tfweb/src/i18n/nl.json Normal file
View File

@ -0,0 +1,13 @@
{
"common": {
"localeName": "Nederlands",
"appName": "trifid",
"flag": "nl",
"selected": "Geselecteerd"
},
"header": {
"lightMode": "Lichtmodus",
"darkMode": "Donkere modus",
"changeLang": "Taal wijzigen"
}
}

View File

@ -0,0 +1,11 @@
<script lang="ts">
import "../app.css";
import Theme from '../components/Theme.svelte';
import Header from "../components/Header.svelte";
</script>
<Theme/>
<Header/>
<slot />

View File

@ -0,0 +1,3 @@
<script>
import Header from "../../../components/Header.svelte";
</script>

View File

@ -0,0 +1,11 @@
import type { Writable } from "svelte/store";
import {writable} from "svelte/store";
import {browser} from "$app/environment";
export function persist(name: string, def_val = ""): Writable<any> {
const store = writable(browser && localStorage.getItem(name) || def_val);
store.subscribe((value: any) => {
if (browser) return (localStorage.setItem(name, value));
});
return store;
}

50
tfweb/src/scripts/i18n.ts Normal file
View File

@ -0,0 +1,50 @@
import {locale} from "../stores/LocaleStore";
import {get} from "svelte/store";
import en from "../i18n/en.json";
import jp from "../i18n/jp.json";
import nl from "../i18n/nl.json";
import {Logger, logSetup} from "./logger";
logSetup();
const logger = new Logger("i18n");
export const locales: any = {
"en": en,
"jp": jp,
"nl": nl
}
export function getCurrentLocale(): string {
return get(locale);
}
export function setLocale(newLocale: string) {
logger.info(`Setting locale to ${newLocale}`);
if (!(newLocale in locales)) {
logger.error(`Locale ${newLocale} does not exist`);
return false;
}
locale.set(newLocale);
return true;
}
export function t(query: string, locale = ""): string {
let val: any = locales[locale === "" ? getCurrentLocale() : locale];
const spl: Array<string> = query.split('.');
for (let i = 0; i < spl.length; i++) {
const item = spl[i];
if (item in val) {
val = val[item];
} else {
logger.info(`missing locale string ${query}`);
val = "missing: " + query;
break;
}
}
if (typeof val === "object") {
logger.info(`query string ${query} is not a message`);
return "missing: " + query;
}
return val;
}

118
tfweb/src/scripts/logger.ts Normal file
View File

@ -0,0 +1,118 @@
// LOG LEVELS:
// 0: Fatal
// 1: Error
// 2: Warn
// 3: Info
// 4: Debug
// Logs are written in-memory and logged to console if logLevel > showLogLevel
let loglevel = 0;
export const LEVEL_FATAL = 0;
export const LEVEL_ERROR = 1;
export const LEVEL_WARN = 2;
export const LEVEL_INFO = 3;
export const LEVEL_DEBUG = 4;
interface LogLine {
level: number,
module: string,
message: string,
timestamp: number,
deltaStart: number,
deltaLast: number
}
const debugLog: Array<LogLine> = [];
let timestampStart: number;
let deltaTimestamp: number;
let logger: Logger;
let loggerSetup = false;
export function logSetup() {
if (loggerSetup) return;
loggerSetup = true;
timestampStart = Date.now();
deltaTimestamp = Date.now();
logger = new Logger("Logger.ts");
consoleLogLevel(LEVEL_INFO);
logger.info("Logger setup complete");
}
function log(level: number, module: string, message: string) {
const log = {
level: level,
module: module,
message: message,
timestamp: Date.now(),
deltaStart: Date.now() - timestampStart,
deltaLast: Date.now() - deltaTimestamp
};
deltaTimestamp = Date.now();
debugLog.push(log);
consoleLogHandler(log);
}
function consoleLogHandler(log: LogLine) {
if (import.meta.env.SSR) return; // no logging in ssr
if (log.level <= loglevel) {
let textstyle = "";
switch (log.level) {
case 0:
textstyle = "color:red;font-weight:bold;";
break;
case 1:
textstyle = "color:red;";
break;
case 2:
textstyle = "color:yellow;";
break;
case 3:
textstyle = "";
break;
case 4:
textstyle = "color:gray;";
break;
}
console.log(`%c${new Date(log.timestamp).toISOString()} ${(log.deltaStart / 1000).toFixed(3)}Σ ${(log.deltaLast / 1000).toFixed(3)}Δ\n%c[${log.module}]%c ${log.message}`, "color:gray", "color:#8c188c", textstyle);
}
}
export function consoleLogLevel(level: number) {
loglevel = level;
}
export function consoleLogDump() {
for (let i = 0; i < debugLog.length; i++) {
consoleLogHandler(debugLog[i]);
}
}
export class Logger {
private name: string;
constructor(name: string) {
this.name = name;
}
fatal(message: any) {
log(LEVEL_FATAL, this.name, message)
}
error(message: any) {
log(LEVEL_ERROR, this.name, message)
}
warn(message: any) {
log(LEVEL_WARN, this.name, message)
}
info(message: any) {
log(LEVEL_INFO, this.name, message)
}
debug(message: any) {
log(LEVEL_DEBUG, this.name, message)
}
}

View File

@ -0,0 +1,3 @@
import { persist } from "../scripts/PersistentStore";
export const locale = persist("lang", "en");

View File

@ -0,0 +1,3 @@
import { persist } from "../scripts/PersistentStore";
export const theme = persist("theme", "light");

BIN
tfweb/static/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

15
tfweb/svelte.config.js Normal file
View File

@ -0,0 +1,15 @@
import adapter from '@sveltejs/adapter-auto';
import { vitePreprocess } from '@sveltejs/kit/vite';
/** @type {import('@sveltejs/kit').Config} */
const config = {
// Consult https://kit.svelte.dev/docs/integrations#preprocessors
// for more information about preprocessors
preprocess: vitePreprocess(),
kit: {
adapter: adapter()
}
};
export default config;

22
tfweb/tailwind.config.cjs Normal file
View File

@ -0,0 +1,22 @@
/** @type {import('tailwindcss').Config} */
const colors = require("tailwindcss/colors.js");
module.exports = {
content: ['./src/**/*.{html,js,svelte,ts}'],
theme: {
extend: {
colors: {
neutral: colors.slate,
positive: colors.green,
urge: colors.violet,
warning: colors.yellow,
info: colors.blue,
critical: colors.red
}
},
},
plugins: [
require('a17t')
],
darkMode: 'class'
}

17
tfweb/tsconfig.json Normal file
View File

@ -0,0 +1,17 @@
{
"extends": "./.svelte-kit/tsconfig.json",
"compilerOptions": {
"allowJs": true,
"checkJs": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"sourceMap": true,
"strict": true
}
// Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias
//
// 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
}

6
tfweb/vite.config.ts Normal file
View File

@ -0,0 +1,6 @@
import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';
export default defineConfig({
plugins: [sveltekit()]
});

1736
tfweb/yarn.lock Normal file

File diff suppressed because it is too large Load Diff