From 08f923875ccd97f39cb19e764709a3dd8c157f2f Mon Sep 17 00:00:00 2001 From: a Date: Sun, 20 Oct 2024 21:48:23 -0500 Subject: [PATCH] noot --- .gitignore | 11 +++++++ README.md | 16 ++++++++++ app.config.ts | 15 +++++++++ components/Button.tsx | 12 +++++++ deno.json | 45 ++++++++++++++++++++++++++ dev.ts | 9 ++++++ doorgan.yml | 26 +++++++++++++++ fresh.config.ts | 6 ++++ fresh.gen.ts | 27 ++++++++++++++++ generate.ts | 7 ++++ islands/Counter.tsx | 16 ++++++++++ islands/MagicPage.tsx | 64 +++++++++++++++++++++++++++++++++++++ lib/config/index.ts | 57 +++++++++++++++++++++++++++++++++ lib/config/types.ts | 23 +++++++++++++ lib/config/types.zod.ts | 26 +++++++++++++++ lib/container.ts | 3 ++ main.ts | 14 ++++++++ routes/[page].ts/index.tsx | 18 +++++++++++ routes/_404.tsx | 27 ++++++++++++++++ routes/_app.tsx | 34 ++++++++++++++++++++ routes/index.tsx | 17 ++++++++++ static/favicon.ico | Bin 0 -> 22382 bytes static/logo.svg | 6 ++++ static/styles.css | 3 ++ tailwind.config.ts | 7 ++++ 25 files changed, 489 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 app.config.ts create mode 100644 components/Button.tsx create mode 100644 deno.json create mode 100755 dev.ts create mode 100644 doorgan.yml create mode 100644 fresh.config.ts create mode 100644 fresh.gen.ts create mode 100644 generate.ts create mode 100644 islands/Counter.tsx create mode 100644 islands/MagicPage.tsx create mode 100644 lib/config/index.ts create mode 100644 lib/config/types.ts create mode 100644 lib/config/types.zod.ts create mode 100644 lib/container.ts create mode 100644 main.ts create mode 100644 routes/[page].ts/index.tsx create mode 100644 routes/_404.tsx create mode 100644 routes/_app.tsx create mode 100644 routes/index.tsx create mode 100644 static/favicon.ico create mode 100644 static/logo.svg create mode 100644 static/styles.css create mode 100644 tailwind.config.ts diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5b4bdef --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# Fresh build directory +_fresh/ +# npm dependencies +node_modules/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..ec0e33e --- /dev/null +++ b/README.md @@ -0,0 +1,16 @@ +# Fresh project + +Your new Fresh project is ready to go. You can follow the Fresh "Getting +Started" guide here: https://fresh.deno.dev/docs/getting-started + +### Usage + +Make sure to install Deno: https://deno.land/manual/getting_started/installation + +Then start the project: + +``` +deno task start +``` + +This will watch the project directory and restart as necessary. diff --git a/app.config.ts b/app.config.ts new file mode 100644 index 0000000..f83b9f7 --- /dev/null +++ b/app.config.ts @@ -0,0 +1,15 @@ +import { container } from "#/lib/container.ts"; +import { ConfigLoader } from "#/lib/config/index.ts"; +const configLoader = await ConfigLoader.New( + "doorgan.yml", + "doorgan.yaml", + Deno.env.get("HOME")+"/.config/doorgan.yml", + Deno.env.get("HOME")+"/.config/doorgan.yaml" +) +container.bind({ + provide: ConfigLoader, + useFactory: () => { + return configLoader; + } +}) + diff --git a/components/Button.tsx b/components/Button.tsx new file mode 100644 index 0000000..f1b80a0 --- /dev/null +++ b/components/Button.tsx @@ -0,0 +1,12 @@ +import { JSX } from "preact"; +import { IS_BROWSER } from "$fresh/runtime.ts"; + +export function Button(props: JSX.HTMLAttributes) { + return ( + +

{props.count}

