feat: add dark mode toggle and enhance interaction in CodemirrorEditor (#511)

This commit is contained in:
dribble-njr 2025-01-10 17:52:01 +08:00 committed by GitHub
parent 741322d69e
commit 1dc3693826
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 94 additions and 3 deletions

View File

@ -1,12 +1,13 @@
<script setup lang="ts"> <script setup lang="ts">
import { useStore } from '@/stores' import { useStore } from '@/stores'
import { Info } from 'lucide-vue-next' import { Info } from 'lucide-vue-next'
import { Primitive } from 'radix-vue'
const store = useStore() const store = useStore()
const { output } = storeToRefs(store) const { output } = storeToRefs(store)
const dialogVisible = ref(false) const dialogVisible = ref(false)
const extensionInstalled = ref(false)
const form = ref<any>({ const form = ref<any>({
title: ``, title: ``,
desc: ``, desc: ``,
@ -40,12 +41,12 @@ function prePost() {
declare global { declare global {
interface Window { interface Window {
syncPost: (data: { thumb: string, title: string, desc: string, content: string }) => void syncPost: (data: { thumb: string, title: string, desc: string, content: string }) => void
$syncer: any
} }
} }
function post() { function post() {
dialogVisible.value = false; dialogVisible.value = false;
// 使 window.$syncer
(window.syncPost)({ (window.syncPost)({
thumb: form.value.thumb || form.value.auto.thumb, thumb: form.value.thumb || form.value.auto.thumb,
title: form.value.title || form.value.auto.title, title: form.value.title || form.value.auto.title,
@ -59,6 +60,10 @@ function onUpdate(val: boolean) {
dialogVisible.value = false dialogVisible.value = false
} }
} }
onMounted(() => {
extensionInstalled.value = window.$syncer !== `undefined`
})
</script> </script>
<template> <template>
@ -80,6 +85,23 @@ function onUpdate(val: boolean) {
</AlertDescription> </AlertDescription>
</Alert> </Alert>
<Alert v-if="!extensionInstalled">
<Info class="h-4 w-4" />
<AlertTitle>未检测到插件</AlertTitle>
<AlertDescription>
请安装
<Primitive
as="a"
class="text-blue-500"
href="https://www.wechatsync.com/?utm_source=syncicon#install"
target="_blank"
>
文章同步助手
</Primitive>
插件
</AlertDescription>
</Alert>
<div class="w-full flex items-center gap-4"> <div class="w-full flex items-center gap-4">
<Label for="thumb" class="w-10 text-end"> <Label for="thumb" class="w-10 text-end">
封面 封面

View File

@ -8,7 +8,7 @@ import {
} from '@/config' } from '@/config'
import { useStore } from '@/stores' import { useStore } from '@/stores'
import { addPrefix, processClipboardContent } from '@/utils' import { addPrefix, processClipboardContent } from '@/utils'
import { ChevronDownIcon, PanelLeftClose, PanelLeftOpen, Settings } from 'lucide-vue-next' import { ChevronDownIcon, Moon, PanelLeftClose, PanelLeftOpen, Settings, Sun } from 'lucide-vue-next'
const emit = defineEmits([`addFormat`, `formatContent`, `startCopy`, `endCopy`]) const emit = defineEmits([`addFormat`, `formatContent`, `startCopy`, `endCopy`])
@ -155,6 +155,11 @@ function copy() {
</Tooltip> </Tooltip>
</TooltipProvider> </TooltipProvider>
<Button variant="outline" @click="toggleDark()">
<Moon v-show="isDark" class="size-4" />
<Sun v-show="!isDark" class="size-4" />
</Button>
<div class="space-x-1 bg-background text-background-foreground mx-2 flex items-center border rounded-md"> <div class="space-x-1 bg-background text-background-foreground mx-2 flex items-center border rounded-md">
<Button variant="ghost" class="shadow-none" @click="copy"> <Button variant="ghost" class="shadow-none" @click="copy">
复制 复制

View File

@ -0,0 +1,61 @@
<script setup lang="ts">
import { throttle } from 'es-toolkit'
import { ArrowUpFromLine } from 'lucide-vue-next'
type Target = HTMLElement | Window | null
const props = defineProps<{
left?: number
top?: number
right?: number
bottom?: number
visibilityHeight?: number
target?: string
onClick?: (e: MouseEvent) => void
}>()
const visibilityHeight = ref(props.visibilityHeight ?? 400)
const visible = ref(false)
const target = ref<Target>(null)
function scrollToTop(e: MouseEvent) {
console.log(`scrollToTop`)
target.value?.scrollTo({ top: 0, left: 0, behavior: `smooth` })
props.onClick?.(e)
}
const throttledScroll = throttle((el: Target) => {
if (el instanceof HTMLElement) {
visible.value = el.scrollTop > visibilityHeight.value
}
else {
visible.value = window.scrollY > visibilityHeight.value
}
}, 200, { edges: [`leading`, `trailing`] })
onMounted(() => {
if (props.target) {
target.value = document.getElementById(props.target)
}
else {
target.value = window
}
target.value!.addEventListener(`scroll`, () => {
throttledScroll(target.value)
})
})
onUnmounted(() => {
target.value!.removeEventListener(`scroll`, () => {
throttledScroll(target.value)
})
})
</script>
<template>
<Button v-if="visible" variant="outline" size="icon" class="fixed z-50 rounded-full" :style="{ left: `${left}px`, top: `${top}px`, right: `${right}px`, bottom: `${bottom}px` }" @click="scrollToTop">
<ArrowUpFromLine />
</Button>
</template>

View File

@ -0,0 +1 @@
export { default as BackTop } from './BackTop.vue'

View File

@ -430,6 +430,8 @@ onMounted(() => {
</div> </div>
</div> </div>
</div> </div>
<BackTop target="preview" :right="40" :bottom="40" />
</div> </div>
<CssEditor class="order-2 flex-1" /> <CssEditor class="order-2 flex-1" />
<RightSlider class="order-2" /> <RightSlider class="order-2" />