From 62f23224f43793c854810d2a5bfcb86411ea3834 Mon Sep 17 00:00:00 2001 From: toridoriv Date: Mon, 16 Oct 2023 19:59:45 -0300 Subject: [PATCH] :building_construction: Using Fresh instead of Express --- .vscode/settings.json | 2 +- bin/commands/cache.ts | 2 + components/deps.ts | 1 + components/head.tsx | 19 +++++ components/nav.tsx | 37 +++++++++ deno.json | 13 ++- dev.ts | 5 ++ fresh.config.ts | 7 ++ fresh.gen.ts | 17 ++++ main.ts | 9 +++ modules/config/deps.ts | 1 + modules/config/mod.ts | 11 +++ modules/config/schemas.ts | 54 +++++++++++++ routes/_app.tsx | 98 +++++++++++++++++++++++ routes/index.tsx | 27 +++++++ scripts.config.ts | 6 +- static/assets/android-chrome-192x192.png | Bin 0 -> 6657 bytes static/assets/android-chrome-512x512.png | Bin 0 -> 23153 bytes static/assets/apple-touch-icon.png | Bin 0 -> 5921 bytes static/assets/dev-site.webmanifest | 20 +++++ static/assets/favicon-16x16.png | Bin 0 -> 471 bytes static/assets/favicon-32x32.png | Bin 0 -> 919 bytes static/assets/favicon.ico | Bin 0 -> 15406 bytes static/assets/robots.txt | 2 + static/assets/site.webmanifest | 20 +++++ static/scripts/main.mjs | 10 +++ static/styles/main.css | 45 +++++++++++ 27 files changed, 401 insertions(+), 5 deletions(-) create mode 100644 components/deps.ts create mode 100644 components/head.tsx create mode 100644 components/nav.tsx create mode 100644 dev.ts create mode 100644 fresh.config.ts create mode 100644 fresh.gen.ts create mode 100644 main.ts create mode 100644 modules/config/deps.ts create mode 100644 modules/config/mod.ts create mode 100644 modules/config/schemas.ts create mode 100644 routes/_app.tsx create mode 100644 routes/index.tsx create mode 100644 static/assets/android-chrome-192x192.png create mode 100644 static/assets/android-chrome-512x512.png create mode 100644 static/assets/apple-touch-icon.png create mode 100644 static/assets/dev-site.webmanifest create mode 100644 static/assets/favicon-16x16.png create mode 100644 static/assets/favicon-32x32.png create mode 100644 static/assets/favicon.ico create mode 100644 static/assets/robots.txt create mode 100644 static/assets/site.webmanifest create mode 100644 static/scripts/main.mjs create mode 100644 static/styles/main.css diff --git a/.vscode/settings.json b/.vscode/settings.json index 8447c69..53efd06 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,5 @@ { - "[javascript][json][jsonc][typescript]": { + "[javascript][typescriptreact][json][jsonc][typescript]": { "editor.defaultFormatter": "denoland.vscode-deno" }, "[html]": { diff --git a/bin/commands/cache.ts b/bin/commands/cache.ts index 4b9e552..2e72cc7 100644 --- a/bin/commands/cache.ts +++ b/bin/commands/cache.ts @@ -35,6 +35,8 @@ function getDependencyFiles(isDevelopment: boolean) { match: [ /deps\./, new RegExp("express-serve-static-core.d.ts"), + new RegExp("dev.ts"), + new RegExp("^main.ts"), ], skip, }; diff --git a/components/deps.ts b/components/deps.ts new file mode 100644 index 0000000..85ffee2 --- /dev/null +++ b/components/deps.ts @@ -0,0 +1 @@ +export { type JSX } from "preact"; diff --git a/components/head.tsx b/components/head.tsx new file mode 100644 index 0000000..94e5667 --- /dev/null +++ b/components/head.tsx @@ -0,0 +1,19 @@ +import { JSX } from "@components/deps.ts"; + +export function Favicon(props: JSX.HTMLAttributes) { + return ; +} + +export function PreloadedStylesheet( + props: JSX.HTMLAttributes, +) { + return ( + + ); +} diff --git a/components/nav.tsx b/components/nav.tsx new file mode 100644 index 0000000..78a241a --- /dev/null +++ b/components/nav.tsx @@ -0,0 +1,37 @@ +import { JSX } from "./deps.ts"; + +export function NavItem( + { children, ...props }: JSX.HTMLAttributes, +) { + return ( + + ); +} + +export function Nav(props: JSX.HTMLAttributes[]) { + return ( + + ); +} diff --git a/deno.json b/deno.json index 6928c06..2a9d3cb 100644 --- a/deno.json +++ b/deno.json @@ -1,6 +1,8 @@ { "compilerOptions": { "exactOptionalPropertyTypes": true, + "jsx": "react-jsx", + "jsxImportSource": "preact", "noImplicitThis": true, "strictNullChecks": true }, @@ -16,7 +18,16 @@ "useTabs": false }, "imports": { - "@modules/": "./modules/" + "$fresh/": "https://deno.land/x/fresh@1.5.2/", + "@components/": "./components/", + "@islands/": "./islands/", + "@modules/": "./modules/", + "@preact/signals": "https://esm.sh/*@preact/signals@1.2.1", + "@preact/signals-core": "https://esm.sh/*@preact/signals-core@1.5.0", + "@routes/": "./routes/", + "preact": "https://esm.sh/preact@10.18.1", + "preact-render-to-string": "https://esm.sh/*preact-render-to-string@6.2.2", + "preact/": "https://esm.sh/preact@10.18.1/" }, "lint": { "exclude": [ diff --git a/dev.ts b/dev.ts new file mode 100644 index 0000000..e9d0465 --- /dev/null +++ b/dev.ts @@ -0,0 +1,5 @@ +import "https://deno.land/std@0.204.0/dotenv/load.ts"; +import dev from "$fresh/dev.ts"; +import config from "./fresh.config.ts"; + +await dev(import.meta.url, "./main.ts", config); diff --git a/fresh.config.ts b/fresh.config.ts new file mode 100644 index 0000000..7ca7d40 --- /dev/null +++ b/fresh.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from "$fresh/server.ts"; + +export default defineConfig({ + server: { + port: 3000, + }, +}); diff --git a/fresh.gen.ts b/fresh.gen.ts new file mode 100644 index 0000000..b085f85 --- /dev/null +++ b/fresh.gen.ts @@ -0,0 +1,17 @@ +// DO NOT EDIT. This file is generated by Fresh. +// This file SHOULD be checked into source version control. +// This file is automatically updated during development when running `dev.ts`. + +import * as $0 from "./routes/_app.tsx"; +import * as $1 from "./routes/index.tsx"; + +const manifest = { + routes: { + "./routes/_app.tsx": $0, + "./routes/index.tsx": $1, + }, + islands: {}, + baseUrl: import.meta.url, +}; + +export default manifest; diff --git a/main.ts b/main.ts new file mode 100644 index 0000000..190e44f --- /dev/null +++ b/main.ts @@ -0,0 +1,9 @@ +/// +/// +/// +/// +/// + +import "https://deno.land/std@0.204.0/dotenv/load.ts"; + +import { start } from "https://deno.land/x/fresh@1.5.1/server.ts"; diff --git a/modules/config/deps.ts b/modules/config/deps.ts new file mode 100644 index 0000000..a1746bc --- /dev/null +++ b/modules/config/deps.ts @@ -0,0 +1 @@ +export { z } from "https://deno.land/x/zod@v3.22.4/mod.ts"; diff --git a/modules/config/mod.ts b/modules/config/mod.ts new file mode 100644 index 0000000..5e20428 --- /dev/null +++ b/modules/config/mod.ts @@ -0,0 +1,11 @@ +import { + EnvironmentSchema, + PackageJsonSchema, +} from "@modules/config/schemas.ts"; +import PackageJson from "../../package.json" assert { type: "json" }; + +const env = EnvironmentSchema.parse(Deno.env.toObject()); + +const packageJson = PackageJsonSchema.parse(PackageJson); + +export default { ...env, ...packageJson }; diff --git a/modules/config/schemas.ts b/modules/config/schemas.ts new file mode 100644 index 0000000..54e4cc8 --- /dev/null +++ b/modules/config/schemas.ts @@ -0,0 +1,54 @@ +import { z } from "@modules/config/deps.ts"; + +const UrlSchema = z.string().url(); +const EmailSchema = z.string().email(); +const NotEmptyStringSchema = z.string().min(1); + +export const EnvironmentSchema = z.object({ + ENVIRONMENT: z + .preprocess(toUpperCase, z.enum(["DEVELOPMENT", "PRODUCTION"])) + .default("PRODUCTION"), + PORT: z.coerce.number().int().min(1000).default(3000), + MONGODB_URI: UrlSchema.default("mongodb://localhost"), + ADMIN_EMAILS: z.preprocess(split, z.array(EmailSchema)), + ADMIN_PASSWORDS: z.preprocess(split, z.array(NotEmptyStringSchema)), + LOCATION: UrlSchema, + LOG_LEVEL: z.string().default("INFO"), + PRETTY_LOG: z.coerce.boolean().default(false), +}); + +export const PackageJsonSchema = z + .object({ + name: z.string().min(1), + version: z.string().min(5), + homepage: UrlSchema, + }) + .transform(toUpperCaseKeys); + +function toUpperCase(value: T) { + if (typeof value === "string") { + return value.toUpperCase(); + } + + return value; +} + +function split(value: T) { + if (typeof value === "string") { + return value.split(","); + } + + return value; +} + +function toUpperCaseKeys(value: { + name: string; + version: string; + homepage: string; +}) { + return { + NAME: value.name, + VERSION: value.version, + HOMEPAGE: value.homepage, + }; +} diff --git a/routes/_app.tsx b/routes/_app.tsx new file mode 100644 index 0000000..fca0c93 --- /dev/null +++ b/routes/_app.tsx @@ -0,0 +1,98 @@ +import { AppProps } from "$fresh/server.ts"; +import { Nav } from "@components/nav.tsx"; +import { Favicon, PreloadedStylesheet } from "@components/head.tsx"; +import config from "@modules/config/mod.ts"; + +export interface AppProperties { + subtitle?: string; +} + +const links = [ + { children: "🏠 Home", href: "/" }, + { children: "📚 Catalog", href: "/catalog/pages/1" }, + { children: "🏷️ Tags", href: "/tags" }, + { children: "🌐 Languages", href: "/languages" }, +]; + +const favicons = [ + { + rel: "apple-touch-icon", + sizes: "180x180", + href: "/assets/apple-touch-icon.png", + }, + { + rel: "icon", + type: "image/png", + sizes: "32x32", + href: "/assets/favicon-32x32.png", + }, + { + rel: "icon", + type: "image/png", + sizes: "16x16", + href: "/assets/favicon-16x16.png", + }, + { + rel: "icon", + type: "image/x-icon", + sizes: "any", + href: "/assets/favicon.ico", + }, +]; + +const stylesheets = [ + { + href: "https://cdn.jsdelivr.net/npm/halfmoon@2.0.1/css/halfmoon.min.css", + }, + { + href: + "https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css", + }, + { + href: "https://cdn.jsdelivr.net/npm/victormono@latest/dist/index.min.css", + }, + { + href: "/styles/main.css", + }, +]; + +export default function App( + { Component, state, data }: AppProps, +) { + console.log(Component.displayName); + console.log(state); + const title = data.subtitle ? `${data.subtitle} - Duofiction` : "Duofiction"; + + return ( + + + + + + {title} + {favicons.map(Favicon)} + + {stylesheets.map(PreloadedStylesheet)} + + + {Nav(links)} +
+ +
+ + + + + ); +} diff --git a/routes/index.tsx b/routes/index.tsx new file mode 100644 index 0000000..3f6d6be --- /dev/null +++ b/routes/index.tsx @@ -0,0 +1,27 @@ +import { useState } from "preact/hooks"; +import { Handlers, RouteContext } from "$fresh/server.ts"; + +interface Data { + subtitle: string; +} +export const handler: Handlers = { + GET(req, ctx) { + console.log(req); + return ctx.render({ subtitle: "Home" }, {}); + }, +}; + +export default function Home(_: Request, ctx: RouteContext) { + // ctx.state.subtitle = "Home"; + return ( +
+
+

Welcome to Fresh

+

+ Try updating this message in the + ./routes/index.tsx file, and refresh. +

+
+
+ ); +} diff --git a/scripts.config.ts b/scripts.config.ts index dd2cb2d..3f86f11 100644 --- a/scripts.config.ts +++ b/scripts.config.ts @@ -3,7 +3,7 @@ import type { DenonConfig } from "https://deno.land/x/denon@2.5.0/mod.ts"; const config: DenonConfig = { scripts: { start: { - cmd: `deno run app/main.ts`, + cmd: `deno run dev.ts`, desc: "Run my webserver", watch: true, }, @@ -13,8 +13,8 @@ const config: DenonConfig = { watcher: { interval: 1_000, skip: ["**/.git/**", "**/bin/**"], - paths: ["app", "common"], - exts: ["ts", "css", "mjs", "json", "webmanifest"], + paths: ["islands", "modules", "routes", "components"], + exts: ["ts", "css", "mjs", "json", "webmanifest", "tsx"], }, }; diff --git a/static/assets/android-chrome-192x192.png b/static/assets/android-chrome-192x192.png new file mode 100644 index 0000000000000000000000000000000000000000..6291735c125249ca81d65c61784132e5012e5b5a GIT binary patch literal 6657 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 literal 0 HcmV?d00001 diff --git a/static/assets/android-chrome-512x512.png b/static/assets/android-chrome-512x512.png new file mode 100644 index 0000000000000000000000000000000000000000..eb5c51708faaf5e7654681377bb4c723ef2863dc GIT binary patch literal 23153 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 literal 0 HcmV?d00001 diff --git a/static/assets/apple-touch-icon.png b/static/assets/apple-touch-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ec1af68c624582d0fc90c2543022324eb38b2dde GIT binary patch literal 5921 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∋0 zosc2O`#v%8wmWa*0OXTq(#{INNjY5UCoB$#cg1iEEb3leZ9eY~5qA=Y@8yn5^^Od! zJ!DBu?3M-w;fwDQ^E%ACnLkfEu8%2nOH7!+Tc?zW%<8zkC69`&lDcM`+|8v+r?Ybd zca+8OHpk>zDCG#CUJHm<@#NXgNAR$Ha1Z=Q#Nl{?YnFQT*0$)93#|zEEfE%xkZ28l hv+M88w7>cfS84vh1zb~rrMUrs7b-86E0tb_{|~n>VGRHP literal 0 HcmV?d00001 diff --git a/static/assets/dev-site.webmanifest b/static/assets/dev-site.webmanifest new file mode 100644 index 0000000..dce70dc --- /dev/null +++ b/static/assets/dev-site.webmanifest @@ -0,0 +1,20 @@ +{ + "background_color": "#ffffff", + "display": "standalone", + "icons": [ + { + "sizes": "192x192", + "src": "/assets/android-chrome-192x192.png", + "type": "image/png" + }, + { + "sizes": "512x512", + "src": "/assets/android-chrome-512x512.png", + "type": "image/png" + } + ], + "name": "Duofiction", + "short_name": "Duofiction", + "start_url": "http://localhost:3000", + "theme_color": "#ffffff" +} diff --git a/static/assets/favicon-16x16.png b/static/assets/favicon-16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..bc5b88fc88609dc5a24b97f32e6b7e15b2af61e2 GIT binary patch literal 471 zcmV;|0Vw{7P)Px$kx4{BR5(walS?lHQ547jry3+4^{S$)A(51gU_q={*a-^@3o8p>fVHIi@&zoc z_yjBvD~XVFVc`*vHbiSC9f?QHbZX38nF+TulXjZ8yYtBJo^$@^(D}_%R5!kM2^fOs zF$P@Tr7<}+yk|z*<^>|nn?uRVR2+mgZ@{u00LwZsBki+Z2qmxY6JWGb9bhrAZ)$CG zf>5f210J&?-t!9QbhL)h-5LICp~SZ#A9I2r*zItDVHo%}Or97F)}SU3z{PC}L$Q9; z*VXbEV{9uXM3-EEjFiKtqVlAc<|cH7+mTmPi1G_e2OOZ&#hIyb7eG-9DC&B-QE;DC zQK+dAL^v6V^-Z_{FRyPXXvM04o=EtA05xkskqr9-T{Fdpq`X6*O+G}w7BUxaxZ0I1 z8QqVu1P6GB=*&zVy&|73DfAa3u=R>EIsw^?gp=(wf=YwaZGzEq#YOT?QxZkUx!kXt z&Nkw!;{;;9t6WJEw~Ri=Px&QAtEWR9Hv7mrG0&VGzgv^R*OEP&7OWiXa&Ag$ZhWa52QACl6@i(P%sv9~kti z;K_)I5N;-V_a-Eom>A_s445d`5G=H4X|cibl0J5K%&ruH-R^^;b7{KYJbvGNvore< zmZbml=oKO84uCxXDE-{*ml+rs@}X!;0iSHB9|Vi$DsPeBj%`{;3pDV>e_dhja$>0J zrUAAV7T{FFArL6Y5KT@X=moQ@*5{vOMoG2+syzWgMyjdG>$Q~$>i<0E6+)SG8r&!Y zX&=l864`^G0iUlzQF6X-B*2od0K?T(?JXKJ^ZhRY)VP)qI)K^v1%#%e02^})L8#tQ z4v!~CSA<3u3EXGYUpzb~`{)rHN&dkDF zydAqLyrvKexcx}o?t7*HlT*<@HDDrpt1D4huvybcNfJiJf`})Q)-MvF>uU4ao2CG& zqv+2V)JzzO5JKCn%jH^LRul!(GqV~*Cc7o&C+E&IwwMA~ZzBs0qQ}Sf*R+}fEc{wT zBs!hxEp!w;KQ9**rNw`3>;S02zlEn919u19ISYxuDj} ze+OV0kmC9xPA*xRM@2Rc2xWCqQ`E(R1Ox;|~(6{7yw8m{)?Y1z5Zz_{atq!=X+xW0 z*axgW$*cg>_3&f}v6#Lfg`m#%)@!#+hmoBC>(;S53)UUL%FqZPn~RB6ytV5DdUW(a z&D~W;kj>T1DS*Pbbo^lL^Hl)qpASQ$H$c?s)S(l70)`VY&#}hF%0(T31~Kfv46vFW t+t+hD01TcZqTXDIZr9h>Xd`%~{03;~62T-56*K?<002ovPDHLkV1n0nnjHWD literal 0 HcmV?d00001 diff --git a/static/assets/favicon.ico b/static/assets/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..fafcbdd336ac7ab3a6d5289be04c48b94fa1b159 GIT binary patch literal 15406 zcmeHOeQaA-6@Q}}YzU-H0s)1gz#p(bCj|V5sDuz3#My*ewxKbt+ExmzV@aGYE7j^2 z!KNf#7uJj=?b5Z&$84uv(xmxvW9QRJ9NTH)_#;gm+bOHvFK8Z0)6jFKUXtFe`8!k`D9%u&rFaZDxP$88THF+_6AXEdGR3Fke4NEQCStwKjR&M$aviG&+an_U zCUYgv?;Gg`ci#t+G<2#F`YY-(4~Zart$cX~$oHyeLdejh7;yuV#n-&<=cRLkb^G#BmN%E}K<4g;;Zaz2CDZ@q_W zRJEIzO{JK8y|y4Y=z`O~d07(%G5O@L40hX)teUB(z837;_o~IP)MP6C3fun^gXeUO zKR;*BzEutO9WTjZuosz^u>X&X=|A=V?9?bkrri3;KLTO*z&e_7IO>`oDY?I?=+D`; zm~Foy@>j6W&3Kmm$`#*XL52O#wg35Xppy0-LVZ>vKNtODb#}ae#V^;;ej#3?@71(l z$^MMF+JJm|AvPLk+UKaSTSrLeg%_)TYV7Z{$CZ^oJ3R`Hwv+1Sti85cD4XtC`AX|g z&^MTsO?@!bW6Q`Mb+$n)GMU-a(7r~DVzd5(O7gS)Dz)~m=o0^3SIaTn3!Q}P$@uDC zNVqTJ@00QPwQGx8mEwQ_-=_`rYN6BOXE1RC#{#Ww9T<4lF z7T{hU16A_l@sCsS?}*73lh607s#00Ltn56?SCmcibJ~AMwLck8q*96RQvFI-PWvxW z`HQM$^Vk-`{ynP3o0)s$9}D$AomxzMtg_v_Y`&bI@jQnZwC%rn`Q6qfoMS${vUv>u zk0r%PFht8ejie?{f}e5o0nh!>8BaOJXJ?Q;)1^DCE@^!ZdOtzgj``{FM<0cXty7lh@|C*cnmK1(!8^-DAOu*3ipn-ky>D<)l52WzBG4@A#W}v(^exMQc z#=`!eO5vy2%or$YbY*mV0-T%TvTJg_ftc1=d zg>efb@85g-H8@oMhvk@XDaM3lsPZp+O#gUz&lhYvET0E*hXj|R{Xt(;YrI(Ld(Ti8 z{B7U!aCo#g{WwWWHi8G{1~(`4Tll5x>Ffug~p(>eu$ckzIe%92XYE zXg{$RmOe}IdMW(4reUmn0Q?w#F^arHotZgW{rl&k`O#9@Sf&80_;-cow@z$(`fe%w zA^#9`wH!B8|63c5LTyP|?$|TA0cEwe9mV%Z;-4JK#2Jm8v+XC2g7wLA@%|LaSCAhA zdHke5GzQano=>qCBgos`4lO%22EP%07hA{d#0Yek9md>_G6TjyzW+%!#V~0MjHHRP zZP5ChCGYqt7U%3}gq{yh7)uzt@2IbVmZ$dR)CRk$?Dg7RyY7;jzd^K}dG#OOISMU1 z_lxEU$(?B{;wdL*1oNyYS0*1cXlnvjWgYY#s*$C(ot92(QRzEG zc9Xfx{rj+{ROlkQ&8y|W(t`JTUG>w5R;OFt2M}K(U%~0yWuhoB}yiLk2kZLd0N59j)TB}bu<#GOux0h@w zWj{kY{AW6Eycif;*9P*V==}J6+bLaZdRJ@xFIe3Cvm|~U`aCd_K#${DIc5Fp9^E>v)IWOoWMO~coOqa&_7f|Sm$S9zw?&GKPi`)Y(1T) zQ+|Wuc{+ctzFG#-m!se2^@S~^^e5YoI1WnjknHawu?oUX_=hl#rx|-Pp1yH4u^j-m zu(v80uP1&;`yH}1 z!m-nXpYkGZq+*XrVHUoZ!ml-cOEzX|tS{qp=Y&6*K%H5948WD~?}b>!?xpZ&F^K52 z>ff~L4`RQ5CGn&fPN{dB1%En~h+j*^9+$!?^?ozpU!JRrkE_)gVO#Qgey2u%uAt6b zPW(=YNy~j+&rcQc#}nymi<)(YZC~EbujTM#zoXtf0WNiRhQ%y=ru+f?pUaqPyQ0=6 zN#IBOp8hDAcuI&N*ZakY|A(0OVQl_}2!6_CQnRk=X%C-2gLx>%KuZt&4{7MVLI3~& literal 0 HcmV?d00001 diff --git a/static/assets/robots.txt b/static/assets/robots.txt new file mode 100644 index 0000000..77470cb --- /dev/null +++ b/static/assets/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: / \ No newline at end of file diff --git a/static/assets/site.webmanifest b/static/assets/site.webmanifest new file mode 100644 index 0000000..3aa94af --- /dev/null +++ b/static/assets/site.webmanifest @@ -0,0 +1,20 @@ +{ + "background_color": "#ffffff", + "display": "standalone", + "icons": [ + { + "sizes": "192x192", + "src": "/assets/android-chrome-192x192.png", + "type": "image/png" + }, + { + "sizes": "512x512", + "src": "/assets/android-chrome-512x512.png", + "type": "image/png" + } + ], + "name": "Duofiction", + "short_name": "Duofiction", + "start_url": "https://duofiction.deno.dev", + "theme_color": "#ffffff" +} diff --git a/static/scripts/main.mjs b/static/scripts/main.mjs new file mode 100644 index 0000000..d21c760 --- /dev/null +++ b/static/scripts/main.mjs @@ -0,0 +1,10 @@ +/// +import $ from "https://esm.sh/jquery@3.7.1"; + +$("#login-form").on("submit", function handleSubmitLogin() { + $("#redirect-to").attr("value", window.location.pathname); +}); + +$("#logout-form").on("submit", function handleClickLogout() { + $("#logout-redirect-to").attr("value", window.location.pathname); +}); diff --git a/static/styles/main.css b/static/styles/main.css new file mode 100644 index 0000000..6e21938 --- /dev/null +++ b/static/styles/main.css @@ -0,0 +1,45 @@ +:root { + --bs-success: var(--bs-emerald-700); +} + +.pagination { + --bs-pagination-active-bg: var(--bs-emerald-700); +} + +nav.navbar { + margin-bottom: 2em; +} + +nav.navbar form { + margin-bottom: 0; +} + +.navbar-nav { + align-items: center; +} + +article.card { + margin-bottom: 2em; +} + +.with-translation span:nth-of-type(1) { + font-size: x-large; +} + +.with-translation span:nth-of-type(2) { + font-size: medium; +} + +.btn.tag { + font-family: "Victor Mono", Monaco, Lucida, monospace; + font-size: smaller; +} + +.btn.tag.counter { + margin-right: 1em; + margin-top: 1em; +} + +.card .translation { + font-size: small; +} \ No newline at end of file