diff --git a/packages/nano-remix/scripts/build.ts b/packages/nano-remix/scripts/build.ts index 36b74f2..7043137 100644 --- a/packages/nano-remix/scripts/build.ts +++ b/packages/nano-remix/scripts/build.ts @@ -20,14 +20,9 @@ const buildDynamicRoute = async ({ distDir, routeName, filepath }: BuildRouteOpt const dynamicRouteFilepath = join(distDir, "routes", outDir, filename) await mkdirSync(dirname(dynamicRouteFilepath), { recursive: true }) - // Create a relative import path from the generated file to the source file - const relativeImportPath = relative(dirname(dynamicRouteFilepath), filepath) - // Normalize the path for cross-platform compatibility and ensure forward slashes - const normalizedImportPath = relativeImportPath.replace(/\\/g, "/") - // Only import the Component so that tree-shaking will get rid of the server-side code const code = ` -import Component from "${normalizedImportPath}" +import Component from "${filepath}" import { wrapComponentWithLoader} from "@workshop/nano-remix" import { render } from 'hono/jsx/dom' diff --git a/packages/nano-remix/src/buildRoute.ts b/packages/nano-remix/src/buildRoute.ts index 52a4b05..6d7f598 100644 --- a/packages/nano-remix/src/buildRoute.ts +++ b/packages/nano-remix/src/buildRoute.ts @@ -9,9 +9,12 @@ type BuildRouteOptions = { export const buildRoute = async ({ distDir, routeName, filepath, force = false }: BuildRouteOptions) => { if (!force && !(await shouldRebuild(routeName, filepath, distDir))) { + console.log(`🌭 Skipping build for ${routeName} - up to date`) return } + console.log(`🌭 Building route ${routeName} from ${filepath}`) + const scriptPath = join(import.meta.dirname, "../scripts/build.ts") const proc = Bun.spawn({ @@ -32,6 +35,10 @@ export const buildRoute = async ({ distDir, routeName, filepath, force = false } } const shouldRebuild = async (routeName: string, sourceFilePath: string, distDir: string) => { + if (process.env.NODE_ENV !== "production") { + return true + } + try { const outputPath = join(distDir, routeName + ".js") diff --git a/packages/nano-remix/src/nanoRemix.ts b/packages/nano-remix/src/nanoRemix.ts index 0d9b604..eedf0ff 100644 --- a/packages/nano-remix/src/nanoRemix.ts +++ b/packages/nano-remix/src/nanoRemix.ts @@ -33,12 +33,7 @@ export const nanoRemix = async (req: Request, options: Options = {}) => { // If the the route includes an extension it is a static file that we serve from the distDir if (!ext) { - await buildRoute({ - distDir, - routeName, - filepath: route.filePath, - force: options.disableCache, // Force rebuild if cache is disabled - }) + await buildRoute({ distDir, routeName, filepath: route.filePath, force: options.disableCache }) return await renderServer(req, route) } else { const file = Bun.file(join(distDir, routeName + ext)) diff --git a/packages/todo/package.json b/packages/todo/package.json index 39ac6be..e1ff7bf 100644 --- a/packages/todo/package.json +++ b/packages/todo/package.json @@ -6,7 +6,7 @@ "private": true, "scripts": { "dev": "bun run src/server.tsx", - "serve-subdomain": "NODE_ENV=production bun run src/server.tsx" + "serve-subdomain": "bun run src/server.tsx" }, "dependencies": { "@codemirror/autocomplete": "^6.18.6", diff --git a/packages/todo/src/editor.css b/packages/todo/src/todo.css similarity index 100% rename from packages/todo/src/editor.css rename to packages/todo/src/todo.css diff --git a/packages/todo/src/todoClickHandler.ts b/packages/todo/src/todoClickHandler.ts index 186c6cd..c3194d6 100644 --- a/packages/todo/src/todoClickHandler.ts +++ b/packages/todo/src/todoClickHandler.ts @@ -4,6 +4,7 @@ import { triggerTodoCompletionEffect } from "./todoCompletion" export const todoClickHandler = EditorView.domEventHandlers({ click(event, view) { + const element = event.target as HTMLElement const pos = view.posAtCoords({ x: event.clientX, y: event.clientY }) if (pos === null) return false @@ -12,40 +13,26 @@ export const todoClickHandler = EditorView.domEventHandlers({ if (!todo) return false - // Find the checkbox position in the line - const checkboxMatch = line.text.match(/^(\s*-\s*\[)\s*(\w?)\s*(\]\s+)/) - if (!checkboxMatch) return false + if (element.matches(".todo-url")) { + const url = element.getAttribute("data-url") + if (url) { + window.open(url, "_blank") + return true + } + } - const [fullMatch, beforeCheckbox, currentState, afterCheckbox] = checkboxMatch - if (!beforeCheckbox || !afterCheckbox) return false - - const checkboxStart = line.from + beforeCheckbox.length - const checkboxEnd = checkboxStart + 1 - - // Check if the click was within the checkbox area (including some padding) - const clickOffset = pos - line.from - - // Allow clicking anywhere in the "- [ ]" or "- [x]" part - if (clickOffset >= 0 && clickOffset <= fullMatch.length) { - // Toggle the checkbox state - const newState = todo.done ? " " : "x" - const isCompleting = !todo.done // Will be completing if currently not done - - view.dispatch({ - changes: { - from: checkboxStart, - to: checkboxEnd, - insert: newState, - }, - }) - - // Add animation effect when completing (not uncompleting) - if (isCompleting) { + if (element.matches(".todo-checkbox")) { + todo.done = !todo.done + if (todo.done) { triggerTodoCompletionEffect(view, line.from) } - - event.preventDefault() - return true + view.dispatch({ + changes: { + from: line.from, + to: line.to, + insert: todo.toString(), + }, + }) } return false diff --git a/packages/todo/src/todoCompletion.ts b/packages/todo/src/todoCompletion.ts index ec570bd..79a8bc0 100644 --- a/packages/todo/src/todoCompletion.ts +++ b/packages/todo/src/todoCompletion.ts @@ -2,8 +2,6 @@ import { completionAnimationEffect } from "@/todoDecorations" import { EditorView } from "@codemirror/view" export const triggerTodoCompletionEffect = async (view: EditorView, pos: number) => { - console.log(`🌭 HAHAHA`, pos) - // Dispatch the completion animation effect view.dispatch({ effects: completionAnimationEffect.of({ pos, duration: 1000 }), diff --git a/packages/todo/src/todoDecorations.ts b/packages/todo/src/todoDecorations.ts index dc611d7..27f5966 100644 --- a/packages/todo/src/todoDecorations.ts +++ b/packages/todo/src/todoDecorations.ts @@ -2,25 +2,6 @@ import { Todo } from "@/todo" import { RangeSetBuilder, StateEffect } from "@codemirror/state" import { EditorView, Decoration, ViewPlugin, ViewUpdate, WidgetType } from "@codemirror/view" import { type RefObject } from "hono/jsx" -import { DateTime } from "luxon" - -// URL Widget for clickable URLs -class URLWidget extends WidgetType { - constructor(readonly url: string, readonly text: string) { - super() - } - - toDOM() { - const span = document.createElement("span") - span.className = "todo-url" - span.textContent = this.text - span.addEventListener("click", (e) => { - e.preventDefault() - window.open(this.url, "_blank") - }) - return span - } -} // Effect to trigger a decoration refresh on filter change export const refreshFilterEffect = StateEffect.define() @@ -108,13 +89,12 @@ export const todoDecorations = (filterRef: RefObject) => { builder.add(line.from, line.from + checkboxEnd, Decoration.mark({ class: "todo-checkbox" })) } - for (const { type, start, end, widget } of decorationsFor(todo)) { - if (widget) { - // Replace the text with a clickable widget - builder.add(line.from + start, line.from + end, Decoration.replace({ widget })) - } else { - builder.add(line.from + start, line.from + end, Decoration.mark({ class: `todo-${type}` })) - } + for (const { type, start, end, attributes } of decorationsFor(todo)) { + builder.add( + line.from + start, + line.from + end, + Decoration.mark({ class: `todo-${type}`, attributes }) + ) } } else if (/^\s*#+\s/.test(text)) { builder.add(line.from, line.to, Decoration.mark({ class: "todo-header" })) @@ -132,21 +112,20 @@ export const todoDecorations = (filterRef: RefObject) => { ) } +type TodoAttributes = Record const decorationsFor = (todo: Todo) => { - const decorations: { type: string; start: number; end: number; widget?: WidgetType }[] = [] + const decorations: { type: string; start: number; end: number; attributes: TodoAttributes }[] = [] let start = 0 for (const node of todo.nodes) { const { type } = node const end = start + node.content.length - + const attributes: TodoAttributes = {} if (type === "url") { - // Create a widget for clickable URLs - const urlWidget = new URLWidget(node.url, node.content) - decorations.push({ type: "url", start, end, widget: urlWidget }) - } else { - decorations.push({ type, start, end }) + attributes["data-url"] = node.url } + + decorations.push({ type, start, end, attributes }) start = end } diff --git a/packages/todo/src/todoEditor.tsx b/packages/todo/src/todoEditor.tsx index 644a230..b73ecb7 100644 --- a/packages/todo/src/todoEditor.tsx +++ b/packages/todo/src/todoEditor.tsx @@ -11,33 +11,7 @@ import { dateAutocompletion } from "@/dateAutocompletion" import { todoTimer } from "@/todoTimer" import { DateTime } from "luxon" -import "./editor.css" - -// Constants -const COMPLETION_ANIMATION_DURATION = 1000 -const FILTER_PLACEHOLDER = "Filter by tag" - -const getDefaultDocument = () => { - const today = DateTime.local().toFormat("MM/dd/yyyy") - const future = DateTime.local().plus({ days: 3 }).toFormat("MM/dd/yyyy") - - return ` -# Today - -- [ ] Sample task with a due date @${today} -- [ ] You can use a #tag to filter todos - - [ ] A sub task! Create nested todos by indenting with -- [x] Complete a todo by pressing -- [ ] You can also set a time estimate for todos (press the play button to start) 10m - -# This week -- [ ] Another todo with a due date @${future} - -# Later -- [ ] I use later as a junk drawer for todos I don't want to forget - -`.trim() -} +import "./todo.css" type TodoEditorProps = { defaultValue?: string @@ -171,3 +145,27 @@ const KeyboardShortcuts = ({ onClose }: { onClose: () => void }) => { ) } + +const FILTER_PLACEHOLDER = "Filter by tag" + +const getDefaultDocument = () => { + const today = DateTime.local().toFormat("MM/dd/yyyy") + const future = DateTime.local().plus({ days: 3 }).toFormat("MM/dd/yyyy") + + return ` +# Today + +- [ ] Sample task with a due date @${today} +- [ ] You can use a #tag to filter todos + - [ ] A sub task! Create nested todos by indenting with +- [x] Complete a todo by pressing +- [ ] You can also set a time estimate for todos (press the play button to start) 10m + +# This week +- [ ] Another todo with a due date @${future} + +# Later +- [ ] I use later as a junk drawer for todos I don't want to forget + +`.trim() +} diff --git a/packages/werewolf-ui/package.json b/packages/werewolf-ui/package.json index ad65c13..f21edd5 100644 --- a/packages/werewolf-ui/package.json +++ b/packages/werewolf-ui/package.json @@ -7,7 +7,7 @@ "module": "src/index.tsx", "scripts": { "dev": "bun --hot src/server.tsx", - "serve-subdomain": "NODE_ENV=production bun src/server.tsx" + "serve-subdomain": "bun src/server.tsx" }, "prettier": { "semi": false,