+ + + ); +} diff --git a/islands/MagicPage.tsx b/islands/MagicPage.tsx new file mode 100644 index 0000000..e073cac --- /dev/null +++ b/islands/MagicPage.tsx @@ -0,0 +1,64 @@ +import type { Config, Item, Page } from "#/lib/config/types.ts"; +import { useState } from "preact/hooks"; + + +interface MagicPageProps { + config: Config; + page: Page; +} + + +export default function MagicPage(props: MagicPageProps) { + const {page, config} = props + const [iframeUrl, setIframeUrl]= useState(undefined) + return ( +
+
+ { + page?.categories?.map((category, idx) => { + return ( +
+

{category.name}

+
+ { + category.items?.map((item,idx) => { + return ( +
+
{ + console.log("click event", e) + if(e.buttons === 4) { + globalThis.window.open(item.href, "_blank") + } else if(e.buttons === 1) { + if(item.iframe) { + setIframeUrl(item.href) + } else{ + globalThis.window.open(item.href, "_blank") + } + } + }}> +

{item.iframe && "!"}{item.name}

+
+
+ ); + }) + } +
+
+ ); + }) + } +
+
+ {iframeUrl && + + } +
+
+ ); +} diff --git a/lib/config/index.ts b/lib/config/index.ts new file mode 100644 index 0000000..f1b7631 --- /dev/null +++ b/lib/config/index.ts @@ -0,0 +1,57 @@ +import type { Config } from "#/lib/config/types.ts"; +import { configSchema } from "#/lib/config/types.zod.ts"; +import { parse } from "jsr:@std/yaml"; +import { debounce } from "jsr:@std/async/debounce"; + +export * from "#/lib/config/types.ts" + +export class ConfigLoader { + private currentConfig: Config + private path: string + + constructor(firstConfig: Config, path:string) { + this.path = path + this.currentConfig = firstConfig + const run = debounce(async (e: Deno.FsEvent)=>{ + console.log("config file hmr", e); + const fileText = await Deno.readTextFile(path) + const parsed = parse(fileText) + return [await configSchema.parseAsync(parsed), path] + }, 250) + const watch = async ()=>{ + const watcher = Deno.watchFs(path) + for await (const event of watcher) { + if(event.kind === "remove") { + return + } + run(event) + } + } + watch() + } + + static async New(...paths: string[]) { + const [config, configPath] = await this.firstLoad(paths) + return new ConfigLoader(config, configPath) + + } + config(): Config { + return this.currentConfig + } + static async firstLoad(paths: string[]): Promise<[Config, string]>{ + const errs = [] + for (const path of paths) { + let fileText:string + try { + fileText = await Deno.readTextFile(path) + } catch (e) { + errs.push(e) + console.error(`Failed to load config from ${path}: ${e}`) + continue + } + const parsed = parse(fileText) + return [await configSchema.parseAsync(parsed), path] + } + throw new AggregateError(errs) + } +} diff --git a/lib/config/types.ts b/lib/config/types.ts new file mode 100644 index 0000000..bf3b471 --- /dev/null +++ b/lib/config/types.ts @@ -0,0 +1,23 @@ +export interface Config { + title?: string; + favicon?: string; + faviconType?: string; + pages?: Page[]; +} + +export interface Page { + name?: string; + title?: string; + categories?: Category[]; +} + +export interface Category { + name?: string; + items?: Item[]; +} + +export interface Item { + name: string; + href: string; + iframe?: boolean; +} diff --git a/lib/config/types.zod.ts b/lib/config/types.zod.ts new file mode 100644 index 0000000..c5cff50 --- /dev/null +++ b/lib/config/types.zod.ts @@ -0,0 +1,26 @@ +// Generated by ts-to-zod +import { z } from "zod"; + +export const itemSchema = z.object({ + name: z.string(), + href: z.string(), + iframe: z.boolean().optional(), +}); + +export const categorySchema = z.object({ + name: z.string().optional(), + items: z.array(itemSchema).optional(), +}); + +export const pageSchema = z.object({ + name: z.string().optional(), + title: z.string().optional(), + categories: z.array(categorySchema).optional(), +}); + +export const configSchema = z.object({ + title: z.string().optional(), + favicon: z.string().optional(), + faviconType: z.string().optional(), + pages: z.array(pageSchema).optional(), +}); diff --git a/lib/container.ts b/lib/container.ts new file mode 100644 index 0000000..695a6cc --- /dev/null +++ b/lib/container.ts @@ -0,0 +1,3 @@ +import { Container } from "@needle-di/core"; + +export const container = new Container(); diff --git a/main.ts b/main.ts new file mode 100644 index 0000000..c50bb36 --- /dev/null +++ b/main.ts @@ -0,0 +1,14 @@ +/// +/// +/// +/// +/// + +import "$std/dotenv/load.ts"; +import "#/app.config.ts"; + +import { start } from "$fresh/server.ts"; +import manifest from "#/fresh.gen.ts"; +import config from "#/fresh.config.ts"; + +await start(manifest, config); diff --git a/routes/[page].ts/index.tsx b/routes/[page].ts/index.tsx new file mode 100644 index 0000000..ec1292f --- /dev/null +++ b/routes/[page].ts/index.tsx @@ -0,0 +1,18 @@ +import {useContext} from "preact/hooks"; +import { ConfigContext } from "#/routes/_app.tsx"; +import type { PageProps } from "$fresh/server.ts"; + + +export default function SubPage(props: PageProps) { + const config = useContext(ConfigContext); + const page = config.pages?.find((page) => page.title === props.params.page); + if(!page) { + return
page not found
+ } + return ( +
+
+
+
+ ); +} diff --git a/routes/_404.tsx b/routes/_404.tsx new file mode 100644 index 0000000..c63ae2e --- /dev/null +++ b/routes/_404.tsx @@ -0,0 +1,27 @@ +import { Head } from "$fresh/runtime.ts"; + +export default function Error404() { + return ( + <> + + 404 - Page not found + +
+
+ the Fresh logo: a sliced lemon dripping with juice +

404 - Page not found

+

+ The page you were looking for doesn't exist. +

+ Go back home +
+
+ + ); +} diff --git a/routes/_app.tsx b/routes/_app.tsx new file mode 100644 index 0000000..e7ec1ff --- /dev/null +++ b/routes/_app.tsx @@ -0,0 +1,34 @@ +import { type PageProps } from "$fresh/server.ts"; +import { Partial } from "$fresh/runtime.ts"; +import { container } from "#/lib/container.ts"; +import { ConfigLoader } from "#/lib/config/index.ts"; +import { createContext } from "preact" +import type { Config } from "#/lib/config/types.ts"; +const configLoader = container.get(ConfigLoader); +const config = configLoader.config() +export const ConfigContext = createContext(config) + + +export default function App({ Component }: PageProps) { + const config = configLoader.config() + return ( + + + + + {config.title || "new tab"} + + { + config.favicon && + } + + + + + + + + + + ); +} diff --git a/routes/index.tsx b/routes/index.tsx new file mode 100644 index 0000000..a697169 --- /dev/null +++ b/routes/index.tsx @@ -0,0 +1,17 @@ +import { container } from "#/lib/container.ts"; +import { ConfigLoader } from "#/lib/config/index.ts"; +import MagicPage from "#/islands/MagicPage.tsx"; + + + + +export default function HomePage() { + const configLoader = container.get(ConfigLoader); + const config = configLoader.config() + let page = config.pages?.find((page) => page.name === undefined || page.name === "index"); + if(page === undefined) { + page = { + } + } + return ; +} diff --git a/static/favicon.ico b/static/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..1cfaaa2193b0f210107a559f7421569f57a25388 GIT binary patch literal 22382 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 bck@HGWY%2idMme??%p8ZW3z(%VE+9-Ofn0d literal 0 HcmV?d00001 diff --git a/static/logo.svg b/static/logo.svg new file mode 100644 index 0000000..ef2fbe4 --- /dev/null +++ b/static/logo.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/static/styles.css b/static/styles.css new file mode 100644 index 0000000..bd6213e --- /dev/null +++ b/static/styles.css @@ -0,0 +1,3 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; \ No newline at end of file diff --git a/tailwind.config.ts b/tailwind.config.ts new file mode 100644 index 0000000..995bc39 --- /dev/null +++ b/tailwind.config.ts @@ -0,0 +1,7 @@ +import { type Config } from "tailwindcss"; + +export default { + content: [ + "{routes,islands,components}/**/*.{ts,tsx,js,jsx}", + ], +} satisfies Config;