From 8d712322c09f18622f5376cd461d19e3b46553ee Mon Sep 17 00:00:00 2001 From: Max Richter Date: Sat, 10 Jan 2026 19:28:09 +0100 Subject: [PATCH] fix: make it work with new vite --- .gitignore | 2 +- Dockerfile | 2 +- README.md | 13 +- assets/style.css | 3 - assets/styles.css | 1 + client.ts | 3 +- components/Button.tsx | 4 +- components/Card.tsx | 7 +- components/Image.tsx | 3 +- components/PageHero.tsx | 6 +- compose.yml | 10 +- deno.json | 86 ++-- deno.lock | 451 ++++-------------- drizzle/0011_reflective_frank_castle.sql | 7 + drizzle/meta/0011_snapshot.json | 309 ++++++++++++ drizzle/meta/_journal.json | 9 +- islands/Counter.tsx | 9 +- islands/KMenu.tsx | 9 +- islands/Link.tsx | 2 +- lib/db/schema.ts | 2 +- lib/db/sqlite.ts | 1 - lib/log/fs.ts | 3 +- lib/promise.ts | 105 ---- lib/recommendation.ts | 4 +- lib/taskManager.ts | 110 ----- lib/telegram.ts | 65 --- main.ts | 11 +- routes/_app.tsx | 30 +- routes/_error.tsx | 2 +- routes/_layout.tsx | 12 +- routes/_middleware.ts | 2 +- routes/admin/cache/index.tsx | 31 -- routes/admin/log/index.tsx | 81 ---- routes/admin/performance/index.tsx | 90 ---- routes/api/query/index.ts | 1 - routes/api/series/enhance/[name].ts | 5 +- routes/articles/[name].tsx | 4 +- routes/articles/index.tsx | 7 +- routes/index.tsx | 5 +- routes/recipes/[name].tsx | 4 +- routes/recipes/index.tsx | 13 +- routes/series/[name].tsx | 4 +- routes/series/index.tsx | 7 +- static/.gitignore | 12 + static/android-chrome-192x192.png | Bin 30901 -> 0 bytes static/android-chrome-256x256.png | Bin 12648 -> 0 bytes static/apple-touch-icon.png | Bin 27602 -> 0 bytes static/browserconfig.xml | 9 - static/favicon-16x16.png | Bin 1375 -> 0 bytes static/favicon-32x32.png | Bin 2364 -> 0 bytes static/favicon.ico | Bin 15086 -> 0 bytes static/global.css | 30 +- static/logo.svg | 19 - static/mstile-150x150.png | Bin 14637 -> 0 bytes static/noise.png | Bin 22606 -> 0 bytes static/og-image.jpg | Bin 189761 -> 0 bytes static/prism-twilight.css | 177 ------- static/safari-pinned-tab.svg | 35 -- static/site.webmanifest | 19 - static/splotch_1.png | Bin 403041 -> 0 bytes static/splotch_2.png | Bin 416869 -> 0 bytes static/splotch_3.png | Bin 399180 -> 0 bytes static/thumbhash.js | 271 ----------- tailwind.config.ts | 7 - vite.config.ts | 6 + ...timestamp-1768053860058-b3b215439a75c8.mjs | 14 - 66 files changed, 590 insertions(+), 1544 deletions(-) delete mode 100644 assets/style.css create mode 100644 assets/styles.css create mode 100644 drizzle/0011_reflective_frank_castle.sql create mode 100644 drizzle/meta/0011_snapshot.json delete mode 100644 lib/promise.ts delete mode 100644 lib/taskManager.ts delete mode 100644 lib/telegram.ts delete mode 100644 routes/admin/cache/index.tsx delete mode 100644 routes/admin/log/index.tsx delete mode 100644 routes/admin/performance/index.tsx create mode 100644 static/.gitignore delete mode 100644 static/android-chrome-192x192.png delete mode 100644 static/android-chrome-256x256.png delete mode 100644 static/apple-touch-icon.png delete mode 100644 static/browserconfig.xml delete mode 100644 static/favicon-16x16.png delete mode 100644 static/favicon-32x32.png delete mode 100644 static/favicon.ico delete mode 100644 static/logo.svg delete mode 100644 static/mstile-150x150.png delete mode 100644 static/noise.png delete mode 100755 static/og-image.jpg delete mode 100644 static/prism-twilight.css delete mode 100644 static/safari-pinned-tab.svg delete mode 100644 static/site.webmanifest delete mode 100644 static/splotch_1.png delete mode 100644 static/splotch_2.png delete mode 100644 static/splotch_3.png delete mode 100644 static/thumbhash.js delete mode 100644 tailwind.config.ts delete mode 100644 vite.config.ts.timestamp-1768053860058-b3b215439a75c8.mjs diff --git a/.gitignore b/.gitignore index 8cd8b2c..df698cb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ -# dotenv environment variable files .env .env.development.local .env.test.local @@ -10,3 +9,4 @@ data-dev/ _fresh/ node_modules/ mise.toml + diff --git a/Dockerfile b/Dockerfile index 209884d..58c0a8b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,7 +15,7 @@ COPY . . ENV DATA_DIR=/app/data RUN mkdir -p $DATA_DIR && \ - deno install --allow-import --allow-ffi --allow-scripts=npm:sharp -e main.ts &&\ + deno install --allow-import --allow-ffi -e main.ts &&\ sed -i -e 's/"deno"/"no-deno"/' node_modules/@libsql/client/package.json &&\ deno task build diff --git a/README.md b/README.md index 2820dea..b2e551f 100644 --- a/README.md +++ b/README.md @@ -5,18 +5,13 @@ Started" guide here: https://fresh.deno.dev/docs/getting-started ### Usage -Make sure to install Deno: https://deno.land/manual/getting_started/installation +Make sure to install Deno: +https://docs.deno.com/runtime/getting_started/installation -Then start the project: +Then start the project in development mode: ``` -deno task start +deno task dev ``` This will watch the project directory and restart as necessary. - -## FIX - -``` -sed -i -e 's/"deno"/"no-deno"/' node_modules/@libsql/client/package.json -``` diff --git a/assets/style.css b/assets/style.css deleted file mode 100644 index 9efd3cf..0000000 --- a/assets/style.css +++ /dev/null @@ -1,3 +0,0 @@ -@import "tailwindcss"; -@source "../components/*" -@source "../components/**/*" diff --git a/assets/styles.css b/assets/styles.css new file mode 100644 index 0000000..f1d8c73 --- /dev/null +++ b/assets/styles.css @@ -0,0 +1 @@ +@import "tailwindcss"; diff --git a/client.ts b/client.ts index aac395d..016cb07 100644 --- a/client.ts +++ b/client.ts @@ -1 +1,2 @@ -import "./assets/style.css"; +// Import CSS files here for hot module reloading to work. +import "./assets/styles.css"; diff --git a/components/Button.tsx b/components/Button.tsx index 4bed6a4..3d34f67 100644 --- a/components/Button.tsx +++ b/components/Button.tsx @@ -1,12 +1,10 @@ import { ButtonHTMLAttributes } from "preact"; -import { IS_BROWSER } from "fresh/runtime"; export function Button(props: ButtonHTMLAttributes) { return ( ); diff --git a/islands/KMenu.tsx b/islands/KMenu.tsx index 6e444e7..53e2c67 100644 --- a/islands/KMenu.tsx +++ b/islands/KMenu.tsx @@ -4,7 +4,6 @@ import { useEventListener } from "@lib/hooks/useEventListener.ts"; import { menus } from "@islands/KMenu/commands.ts"; import { MenuEntry } from "@islands/KMenu/types.ts"; import * as icons from "@components/icons.tsx"; -import { IS_BROWSER } from "fresh/runtime"; import { isKMenuOpen } from "@lib/kmenu.ts"; const KMenuEntry = ( { entry, activeIndex, index }: { @@ -155,17 +154,17 @@ export const KMenu = ( } else { input.current?.blur(); } - }, IS_BROWSER ? document?.body : undefined); + }, typeof document !== "undefined" ? document?.body : undefined); return (
{ const [date, ...rest] = line.split(" | "); const parsed = JSON.parse(rest.join(" | ")) as Log; + const dateObj = new Date(date); return { ...parsed, - date: new Date(date), + date: isNaN(dateObj.getTime()) ? new Date() : dateObj, } as Log; }); diff --git a/lib/promise.ts b/lib/promise.ts deleted file mode 100644 index 9078783..0000000 --- a/lib/promise.ts +++ /dev/null @@ -1,105 +0,0 @@ -/** - * Interface zur Beschreibung eines eingereihten Promises in der `PromiseQueue`. - */ -interface QueuedPromise { - promise: () => Promise; - resolve: (value: T | PromiseLike) => void; - reject: (reason?: unknown) => void; -} - -/** - * Eine einfache Promise Queue, die es ermöglicht mehrere Aufgaben in kontrollierter - * Reihenfolge abzuarbeiten. - * - * Lizenz: CC BY-NC-SA 4.0 - * (c) Peter Müller (https://crycode.de/promise-queue-in-typescript) - */ -export class PromiseQueue { - /** - * Eingereihte Promises. - */ - private queue: QueuedPromise[] = []; - - /** - * Indikator, dass aktuell ein Promise abgearbeitet wird. - */ - private working = false; - - /** - * Ein Promise einreihen. - * Dies fügt das Promise der Warteschlange hinzu. Wenn die Warteschlange leer - * ist, dann wird das Promise sofort gestartet. - * @param promise Funktion, die das Promise zurückgibt. - * @returns Ein Promise, welches eingelöst (oder zurückgewiesen) wird sobald das eingereihte Promise abgearbeitet ist. - */ - public enqueue(promise: () => Promise): Promise { - return new Promise((resolve, reject) => { - this.queue.push({ - promise, - resolve: resolve as (value: unknown) => void, - reject, - }); - this.dequeue(); - }); - } - - /** - * Das erste Promise aus der Warteschlange holen und starten, sofern nicht - * bereits ein Promise aktiv ist. - * @returns `true` wenn ein Promise aus der Warteschlange gestartet wurde oder `false` wenn bereits ein Promise aktiv oder die Warteschlange leer ist. - */ - private dequeue(): boolean { - if (this.working) { - return false; - } - - const item = this.queue.shift(); - if (!item) { - return false; - } - - try { - this.working = true; - item.promise() - .then((value) => { - item.resolve(value); - }) - .catch((err) => { - item.reject(err); - }) - .finally(() => { - this.working = false; - this.dequeue(); - }); - } catch (err) { - item.reject(err); - this.working = false; - this.dequeue(); - } - - return true; - } -} - -export class ConcurrentPromiseQueue { - /** - * Eingereihte Promises. - */ - private queues: PromiseQueue[] = []; - - constructor(concurrency: number = 1) { - this.queues = Array.from({ length: concurrency }).map(() => { - return new PromiseQueue(); - }); - } - - private queueIndex = 0; - private getQueue() { - this.queueIndex = (this.queueIndex + 1) % this.queues.length; - return this.queues[this.queueIndex]; - } - - public enqueue(promise: () => Promise): Promise { - return this.getQueue().enqueue(promise); - } -} diff --git a/lib/recommendation.ts b/lib/recommendation.ts index e38c5b1..d522151 100644 --- a/lib/recommendation.ts +++ b/lib/recommendation.ts @@ -61,7 +61,9 @@ export async function createRecommendationResource( const d = typeof datePublished === "string" ? new Date(datePublished) : datePublished; - resource.year = d.getFullYear(); + if (!isNaN(d.getTime())) { + resource.year = d.getFullYear(); + } } cache.set(cacheId, JSON.stringify(resource)); diff --git a/lib/taskManager.ts b/lib/taskManager.ts deleted file mode 100644 index e0c8013..0000000 --- a/lib/taskManager.ts +++ /dev/null @@ -1,110 +0,0 @@ -import { transcribe } from "@lib/openai.ts"; -import { createResource } from "@lib/marka/index.ts"; -import { createLogger } from "./log/index.ts"; -import { convertOggToMp3 } from "./helpers.ts"; - -const log = createLogger("taskManager"); - -// In-memory task state -const activeTasks: Record< - string, - { - noteName: string; - entries: Array< - { type: string; content: string | ArrayBufferLike; fileName?: string } - >; - } -> = {}; - -export function startTask(chatId: string, noteName: string) { - activeTasks[chatId] = { noteName, entries: [] }; - log.info(`Started note: ${noteName}`); -} - -export async function endTask(chatId: string): Promise { - const task = activeTasks[chatId]; - if (!task) return null; - - log.info("Ending note", task.noteName); - - let finalNote = `# ${task.noteName}\n\n`; - - const photoTasks: { content: ArrayBuffer; path: string }[] = []; - - let photoIndex = 0; - for (const entry of task.entries) { - if (entry.type === "text") { - finalNote += entry.content + "\n\n"; - } else if (entry.type === "voice") { - try { - log.info("Converting OGG to MP3"); - const mp3Data = await convertOggToMp3(entry.content as ArrayBuffer); - log.info("Finished converting OGG to MP3, transcribing..."); - const transcript = await transcribe(mp3Data); - finalNote += `**Voice Transcript:**\n${transcript}\n\n`; - log.info("Finished transcribing"); - } catch (error) { - log.error(error); - finalNote += "**[Voice message could not be transcribed]**\n\n"; - } - } else if (entry.type === "photo") { - const photoUrl = `${ - task.noteName.replace(/\.md$/, "") - }/photo-${photoIndex++}.jpg`; - - finalNote += `**Photo**:\n ${photoUrl}\n\n`; - photoTasks.push({ - content: entry.content as ArrayBuffer, - path: photoUrl, - }); - } - } - - try { - for (const entry of photoTasks) { - await createResource(entry.path, entry.content); - } - } catch (err) { - log.error("Error creating photo document:", err); - } - try { - await createResource(task.noteName, finalNote); - } catch (error) { - log.error("Error creating document:", error); - return error instanceof Error - ? error.toString() - : "Error creating document"; - } - - delete activeTasks[chatId]; - log.debug({ finalNote }); - return finalNote; -} - -export function addTextEntry(chatId: string, text: string) { - const task = activeTasks[chatId]; - if (!task) return; - const entry = { type: "text", content: text }; - log.debug("New Entry", entry); - task.entries.push(entry); -} - -export function addVoiceEntry(chatId: string, buffer: ArrayBufferLike) { - const task = activeTasks[chatId]; - if (!task) return; - const entry = { type: "voice", content: buffer }; - log.debug("New Entry", entry); - task.entries.push(entry); -} - -export function addPhotoEntry( - chatId: string, - buffer: ArrayBufferLike, - fileName: string, -) { - const task = activeTasks[chatId]; - if (!task) return; - const entry = { type: "photo", content: buffer, fileName }; - log.debug("New Entry", entry); - task.entries.push(entry); -} diff --git a/lib/telegram.ts b/lib/telegram.ts deleted file mode 100644 index e9597e4..0000000 --- a/lib/telegram.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { Bot } from "grammy"; -import { TELEGRAM_API_KEY } from "@lib/env.ts"; -import { createLogger } from "./log/index.ts"; - -const bot = new Bot(TELEGRAM_API_KEY); -const log = createLogger("telegram"); - -import * as manager from "./taskManager.ts"; - -async function downloadFile(filePath: string): Promise { - log.info(`Downloading file from path: ${filePath}`); - const url = `https://api.telegram.org/file/bot${bot.token}/${filePath}`; - const response = await fetch(url); - if (!response.ok) { - throw new Error("Failed to download file: " + response.statusText); - } - log.info("File downloaded successfully"); - const buffer = await response.arrayBuffer(); - return new Uint8Array(buffer); -} - -bot.command("start", async (ctx) => { - log.info("Received /start command"); - const [_, noteName] = ctx.message?.text?.split(" ") || []; - if (!noteName) { - return ctx.reply("Please provide a note name. Usage: /start NoteName"); - } - manager.startTask(ctx.chat.id.toString(), noteName); - await ctx.reply(`Started note: ${noteName}`); -}); - -bot.command("end", async (ctx) => { - log.info("Received /end command"); - const finalNote = await manager.endTask(ctx.chat.id.toString()); - if (!finalNote) return ctx.reply("No active note found."); - - try { - await ctx.reply("Note complete. Here is your markdown:"); - await ctx.reply(finalNote, { parse_mode: "MarkdownV2" }); - } catch (err) { - console.error("Error sending final note:", err); - } -}); - -bot.on("message:text", (ctx) => { - log.info("Received text message"); - manager.addTextEntry(ctx.chat.id.toString(), ctx.message.text); -}); - -bot.on("message:voice", async (ctx) => { - log.info("Received photo message"); - log.info("Received voice message"); - const file = await ctx.getFile(); - const buffer = await downloadFile(file.file_path!); - manager.addVoiceEntry(ctx.chat.id.toString(), buffer.buffer); -}); - -bot.on("message:photo", async (ctx) => { - const file = await ctx.getFile(); - const buffer = await downloadFile(file.file_path!); - const fileName = file.file_path!.split("/").pop()!; - manager.addPhotoEntry(ctx.chat.id.toString(), buffer.buffer, fileName); -}); - -bot.start(); diff --git a/main.ts b/main.ts index 1ab909c..f5bf0c5 100644 --- a/main.ts +++ b/main.ts @@ -1,7 +1,8 @@ import { App, staticFiles } from "fresh"; +import { type State } from "./utils.ts"; -export const app = new App() - // Add static file serving middleware - .use(staticFiles()) - // Enable file-system based routing - .fsRoutes(); +export const app = new App(); + +app.use(staticFiles()); + +app.fsRoutes(); diff --git a/routes/_app.tsx b/routes/_app.tsx index 210a4a1..79a4ea5 100644 --- a/routes/_app.tsx +++ b/routes/_app.tsx @@ -1,7 +1,6 @@ -import { PageProps } from "fresh"; -import { Partial } from "fresh/runtime"; +import { define } from "../utils.ts"; -export default function App({ Component }: PageProps) { +export default define.page(function ({ Component }) { return ( @@ -12,9 +11,6 @@ export default function App({ Component }: PageProps) { href="/favicon.png" /> - - - + + + + + + + + Memorium - - - - + + -