diff --git a/package.json b/package.json index 6218984..84f23c2 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "stealthreader", "private": true, - "version": "0.3.2", + "version": "0.4.0", "type": "module", "scripts": { "dev": "vite", diff --git a/src/views/Reader.vue b/src/views/Reader.vue index 9dda5d7..c055696 100644 --- a/src/views/Reader.vue +++ b/src/views/Reader.vue @@ -1,11 +1,17 @@ @@ -123,20 +190,56 @@ const nextPage = () => { if (currentPage.value < pages.value.length - 1) currentPage.value++ } -const onPageClick = (e: MouseEvent) => { - const el = e.currentTarget as HTMLElement - const rect = el.getBoundingClientRect() - if (e.clientX - rect.left < rect.width / 2) prevPage() - else nextPage() +// 右键快捷设置菜单(字体颜色 / 背景颜色) +const menuShow = ref(false) +const menuX = ref(0) +const menuY = ref(0) +const menuFontColor = ref(settingStore.reader.fontColor) +const menuBackgroundColor = ref(settingStore.reader.backgroundColor) + +const getColorDetails = (value: string | null) => { + const color = value || "#000000" + const match = + color.match(/^#([\da-f]{6})([\da-f]{2})$/i) || + color.match(/^#([\da-f]{3})([\da-f])$/i) + const alphaMatch = color.match(/^rgba\(\s*\d+\s*,\s*\d+\s*,\s*\d+\s*,\s*([\d.]+)\s*\)$/i) + const alpha = match + ? Math.round((parseInt(match[2].length === 1 ? match[2] + match[2] : match[2], 16) / 255) * 100) + : alphaMatch + ? Math.round(Number(alphaMatch[1]) * 100) + : 100 + + return { + color: match ? `#${match[1].toUpperCase()}` : color, + opacity: Math.min(100, Math.max(0, alpha)), + } } +const onContextMenu = (e: MouseEvent) => { + menuX.value = e.clientX + menuY.value = e.clientY + menuShow.value = true +} + +watch(menuFontColor, (v) => { + if (v) settingStore.reader.fontColor = v +}) +watch(menuBackgroundColor, (v) => { + if (v) settingStore.reader.backgroundColor = v +}) + const onWindowMouseDown = (e: MouseEvent) => { + if (e.button !== 0) return const target = e.target as HTMLElement | null if (target?.closest("button, a, input, textarea, select, [role='button']")) return void appWindow.startDragging() } const onKeydown = (e: KeyboardEvent) => { + if (e.key === "Escape") { + menuShow.value = false + return + } if (e.key === "ArrowLeft") { e.preventDefault() switchChapter(-1) @@ -266,7 +369,6 @@ onUnmounted(() => { .page-body { flex: 1; min-height: 0; - cursor: pointer; } .page-text { @@ -327,4 +429,62 @@ onUnmounted(() => { .nav-back { padding: 0 4px; } + +.ctx-menu { + display: flex; + flex-direction: column; + gap: 12px; + min-width: 176px; +} + +.ctx-item { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; +} + +.ctx-label { + font-size: 13px; + color: var(--color-text-primary); + white-space: nowrap; +} + +.color-picker-control { + display: inline-flex; + flex-direction: column; + align-items: flex-start; + gap: 6px; +} + +.color-swatch { + width: 22px; + height: 22px; + border: 2px solid #ffffff; + border-radius: 50%; + cursor: pointer; + background-position: 0 0, 0 4px, 4px -4px, -4px 0; + background-size: 8px 8px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); +} + +.color-swatch:focus-visible { + outline: 2px solid var(--color-accent); + outline-offset: 2px; +} + +.color-values { + display: flex; + flex-direction: column; + gap: 1px; + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; + font-size: 12px; + line-height: 16px; +} + +.color-values small { + color: var(--color-text-secondary); + font-family: inherit; + font-size: 11px; +}