From 89e9a2ee451a85ad7c386e2b7b437ea8ac23e68e Mon Sep 17 00:00:00 2001 From: Michael Freno Date: Sat, 20 Dec 2025 23:41:50 -0500 Subject: [PATCH] protections --- src/components/blog/TextEditor.tsx | 50 ++- src/lib/s3upload.ts | 39 +- src/server/api/routers/auth.ts | 507 ++++++++++++++++--------- src/server/api/routers/git-activity.ts | 431 ++++++++++++--------- src/server/api/routers/misc.ts | 32 +- src/server/cache.ts | 68 ++++ src/server/fetch-utils.test.ts | 119 ++++++ src/server/fetch-utils.ts | 156 ++++++++ 8 files changed, 1014 insertions(+), 388 deletions(-) create mode 100644 src/server/fetch-utils.test.ts create mode 100644 src/server/fetch-utils.ts diff --git a/src/components/blog/TextEditor.tsx b/src/components/blog/TextEditor.tsx index fabd582..3a89917 100644 --- a/src/components/blog/TextEditor.tsx +++ b/src/components/blog/TextEditor.tsx @@ -1,4 +1,4 @@ -import { Show, untrack, createEffect, on } from "solid-js"; +import { Show, untrack, createEffect, on, createSignal } from "solid-js"; import { createTiptapEditor, useEditorHTML } from "solid-tiptap"; import StarterKit from "@tiptap/starter-kit"; import Link from "@tiptap/extension-link"; @@ -104,6 +104,13 @@ export interface TextEditorProps { export default function TextEditor(props: TextEditorProps) { let editorRef!: HTMLDivElement; + let bubbleMenuRef!: HTMLDivElement; + + const [showBubbleMenu, setShowBubbleMenu] = createSignal(false); + const [bubbleMenuPosition, setBubbleMenuPosition] = createSignal({ + top: 0, + left: 0 + }); const editor = createTiptapEditor(() => ({ element: editorRef, @@ -126,6 +133,26 @@ export default function TextEditor(props: TextEditorProps) { untrack(() => { props.updateContent(editor.getHTML()); }); + }, + onSelectionUpdate: ({ editor }) => { + const { from, to } = editor.state.selection; + const hasSelection = from !== to; + + if (hasSelection && !editor.state.selection.empty) { + setShowBubbleMenu(true); + + // Position the bubble menu + const { view } = editor; + const start = view.coordsAtPos(from); + const end = view.coordsAtPos(to); + + const left = Math.max((start.left + end.left) / 2, 0); + const top = Math.max(start.top - 10, 0); + + setBubbleMenuPosition({ top, left }); + } else { + setShowBubbleMenu(false); + } } })); @@ -191,13 +218,17 @@ export default function TextEditor(props: TextEditorProps) { {(instance) => ( <> {/* Bubble Menu - appears when text is selected */} -
-
+ +
-
+ - {/* Toolbar - always visible */}