From 063faf4b8e4078aa3671d2a8e014d57249a3c5ff Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Sat, 8 Nov 2025 12:20:58 +0800 Subject: [PATCH] :rotating_light: Clean up eslint issues --- app/assets/css/main.css | 4 ++ app/components/Attachment/AttachmentList.vue | 5 -- app/components/OgImage/ImageCard.vue | 50 +++++-------------- app/components/OgImage/NuxtSeo.vue | 14 +++--- app/components/Post/PostEditor.vue | 52 -------------------- app/components/Post/RepliesList.vue | 2 +- app/composables/useMarkdownProcessor.ts | 32 ++++++++---- app/pages/auth/authorize.vue | 15 +++--- app/pages/files/[id].vue | 2 +- app/pages/index.vue | 2 - app/pages/orders/[id].vue | 8 +-- app/pages/posts/{[id].vue => [...slug].vue} | 37 +++++++++++--- app/pages/spells/[...word].vue | 17 +++++-- app/types/api/order.ts | 6 +-- app/types/api/publisher.ts | 42 ++++++++-------- app/types/markdown-it-texmath.d.ts | 12 +++-- app/types/marked-katex.d.ts | 16 ++++-- 17 files changed, 145 insertions(+), 171 deletions(-) rename app/pages/posts/{[id].vue => [...slug].vue} (89%) diff --git a/app/assets/css/main.css b/app/assets/css/main.css index 986f83c..431575a 100644 --- a/app/assets/css/main.css +++ b/app/assets/css/main.css @@ -43,3 +43,7 @@ body { opacity: 0; filter: blur(1rem); } + +.prose pre { + padding: 0; +} diff --git a/app/components/Attachment/AttachmentList.vue b/app/components/Attachment/AttachmentList.vue index 7a313d8..19a4da3 100644 --- a/app/components/Attachment/AttachmentList.vue +++ b/app/components/Attachment/AttachmentList.vue @@ -58,7 +58,6 @@ const props = defineProps<{ maxHeight?: number }>() -const apiBase = useSolarNetworkUrl() const isAllImages = computed( () => @@ -194,8 +193,4 @@ function calculateAspectRatio(): number { ? (mostFrequent[mid - 1]! + mostFrequent[mid]!) / 2 : mostFrequent[mid]! } - -function getAttachmentUrl(attachment: SnAttachment): string { - return `${apiBase}/drive/files/${attachment.id}` -} diff --git a/app/components/OgImage/ImageCard.vue b/app/components/OgImage/ImageCard.vue index 3c86446..64637db 100644 --- a/app/components/OgImage/ImageCard.vue +++ b/app/components/OgImage/ImageCard.vue @@ -4,49 +4,23 @@ import { useSiteConfig } from "#site-config/app/composables" import { computed, defineComponent, h, resolveComponent } from "vue" const props = defineProps({ - colorMode: { type: String, required: false }, + colorMode: { type: String, required: false, default: "light" }, title: { type: String, required: false, default: "title" }, - description: { type: String, required: false }, - icon: { type: [String, Boolean], required: false }, - siteName: { type: String, required: false }, - siteLogo: { type: String, required: false }, + description: { type: String, required: false, default: null }, + icon: { type: [String, Boolean], required: false, default: null }, + siteName: { type: String, required: false, default: null }, + siteLogo: { type: String, required: false, default: null }, theme: { type: String, required: false, default: "#3f51b5" }, - backgroundImage: { type: String, required: false }, - avatarUrl: { type: String, required: false } + backgroundImage: { type: String, required: false, default: null }, + avatarUrl: { type: String, required: false, default: null } }) -const HexRegex = /^#(?:[0-9a-f]{3}){1,2}$/i const runtimeConfig = useOgImageRuntimeConfig() const colorMode = computed(() => { return props.colorMode || runtimeConfig.colorPreference || "light" }) -const themeHex = computed(() => { - if (HexRegex.test(props.theme)) return props.theme - if (HexRegex.test(`#${props.theme}`)) return `#${props.theme}` - if (props.theme.startsWith("rgb")) { - const rgb = props.theme - .replace("rgb(", "") - .replace("rgba(", "") - .replace(")", "") - .split(",") - .map((v) => Number.parseInt(v.trim(), 10)) - const hex = rgb - .map((v) => { - const hex2 = v.toString(16) - return hex2.length === 1 ? `0${hex2}` : hex2 - }) - .join("") - return `#${hex}` - } - return "#FFFFFF" -}) -const themeRgb = computed(() => { - return themeHex.value - .replace("#", "") - .match(/.{1,2}/g) - ?.map((v) => Number.parseInt(v, 16)) - .join(", ") -}) + + const textShadow = computed(() => { return '2px 2px 8px rgba(0,0,0,0.8)' }) @@ -54,9 +28,7 @@ const siteConfig = useSiteConfig() const siteName = computed(() => { return props.siteName || siteConfig.name }) -const siteLogo = computed(() => { - return props.siteLogo || siteConfig.logo -}) + const IconComponent = runtimeConfig.hasNuxtIcon ? resolveComponent("Icon") : defineComponent({ @@ -67,7 +39,7 @@ const IconComponent = runtimeConfig.hasNuxtIcon if ( typeof props.icon === "string" && !runtimeConfig.hasNuxtIcon && - process.dev + import.meta.dev ) { console.warn( "Please install `@nuxt/icon` to use icons with the fallback OG Image component." diff --git a/app/components/OgImage/NuxtSeo.vue b/app/components/OgImage/NuxtSeo.vue index 604a1c2..c8eb388 100644 --- a/app/components/OgImage/NuxtSeo.vue +++ b/app/components/OgImage/NuxtSeo.vue @@ -3,14 +3,14 @@ import { useOgImageRuntimeConfig } from "#og-image/app/utils" import { useSiteConfig } from "#site-config/app/composables" import { computed, defineComponent, h, resolveComponent } from "vue" const props = defineProps({ - colorMode: { type: String, required: false }, + colorMode: { type: String, required: false, default: "light" }, title: { type: String, required: false, default: "title" }, - description: { type: String, required: false }, - icon: { type: [String, Boolean], required: false }, - siteName: { type: String, required: false }, - siteLogo: { type: String, required: false }, + description: { type: String, required: false, default: null }, + icon: { type: [String, Boolean], required: false, default: null }, + siteName: { type: String, required: false, default: null }, + siteLogo: { type: String, required: false, default: null }, theme: { type: String, required: false, default: "#3f51b5" }, - backgroundImage: { type: String, required: false } + backgroundImage: { type: String, required: false, default: null } }) const HexRegex = /^#(?:[0-9a-f]{3}){1,2}$/i const runtimeConfig = useOgImageRuntimeConfig() @@ -62,7 +62,7 @@ const IconComponent = runtimeConfig.hasNuxtIcon if ( typeof props.icon === "string" && !runtimeConfig.hasNuxtIcon && - process.dev + import.meta.dev ) { console.warn( "Please install `@nuxt/icon` to use icons with the fallback OG Image component." diff --git a/app/components/Post/PostEditor.vue b/app/components/Post/PostEditor.vue index c5b5573..20d402c 100644 --- a/app/components/Post/PostEditor.vue +++ b/app/components/Post/PostEditor.vue @@ -20,7 +20,6 @@ diff --git a/app/components/Post/RepliesList.vue b/app/components/Post/RepliesList.vue index 7117f45..e80b58b 100644 --- a/app/components/Post/RepliesList.vue +++ b/app/components/Post/RepliesList.vue @@ -62,7 +62,7 @@ const router = useRouter() const props = defineProps<{ params: RepliesListParams - hideQuickReply: boolean + hideQuickReply?: boolean }>() defineEmits<{ diff --git a/app/composables/useMarkdownProcessor.ts b/app/composables/useMarkdownProcessor.ts index 3fd2564..6e0ca3e 100644 --- a/app/composables/useMarkdownProcessor.ts +++ b/app/composables/useMarkdownProcessor.ts @@ -1,4 +1,8 @@ -import { createMarkdownExit, type PluginWithParams } from "markdown-exit" +import { + createMarkdownExit, + type PluginSimple, + type PluginWithParams +} from "markdown-exit" import hljs from "highlight.js" import hljsMarkdown from "markdown-it-highlightjs" // @ts-ignore @@ -7,7 +11,11 @@ import katex from "katex" import "highlight.js/styles/a11y-dark.min.css" -export function useMarkdownProcessor() { +export function useMarkdownProcessor( + { preserveEmptyLines }: { preserveEmptyLines?: boolean } = { + preserveEmptyLines: true + } +) { const serverUrl = useSolarNetworkUrl() const processor = createMarkdownExit({ @@ -25,12 +33,22 @@ export function useMarkdownProcessor() { .use(hljsMarkdown, { hljs }) .use(imgSolarNetworkPlugin, { serverUrl: serverUrl }) - // Keep the empty lines + if (preserveEmptyLines) { + processor.use(preserveEmptyLinesPlugin) + } + + return { + render: (content: string) => processor.render(content) + } +} + +const preserveEmptyLinesPlugin: PluginSimple = (md) => { const defaultParagraphRenderer = - processor.renderer.rules.paragraph_open || + md.renderer.rules.paragraph_open || ((tokens, idx, options, _env, self) => self.renderToken(tokens, idx, options)) - processor.renderer.rules.paragraph_open = function ( + + md.renderer.rules.paragraph_open = function ( tokens, idx, options, @@ -58,10 +76,6 @@ export function useMarkdownProcessor() { } return result + defaultParagraphRenderer(tokens, idx, options, env, self) } - - return { - render: (content: string) => processor.render(content) - } } const imgSolarNetworkPlugin: PluginWithParams = ( diff --git a/app/pages/auth/authorize.vue b/app/pages/auth/authorize.vue index 505d419..f1a5db0 100644 --- a/app/pages/auth/authorize.vue +++ b/app/pages/auth/authorize.vue @@ -74,18 +74,18 @@ Authorize Deny @@ -140,9 +140,10 @@ async function fetchClientInfo() { const queryString = window.location.search.slice(1) clientInfo.value = await api(`/id/auth/open/authorize?${queryString}`) checkIfNewApp() - } catch (err: any) { + } catch (err) { error.value = - err.message || "An error occurred while loading the authorization request" + (err instanceof Error ? err.message : String(err)) || + "An error occurred while loading the authorization request" } finally { isLoading.value = false } @@ -171,8 +172,10 @@ async function handleAuthorize(authorize = true) { if (data.redirectUri) { window.location.href = data.redirectUri } - } catch (err: any) { - error.value = err.message || "An error occurred during authorization" + } catch (err) { + error.value = + (err instanceof Error ? err.message : String(err)) || + "An error occurred during authorization" } finally { isAuthorizing.value = false } diff --git a/app/pages/files/[id].vue b/app/pages/files/[id].vue index 37f659f..f26488b 100644 --- a/app/pages/files/[id].vue +++ b/app/pages/files/[id].vue @@ -439,7 +439,7 @@ function handleTouchMove(event: TouchEvent) { zoomLevel.value = Math.max(0.1, Math.min(5, scale)) } -function handleTouchEnd(event: TouchEvent) { +function handleTouchEnd(_event: TouchEvent) { if (fileType.value !== "image") return isPinching.value = false diff --git a/app/pages/index.vue b/app/pages/index.vue index c84fc14..cb9b678 100644 --- a/app/pages/index.vue +++ b/app/pages/index.vue @@ -47,8 +47,6 @@ import type { SnVersion, SnActivity } from "~/types/api" import PostEditor from "~/components/Post/PostEditor.vue" import PostItem from "~/components/Post/PostItem.vue" -import IconLight from "~/assets/images/cloudy-lamb.png" - const router = useRouter() useHead({ diff --git a/app/pages/orders/[id].vue b/app/pages/orders/[id].vue index afab17c..2d2f34e 100644 --- a/app/pages/orders/[id].vue +++ b/app/pages/orders/[id].vue @@ -7,13 +7,13 @@ class="pa-2" > - + The order has been paid successfully. Now you can close this tab and back to the Solar Network! {{ error }}Amount {{ order.amount }} {{ order.currency }} -
+
mdi-calendar Until {{ new Date(order.expiredAt).toLocaleString() }} @@ -47,8 +47,8 @@ mdi-check Pay diff --git a/app/pages/posts/[id].vue b/app/pages/posts/[...slug].vue similarity index 89% rename from app/pages/posts/[id].vue rename to app/pages/posts/[...slug].vue index bbf8402..73a24d1 100644 --- a/app/pages/posts/[id].vue +++ b/app/pages/posts/[...slug].vue @@ -68,7 +68,10 @@ - + postData.value?.post || null) const htmlContent = computed(() => postData.value?.html || "") @@ -189,6 +203,13 @@ useHead({ return [{ name: "description", content: description }] } return [] + }), + link: computed(() => { + if (post.value && post.value.publisher?.name && post.value.slug) { + const slugUrl = `/posts/${post.value.publisher.name}/${post.value.slug}` + return [{ rel: "canonical", href: slugUrl }] + } + return [] }) }) @@ -205,7 +226,9 @@ const userBackground = computed(() => { ) return firstImageAttachment ? `${apiBase}/drive/files/${firstImageAttachment.id}` - : undefined + : post.value?.publisher.background + ? `${apiBase}/drive/files/${post.value?.publisher.background.id}` + : undefined }) defineOgImage({ @@ -260,18 +283,18 @@ onMounted(() => { function makeEmbedImageClickable() { const elements = document.getElementsByClassName("prose-img-solar-network") - let count = 0; + let count = 0 for (const element of elements) { if (element instanceof HTMLImageElement) { - count += 1; + count += 1 element.addEventListener("click", (evt) => { const targetImg = evt.target as HTMLImageElement window.open("/files/" + targetImg.src.split("/").findLast((_) => true)) }) - element.style['cursor'] = 'pointer'; + element.style["cursor"] = "pointer" } } - console.log(`[Article] Made ${count} image(s) clickable in the article.`); + console.log(`[Article] Made ${count} image(s) clickable in the article.`) } diff --git a/app/pages/spells/[...word].vue b/app/pages/spells/[...word].vue index 9c6b7a7..b3a8407 100644 --- a/app/pages/spells/[...word].vue +++ b/app/pages/spells/[...word].vue @@ -2,13 +2,13 @@
- + The magic spell has been applied successfully. Now you can close this tab and back to the Solar Network! {{ error }}
-
+
mdi-calendar Until {{ spell.expiredAt.toString() }} @@ -68,7 +68,16 @@ const spellWord: string = typeof route.params.word === "string" ? route.params.word : route.params.word?.join("/") || "" -const spell = ref(null) +interface SnSpell { + type: number + account: { + name: string + } + createdAt: string + affectedAt: string + expiredAt?: string +} +const spell = ref(null) const error = ref(null) const newPassword = ref() diff --git a/app/types/api/order.ts b/app/types/api/order.ts index 329e907..748e430 100644 --- a/app/types/api/order.ts +++ b/app/types/api/order.ts @@ -13,11 +13,11 @@ export interface SnWalletOrder { remarks?: string appIdentifier?: string productIdentifier?: string - meta?: Record + meta?: Record amount: number expiredAt: string payeeWalletId?: string - payeeWallet?: any + payeeWallet?: unknown transactionId?: string - transaction?: any + transaction?: unknown } diff --git a/app/types/api/publisher.ts b/app/types/api/publisher.ts index c0b5565..92c3abf 100644 --- a/app/types/api/publisher.ts +++ b/app/types/api/publisher.ts @@ -1,30 +1,28 @@ -import type { SnCloudFile } from './post' +import type { SnCloudFile } from "./post" // Verification interface export interface SnVerification { - type: number; - title: string; - description: string; - verifiedBy: string; + type: number + title: string + description: string + verifiedBy: string } // Publisher interface export interface SnPublisher { - id: string; - type: number; - name: string; - nick: string; - bio: string; - pictureId: string; - backgroundId: string; - picture: SnCloudFile | null; - background: SnCloudFile | null; - verification: SnVerification | null; - accountId: string; - realmId: string | null; - account: unknown | null; - resourceIdentifier: string; - createdAt: string; - updatedAt: string; - deletedAt: string | null; + id: string + type: number + name: string + nick: string + bio: string + picture: SnCloudFile | null + background: SnCloudFile | null + verification: SnVerification | null + accountId: string + realmId: string | null + account: unknown | null + resourceIdentifier: string + createdAt: string + updatedAt: string + deletedAt: string | null } diff --git a/app/types/markdown-it-texmath.d.ts b/app/types/markdown-it-texmath.d.ts index 9ecd2a4..fdaed89 100644 --- a/app/types/markdown-it-texmath.d.ts +++ b/app/types/markdown-it-texmath.d.ts @@ -1,10 +1,14 @@ -declare module 'markdown-it-texmath' { +import type { PluginSimple } from "markdown-it" +import type katex from "katex" +import type { KatexOptions } from "katex" + +declare module "markdown-it-texmath" { interface TexMathOptions { - engine?: any + engine?: typeof katex delimiters?: string - katexOptions?: Record + katexOptions?: KatexOptions } - function texmath(options?: TexMathOptions): any + function texmath(options?: TexMathOptions): PluginSimple export default texmath } diff --git a/app/types/marked-katex.d.ts b/app/types/marked-katex.d.ts index 355e75c..d8ae90a 100644 --- a/app/types/marked-katex.d.ts +++ b/app/types/marked-katex.d.ts @@ -1,4 +1,4 @@ -declare module 'marked-katex' { +declare module "marked-katex" { interface Options { throwOnError?: boolean errorColor?: string @@ -7,12 +7,18 @@ declare module 'marked-katex' { fleqn?: boolean macros?: Record colorIsTextColor?: boolean - strict?: boolean | 'ignore' | 'warn' | 'error' - trust?: boolean | ((context: { command: string; url: string; protocol: string }) => boolean) - output?: 'html' | 'mathml' | 'htmlAndMathml' + strict?: boolean | "ignore" | "warn" | "error" + trust?: + | boolean + | ((context: { + command: string + url: string + protocol: string + }) => boolean) + output?: "html" | "mathml" | "htmlAndMathml" } - function markedKatex(options?: Options): any + function markedKatex(options?: Options): object export default markedKatex }