From cc6eba7df1557e949ca72eac3cdb35b4889df41a Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Fri, 13 Jun 2025 21:22:49 -0400 Subject: [PATCH] Linter --- src/app.css | 2 +- src/assets/styles/imports.scss | 2 +- src/lib/components/SegmentedController.svelte | 4 +- .../components/admin/EditorWithUpload.svelte | 64 +++++++++------- .../link-context-menu/LinkContextMenu.ts | 20 ++--- .../edra/extensions/url-embed/UrlEmbed.ts | 74 ++++++++++++------- .../extensions/url-embed/UrlEmbedExtended.ts | 2 +- .../url-embed/UrlEmbedPlaceholder.ts | 2 +- .../components/EmbedContextMenu.svelte | 72 +++++++++--------- .../components/LinkContextMenu.svelte | 72 +++++++++--------- .../headless/components/LinkEditDialog.svelte | 57 ++++++-------- .../components/UrlConvertDropdown.svelte | 30 ++++---- .../components/UrlEmbedPlaceholder.svelte | 53 ++++++------- src/lib/utils/content.ts | 27 +++---- src/lib/utils/extractEmbeds.ts | 6 +- src/routes/api/og-metadata/+server.ts | 10 +-- 16 files changed, 260 insertions(+), 237 deletions(-) diff --git a/src/app.css b/src/app.css index 447401c..1f73230 100644 --- a/src/app.css +++ b/src/app.css @@ -1,3 +1,3 @@ /* Global styles for the entire application */ @import './assets/styles/reset.css'; -@import './assets/styles/globals.scss'; \ No newline at end of file +@import './assets/styles/globals.scss'; diff --git a/src/assets/styles/imports.scss b/src/assets/styles/imports.scss index 5b45e44..509590f 100644 --- a/src/assets/styles/imports.scss +++ b/src/assets/styles/imports.scss @@ -3,4 +3,4 @@ @import './variables.scss'; @import './fonts.scss'; -@import './themes.scss'; \ No newline at end of file +@import './themes.scss'; diff --git a/src/lib/components/SegmentedController.svelte b/src/lib/components/SegmentedController.svelte index c6a28ba..d93c478 100644 --- a/src/lib/components/SegmentedController.svelte +++ b/src/lib/components/SegmentedController.svelte @@ -119,9 +119,7 @@ onmouseenter={() => (hoveredIndex = index)} onmouseleave={() => (hoveredIndex = null)} > - + {item.text} {/each} diff --git a/src/lib/components/admin/EditorWithUpload.svelte b/src/lib/components/admin/EditorWithUpload.svelte index 7894c6c..c6a0da5 100644 --- a/src/lib/components/admin/EditorWithUpload.svelte +++ b/src/lib/components/admin/EditorWithUpload.svelte @@ -83,18 +83,18 @@ let mediaDropdownTriggerRef = $state() let dropdownPosition = $state({ top: 0, left: 0 }) let mediaDropdownPosition = $state({ top: 0, left: 0 }) - + // URL convert dropdown state let showUrlConvertDropdown = $state(false) let urlConvertDropdownPosition = $state({ x: 0, y: 0 }) let urlConvertPos = $state(null) - + // Link context menu state let showLinkContextMenu = $state(false) let linkContextMenuPosition = $state({ x: 0, y: 0 }) let linkContextUrl = $state(null) let linkContextPos = $state(null) - + // Link edit dialog state let showLinkEditDialog = $state(false) let linkEditDialogPosition = $state({ x: 0, y: 0 }) @@ -239,85 +239,89 @@ showLinkEditDialog = false } } - + // Handle URL convert dropdown const handleShowUrlConvertDropdown = (pos: number, url: string) => { if (!editor) return - + // Get the cursor coordinates const coords = editor.view.coordsAtPos(pos) urlConvertDropdownPosition = { x: coords.left, y: coords.bottom + 5 } urlConvertPos = pos showUrlConvertDropdown = true } - + // Handle link context menu - const handleShowLinkContextMenu = (pos: number, url: string, coords: { x: number, y: number }) => { + const handleShowLinkContextMenu = ( + pos: number, + url: string, + coords: { x: number; y: number } + ) => { if (!editor) return - + linkContextMenuPosition = { x: coords.x, y: coords.y + 5 } linkContextUrl = url linkContextPos = pos showLinkContextMenu = true } - + const handleConvertToEmbed = () => { if (!editor || urlConvertPos === null) return - + editor.commands.convertLinkToEmbed(urlConvertPos) showUrlConvertDropdown = false urlConvertPos = null } - + const handleConvertLinkToEmbed = () => { if (!editor || linkContextPos === null) return - + editor.commands.convertLinkToEmbed(linkContextPos) showLinkContextMenu = false linkContextPos = null linkContextUrl = null } - + const handleEditLink = () => { if (!editor || !linkContextUrl) return - + linkEditUrl = linkContextUrl linkEditPos = linkContextPos linkEditDialogPosition = { ...linkContextMenuPosition } showLinkEditDialog = true showLinkContextMenu = false } - + const handleSaveLink = (newUrl: string) => { if (!editor) return - + editor.chain().focus().extendMarkRange('link').setLink({ href: newUrl }).run() showLinkEditDialog = false linkEditPos = null linkEditUrl = '' } - + const handleCopyLink = () => { if (!linkContextUrl) return - + navigator.clipboard.writeText(linkContextUrl) showLinkContextMenu = false linkContextPos = null linkContextUrl = null } - + const handleRemoveLink = () => { if (!editor) return - + editor.chain().focus().extendMarkRange('link').unsetLink().run() showLinkContextMenu = false linkContextPos = null linkContextUrl = null } - + const handleOpenLink = () => { if (!linkContextUrl) return - + window.open(linkContextUrl, '_blank', 'noopener,noreferrer') showLinkContextMenu = false linkContextPos = null @@ -325,7 +329,13 @@ } $effect(() => { - if (showTextStyleDropdown || showMediaDropdown || showUrlConvertDropdown || showLinkContextMenu || showLinkEditDialog) { + if ( + showTextStyleDropdown || + showMediaDropdown || + showUrlConvertDropdown || + showLinkContextMenu || + showLinkEditDialog + ) { document.addEventListener('click', handleClickOutside) return () => { document.removeEventListener('click', handleClickOutside) @@ -484,16 +494,16 @@ // Dismiss URL convert dropdown if user types if (showUrlConvertDropdown && transaction.docChanged) { // Check if the change is actual typing (not just cursor movement) - const hasTextChange = transaction.steps.some(step => - step.toJSON().stepType === 'replace' || - step.toJSON().stepType === 'replaceAround' + const hasTextChange = transaction.steps.some( + (step) => + step.toJSON().stepType === 'replace' || step.toJSON().stepType === 'replaceAround' ) if (hasTextChange) { showUrlConvertDropdown = false urlConvertPos = null } } - + // Call the original onUpdate if provided if (onUpdate) { onUpdate({ editor: updatedEditor, transaction }) diff --git a/src/lib/components/edra/extensions/link-context-menu/LinkContextMenu.ts b/src/lib/components/edra/extensions/link-context-menu/LinkContextMenu.ts index c8b3626..5208b8d 100644 --- a/src/lib/components/edra/extensions/link-context-menu/LinkContextMenu.ts +++ b/src/lib/components/edra/extensions/link-context-menu/LinkContextMenu.ts @@ -2,7 +2,7 @@ import { Extension } from '@tiptap/core' import { Plugin, PluginKey } from '@tiptap/pm/state' export interface LinkContextMenuOptions { - onShowContextMenu?: (pos: number, url: string, coords: { x: number, y: number }) => void + onShowContextMenu?: (pos: number, url: string, coords: { x: number; y: number }) => void } export const LinkContextMenu = Extension.create({ @@ -16,7 +16,7 @@ export const LinkContextMenu = Extension.create({ addProseMirrorPlugins() { const options = this.options - + return [ new Plugin({ key: new PluginKey('linkContextMenu'), @@ -25,26 +25,26 @@ export const LinkContextMenu = Extension.create({ contextmenu: (view, event) => { const { state } = view const pos = view.posAtCoords({ left: event.clientX, top: event.clientY }) - + if (!pos) return false - + const $pos = state.doc.resolve(pos.pos) const marks = $pos.marks() - const linkMark = marks.find(mark => mark.type.name === 'link') - + const linkMark = marks.find((mark) => mark.type.name === 'link') + if (linkMark && linkMark.attrs.href) { event.preventDefault() - + if (options.onShowContextMenu) { options.onShowContextMenu(pos.pos, linkMark.attrs.href, { x: event.clientX, y: event.clientY }) } - + return true } - + return false } } @@ -52,4 +52,4 @@ export const LinkContextMenu = Extension.create({ }) ] } -}) \ No newline at end of file +}) diff --git a/src/lib/components/edra/extensions/url-embed/UrlEmbed.ts b/src/lib/components/edra/extensions/url-embed/UrlEmbed.ts index e52b7dd..511fbcf 100644 --- a/src/lib/components/edra/extensions/url-embed/UrlEmbed.ts +++ b/src/lib/components/edra/extensions/url-embed/UrlEmbed.ts @@ -78,7 +78,10 @@ export const UrlEmbed = Node.create({ }, renderHTML({ HTMLAttributes }) { - return ['div', mergeAttributes({ 'data-url-embed': '' }, this.options.HTMLAttributes, HTMLAttributes)] + return [ + 'div', + mergeAttributes({ 'data-url-embed': '' }, this.options.HTMLAttributes, HTMLAttributes) + ] }, addCommands() { @@ -102,35 +105,41 @@ export const UrlEmbed = Node.create({ (pos) => ({ state, commands, chain }) => { const { doc } = state - + // Find the link mark at the given position const $pos = doc.resolve(pos) const marks = $pos.marks() - const linkMark = marks.find(mark => mark.type.name === 'link') - + const linkMark = marks.find((mark) => mark.type.name === 'link') + if (!linkMark) return false - + const url = linkMark.attrs.href if (!url) return false - + // Find the complete range of text with this link mark let from = pos let to = pos - + // Walk backwards to find the start doc.nodesBetween(Math.max(0, pos - 300), pos, (node, nodePos) => { - if (node.isText && node.marks.some(m => m.type.name === 'link' && m.attrs.href === url)) { + if ( + node.isText && + node.marks.some((m) => m.type.name === 'link' && m.attrs.href === url) + ) { from = nodePos } }) - + // Walk forwards to find the end doc.nodesBetween(pos, Math.min(doc.content.size, pos + 300), (node, nodePos) => { - if (node.isText && node.marks.some(m => m.type.name === 'link' && m.attrs.href === url)) { + if ( + node.isText && + node.marks.some((m) => m.type.name === 'link' && m.attrs.href === url) + ) { to = nodePos + node.nodeSize } }) - + // Use Tiptap's chain commands to replace content return chain() .focus() @@ -179,40 +188,53 @@ export const UrlEmbed = Node.create({ // Check if it's a plain text paste if (text && !html) { // Simple URL regex check - const urlRegex = /^https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)$/ - + const urlRegex = + /^https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)$/ + if (urlRegex.test(text.trim())) { // It's a URL, let it paste as a link naturally (don't prevent default) // But track it so we can show dropdown after const pastedUrl = text.trim() - + // Get the position before paste const beforePos = view.state.selection.from - + setTimeout(() => { const { state } = view const { doc } = state - + // Find the link that was just inserted // Start from where we were before paste let linkStart = -1 let linkEnd = -1 - + // Search for the link in a reasonable range - for (let pos = beforePos; pos < Math.min(doc.content.size, beforePos + pastedUrl.length + 10); pos++) { + for ( + let pos = beforePos; + pos < Math.min(doc.content.size, beforePos + pastedUrl.length + 10); + pos++ + ) { try { const $pos = doc.resolve(pos) const marks = $pos.marks() - const linkMark = marks.find(m => m.type.name === 'link' && m.attrs.href === pastedUrl) - + const linkMark = marks.find( + (m) => m.type.name === 'link' && m.attrs.href === pastedUrl + ) + if (linkMark) { // Found the link, now find its boundaries linkStart = pos - + // Find the end of the link - for (let endPos = pos; endPos < Math.min(doc.content.size, pos + pastedUrl.length + 5); endPos++) { + for ( + let endPos = pos; + endPos < Math.min(doc.content.size, pos + pastedUrl.length + 5); + endPos++ + ) { const $endPos = doc.resolve(endPos) - const hasLink = $endPos.marks().some(m => m.type.name === 'link' && m.attrs.href === pastedUrl) + const hasLink = $endPos + .marks() + .some((m) => m.type.name === 'link' && m.attrs.href === pastedUrl) if (hasLink) { linkEnd = endPos + 1 } else { @@ -225,7 +247,7 @@ export const UrlEmbed = Node.create({ // Position might be invalid, continue } } - + if (linkStart !== -1) { // Store the pasted URL info with correct position const tr = state.tr.setMeta('urlEmbedPaste', { @@ -233,7 +255,7 @@ export const UrlEmbed = Node.create({ lastPastedPos: linkStart }) view.dispatch(tr) - + // Notify the editor to show dropdown if (options.onShowDropdown) { options.onShowDropdown(linkStart, pastedUrl) @@ -251,4 +273,4 @@ export const UrlEmbed = Node.create({ }) ] } -}) \ No newline at end of file +}) diff --git a/src/lib/components/edra/extensions/url-embed/UrlEmbedExtended.ts b/src/lib/components/edra/extensions/url-embed/UrlEmbedExtended.ts index 9edec3f..f41154f 100644 --- a/src/lib/components/edra/extensions/url-embed/UrlEmbedExtended.ts +++ b/src/lib/components/edra/extensions/url-embed/UrlEmbedExtended.ts @@ -49,4 +49,4 @@ export const UrlEmbedExtended = (component: any) => addNodeView() { return SvelteNodeViewRenderer(component) } - }) \ No newline at end of file + }) diff --git a/src/lib/components/edra/extensions/url-embed/UrlEmbedPlaceholder.ts b/src/lib/components/edra/extensions/url-embed/UrlEmbedPlaceholder.ts index 8ce71f0..2c81ff4 100644 --- a/src/lib/components/edra/extensions/url-embed/UrlEmbedPlaceholder.ts +++ b/src/lib/components/edra/extensions/url-embed/UrlEmbedPlaceholder.ts @@ -32,4 +32,4 @@ export const UrlEmbedPlaceholder = (component: any) => addNodeView() { return SvelteNodeViewRenderer(component) } - }) \ No newline at end of file + }) diff --git a/src/lib/components/edra/headless/components/EmbedContextMenu.svelte b/src/lib/components/edra/headless/components/EmbedContextMenu.svelte index d76bc5c..3d4ca3e 100644 --- a/src/lib/components/edra/headless/components/EmbedContextMenu.svelte +++ b/src/lib/components/edra/headless/components/EmbedContextMenu.svelte @@ -1,7 +1,7 @@