refactor: zen modal implemention and fix macos traffic light overlay
Signed-off-by: Innei <tukon479@gmail.com>
This commit is contained in:
parent
eaa5699661
commit
51a4bd273e
|
|
@ -1,3 +1,4 @@
|
|||
import { callWindowExpose } from "@follow/shared/bridge"
|
||||
import { dispatchEventOnWindow } from "@follow/shared/event"
|
||||
import { name } from "@pkg"
|
||||
import type { BrowserWindow, MenuItem, MenuItemConstructorOptions } from "electron"
|
||||
|
|
@ -130,6 +131,18 @@ export const registerAppMenu = () => {
|
|||
{ role: "zoomIn", label: t("menu.zoomIn") },
|
||||
{ role: "zoomOut", label: t("menu.zoomOut") },
|
||||
{ type: "separator" },
|
||||
{
|
||||
type: "normal",
|
||||
label: t("menu.zenMode"),
|
||||
accelerator: "Ctrl+Shift+Z",
|
||||
click: () => {
|
||||
const mainWindow = getMainWindow()
|
||||
if (!mainWindow) return
|
||||
|
||||
const caller = callWindowExpose(mainWindow)
|
||||
caller.zenMode()
|
||||
},
|
||||
},
|
||||
{ role: "togglefullscreen", label: t("menu.toggleFullScreen") },
|
||||
],
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { createSettingAtom } from "@follow/atoms/helper/setting.js"
|
||||
import type { UISettings } from "@follow/shared/interface/settings"
|
||||
import { atom, useAtomValue } from "jotai"
|
||||
|
||||
import { internal_feedColumnShowAtom } from "../sidebar"
|
||||
import { jotaiStore } from "@follow/utils/jotai"
|
||||
import { atom, useAtomValue, useSetAtom } from "jotai"
|
||||
import { useEventCallback } from "usehooks-ts"
|
||||
|
||||
export const createDefaultSettings = (): UISettings => ({
|
||||
// Sidebar
|
||||
|
|
@ -42,6 +42,8 @@ export const createDefaultSettings = (): UISettings => ({
|
|||
wideMode: false,
|
||||
})
|
||||
|
||||
const zenModeAtom = atom(false)
|
||||
|
||||
export const {
|
||||
useSettingKey: useUISettingKey,
|
||||
useSettingSelector: useUISettingSelector,
|
||||
|
|
@ -61,9 +63,28 @@ export const uiServerSyncWhiteListKeys: (keyof UISettings)[] = [
|
|||
"customCSS",
|
||||
]
|
||||
|
||||
const isZenModeAtom = atom((get) => {
|
||||
const ui = get(__uiSettingAtom)
|
||||
return ui.wideMode && !get(internal_feedColumnShowAtom)
|
||||
})
|
||||
export const useIsZenMode = () => useAtomValue(zenModeAtom)
|
||||
export const getIsZenMode = () => jotaiStore.get(zenModeAtom)
|
||||
|
||||
export const useIsZenMode = () => useAtomValue(isZenModeAtom)
|
||||
export const useSetZenMode = () => {
|
||||
const setZenMode = useSetAtom(zenModeAtom)
|
||||
return useEventCallback((checked: boolean) => {
|
||||
setZenMode(checked)
|
||||
})
|
||||
}
|
||||
|
||||
export const useToggleZenMode = () => {
|
||||
const setZenMode = useSetZenMode()
|
||||
const isZenMode = useIsZenMode()
|
||||
return useEventCallback(() => {
|
||||
const newIsZenMode = !isZenMode
|
||||
document.documentElement.dataset.zenMode = newIsZenMode.toString()
|
||||
setZenMode(newIsZenMode)
|
||||
})
|
||||
}
|
||||
|
||||
export const useRealInWideMode = () => {
|
||||
const wideMode = useUISettingKey("wideMode")
|
||||
const isZenMode = useIsZenMode()
|
||||
return wideMode || isZenMode
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ import { atom } from "jotai"
|
|||
import { getRouteParams } from "~/hooks/biz/useRouteParams"
|
||||
import { createAtomHooks } from "~/lib/jotai"
|
||||
|
||||
import { getIsZenMode, useIsZenMode } from "./settings/ui"
|
||||
|
||||
const defaultFeedView = FeedViewType.Articles
|
||||
const viewAtom = atom<FeedViewType | -1>(-1)
|
||||
const [
|
||||
|
|
@ -48,14 +50,20 @@ viewAtom.onMount = () => {
|
|||
setSidebarActiveView(view)
|
||||
}
|
||||
}
|
||||
export const [
|
||||
internal_feedColumnShowAtom,
|
||||
,
|
||||
useFeedColumnShow,
|
||||
,
|
||||
getFeedColumnShow,
|
||||
setFeedColumnShow,
|
||||
] = createAtomHooks(atom(true))
|
||||
const [, , internal_useFeedColumnShow, , internal_getFeedColumnShow, setFeedColumnShow] =
|
||||
createAtomHooks(atom(true))
|
||||
|
||||
export const useFeedColumnShow = () => {
|
||||
const isZenMode = useIsZenMode()
|
||||
return internal_useFeedColumnShow() && !isZenMode
|
||||
}
|
||||
|
||||
export const getFeedColumnShow = () => {
|
||||
const isZenMode = getIsZenMode()
|
||||
return internal_getFeedColumnShow() && !isZenMode
|
||||
}
|
||||
|
||||
export { setFeedColumnShow }
|
||||
|
||||
export const [, , useFeedColumnTempShow, , getFeedColumnTempShow, setFeedColumnTempShow] =
|
||||
createAtomHooks(atom(false))
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import {
|
|||
} from "react"
|
||||
import { useEventCallback } from "usehooks-ts"
|
||||
|
||||
import { useUISettingKey } from "~/atoms/settings/ui"
|
||||
import { useRealInWideMode } from "~/atoms/settings/ui"
|
||||
import {
|
||||
useGetWrappedElementPosition,
|
||||
useWrappedElementPosition,
|
||||
|
|
@ -58,7 +58,7 @@ export const Toc: Component<TocProps> = ({ className, onItemClick }) => {
|
|||
|
||||
const renderContentElementPosition = useWrappedElementPosition()
|
||||
const renderContentElementSize = useWrappedElementSize()
|
||||
const entryContentInWideMode = useUISettingKey("wideMode")
|
||||
const entryContentInWideMode = useRealInWideMode()
|
||||
const shouldShowTitle = useViewport((v) => {
|
||||
if (!entryContentInWideMode) return false
|
||||
const { w } = v
|
||||
|
|
|
|||
|
|
@ -32,6 +32,10 @@ export const shortcuts: Shortcuts = {
|
|||
name: "keys.layout.toggleWideMode",
|
||||
key: "Meta+[",
|
||||
},
|
||||
zenMode: {
|
||||
name: "keys.layout.zenMode",
|
||||
key: "Control+Shift+Z",
|
||||
},
|
||||
},
|
||||
entries: {
|
||||
refetch: {
|
||||
|
|
|
|||
|
|
@ -20,8 +20,20 @@ initializeApp().finally(() => {
|
|||
|
||||
const $container = document.querySelector("#root") as HTMLElement
|
||||
|
||||
if (IN_ELECTRON && getOS() === "Windows") {
|
||||
document.body.style.cssText += `--fo-window-padding-top: ${ElECTRON_CUSTOM_TITLEBAR_HEIGHT}px;`
|
||||
if (IN_ELECTRON) {
|
||||
const os = getOS()
|
||||
|
||||
switch (os) {
|
||||
case "Windows": {
|
||||
document.body.style.cssText += `--fo-window-padding-top: ${ElECTRON_CUSTOM_TITLEBAR_HEIGHT}px;`
|
||||
break
|
||||
}
|
||||
case "macOS": {
|
||||
document.body.style.cssText += `--fo-macos-traffic-light-width: 100px; --fo-macos-traffic-light-height: 30px;`
|
||||
break
|
||||
}
|
||||
}
|
||||
document.documentElement.dataset.os = getOS()
|
||||
}
|
||||
|
||||
ReactDOM.createRoot($container).render(
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { MdiMeditation } from "@follow/components/icons/Meditation.jsx"
|
||||
import { ActionButton } from "@follow/components/ui/button/index.js"
|
||||
import { DividerVertical } from "@follow/components/ui/divider/index.js"
|
||||
import { RotatingRefreshIcon } from "@follow/components/ui/loading/index.jsx"
|
||||
|
|
@ -12,7 +13,13 @@ import * as React from "react"
|
|||
import { useTranslation } from "react-i18next"
|
||||
|
||||
import { setGeneralSetting, useGeneralSettingKey } from "~/atoms/settings/general"
|
||||
import { setUISetting, useUISettingKey } from "~/atoms/settings/ui"
|
||||
import {
|
||||
setUISetting,
|
||||
useIsZenMode,
|
||||
useRealInWideMode,
|
||||
useSetZenMode,
|
||||
useUISettingKey,
|
||||
} from "~/atoms/settings/ui"
|
||||
import { useWhoami } from "~/atoms/user"
|
||||
import { ImpressionView } from "~/components/common/ImpressionTracker"
|
||||
import { FEED_COLLECTION_LIST, ROUTE_ENTRY_PENDING, ROUTE_FEED_IN_LIST } from "~/constants"
|
||||
|
|
@ -234,9 +241,11 @@ const SwitchToMasonryButton = () => {
|
|||
}
|
||||
|
||||
const WideModeButton = () => {
|
||||
const isWideMode = useUISettingKey("wideMode")
|
||||
const isWideMode = useRealInWideMode()
|
||||
const isZenMode = useIsZenMode()
|
||||
const { t } = useTranslation()
|
||||
|
||||
const setIsZenMode = useSetZenMode()
|
||||
return (
|
||||
<ImpressionView
|
||||
event="Switch to Wide Mode"
|
||||
|
|
@ -247,23 +256,33 @@ const WideModeButton = () => {
|
|||
<ActionButton
|
||||
shortcut={shortcuts.layout.toggleWideMode.key}
|
||||
onClick={() => {
|
||||
setUISetting("wideMode", !isWideMode)
|
||||
// TODO: Remove this after useMeasure can get bounds in time
|
||||
window.dispatchEvent(new Event("resize"))
|
||||
if (isZenMode) {
|
||||
setIsZenMode(false)
|
||||
} else {
|
||||
setUISetting("wideMode", !isWideMode)
|
||||
// TODO: Remove this after useMeasure can get bounds in time
|
||||
window.dispatchEvent(new Event("resize"))
|
||||
}
|
||||
window.analytics?.capture("Switch to Wide Mode", {
|
||||
wideMode: !isWideMode ? 1 : 0,
|
||||
click: 1,
|
||||
})
|
||||
}}
|
||||
tooltip={
|
||||
!isWideMode
|
||||
? t("entry_list_header.switch_to_widemode")
|
||||
: t("entry_list_header.switch_to_normalmode")
|
||||
isZenMode
|
||||
? t("zen.exit")
|
||||
: !isWideMode
|
||||
? t("entry_list_header.switch_to_widemode")
|
||||
: t("entry_list_header.switch_to_normalmode")
|
||||
}
|
||||
>
|
||||
<i
|
||||
className={cn(isWideMode ? "i-mgc-align-justify-cute-re" : "i-mgc-align-left-cute-re")}
|
||||
/>
|
||||
{isZenMode ? (
|
||||
<MdiMeditation />
|
||||
) : (
|
||||
<i
|
||||
className={cn(isWideMode ? "i-mgc-align-justify-cute-re" : "i-mgc-align-left-cute-re")}
|
||||
/>
|
||||
)}
|
||||
</ActionButton>
|
||||
</ImpressionView>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { cn, isSafari } from "@follow/utils/utils"
|
|||
import { useDebounceCallback } from "usehooks-ts"
|
||||
|
||||
import { AudioPlayer, useAudioPlayerAtomSelector } from "~/atoms/player"
|
||||
import { useUISettingKeys } from "~/atoms/settings/ui"
|
||||
import { useRealInWideMode, useUISettingKey } from "~/atoms/settings/ui"
|
||||
import { RelativeTime } from "~/components/ui/datetime"
|
||||
import { Media } from "~/components/ui/media"
|
||||
import { FEED_COLLECTION_LIST } from "~/constants"
|
||||
|
|
@ -62,7 +62,8 @@ export function ListItem({
|
|||
{ leading: false },
|
||||
)
|
||||
|
||||
const [settingWideMode, thumbnailRatio] = useUISettingKeys(["wideMode", "thumbnailRatio"])
|
||||
const settingWideMode = useRealInWideMode()
|
||||
const thumbnailRatio = useUISettingKey("thumbnailRatio")
|
||||
const rid = `list-item-${entryId}`
|
||||
|
||||
// NOTE: prevent 0 height element, react virtuoso will not stop render any more
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ function EntryHeaderImpl({
|
|||
{!hideRecentReader && (
|
||||
<div
|
||||
className={cn(
|
||||
"absolute left-5 top-0 flex h-full items-center gap-2 text-[13px] leading-none text-zinc-500",
|
||||
"absolute left-5 top-0 flex h-full items-center gap-2 text-[13px] leading-none text-zinc-500 zen-mode-macos:left-12",
|
||||
"visible z-[11]",
|
||||
views[view].wideMode && "static",
|
||||
shouldShowMeta && "hidden",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { Logo } from "@follow/components/icons/logo.jsx"
|
||||
import { MdiMeditation } from "@follow/components/icons/Meditation.js"
|
||||
import { ActionButton } from "@follow/components/ui/button/index.js"
|
||||
import { Popover, PopoverContent, PopoverTrigger } from "@follow/components/ui/popover/index.jsx"
|
||||
import { stopPropagation } from "@follow/utils/dom"
|
||||
|
|
@ -13,6 +14,7 @@ import { toast } from "sonner"
|
|||
|
||||
import { setAppSearchOpen } from "~/atoms/app"
|
||||
import { useGeneralSettingKey } from "~/atoms/settings/general"
|
||||
import { useIsZenMode, useSetZenMode } from "~/atoms/settings/ui"
|
||||
import { setFeedColumnShow, useFeedColumnShow, useSidebarActiveView } from "~/atoms/sidebar"
|
||||
import { useNavigateEntry } from "~/hooks/biz/useNavigateEntry"
|
||||
import { useI18n } from "~/hooks/common"
|
||||
|
|
@ -81,7 +83,8 @@ const LayoutActionButton = () => {
|
|||
const [animation, setAnimation] = useState({
|
||||
width: !feedColumnShow ? "auto" : 0,
|
||||
})
|
||||
|
||||
const isZenMode = useIsZenMode()
|
||||
const setIsZenMode = useSetZenMode()
|
||||
useEffect(() => {
|
||||
setAnimation({
|
||||
width: !feedColumnShow ? "auto" : 0,
|
||||
|
|
@ -95,18 +98,28 @@ const LayoutActionButton = () => {
|
|||
return (
|
||||
<m.div initial={animation} animate={animation} className="overflow-hidden">
|
||||
<ActionButton
|
||||
tooltip={t("app.toggle_sidebar")}
|
||||
tooltip={isZenMode ? t("zen.exit") : t("app.toggle_sidebar")}
|
||||
icon={
|
||||
<i
|
||||
className={cn(
|
||||
!feedColumnShow
|
||||
? "i-mgc-layout-leftbar-open-cute-re "
|
||||
: "i-mgc-layout-leftbar-close-cute-re",
|
||||
"text-theme-vibrancyFg",
|
||||
)}
|
||||
/>
|
||||
isZenMode ? (
|
||||
<MdiMeditation />
|
||||
) : (
|
||||
<i
|
||||
className={cn(
|
||||
!feedColumnShow
|
||||
? "i-mgc-layout-leftbar-open-cute-re "
|
||||
: "i-mgc-layout-leftbar-close-cute-re",
|
||||
"text-theme-vibrancyFg",
|
||||
)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
onClick={() => setFeedColumnShow(!feedColumnShow)}
|
||||
onClick={() => {
|
||||
if (isZenMode) {
|
||||
setIsZenMode(false)
|
||||
} else {
|
||||
setFeedColumnShow(!feedColumnShow)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</m.div>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -18,11 +18,12 @@ import { bundledThemesInfo } from "shiki/themes"
|
|||
import {
|
||||
getUISettings,
|
||||
setUISetting,
|
||||
useIsZenMode,
|
||||
useToggleZenMode,
|
||||
useUISettingKey,
|
||||
useUISettingSelector,
|
||||
useUISettingValue,
|
||||
} from "~/atoms/settings/ui"
|
||||
import { setFeedColumnShow, useFeedColumnShow } from "~/atoms/sidebar"
|
||||
import { useCurrentModal, useModalStack } from "~/components/ui/modal/stacked/hooks"
|
||||
import { isElectronBuild } from "~/constants"
|
||||
import { useSetTheme } from "~/hooks/common"
|
||||
|
|
@ -246,22 +247,14 @@ export const AppThemeSegment = () => {
|
|||
|
||||
const ZenMode = () => {
|
||||
const { t } = useTranslation("settings")
|
||||
const feedColumnShow = useFeedColumnShow()
|
||||
const isWideMode = useUISettingKey("wideMode")
|
||||
const isZenMode = useIsZenMode()
|
||||
const toggleZenMode = useToggleZenMode()
|
||||
return (
|
||||
<SettingItemGroup>
|
||||
<SettingSwitch
|
||||
checked={!feedColumnShow && isWideMode}
|
||||
checked={isZenMode}
|
||||
className="mt-4"
|
||||
onCheckedChange={(checked) => {
|
||||
if (checked) {
|
||||
setFeedColumnShow(false)
|
||||
setUISetting("wideMode", true)
|
||||
} else {
|
||||
setFeedColumnShow(true)
|
||||
setUISetting("wideMode", false)
|
||||
}
|
||||
}}
|
||||
onCheckedChange={toggleZenMode}
|
||||
label={t("appearance.zen_mode.label")}
|
||||
/>
|
||||
<SettingDescription>{t("appearance.zen_mode.description")}</SettingDescription>
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import { useState } from "react"
|
|||
import { useHotkeys } from "react-hotkeys-hook"
|
||||
import { useParams } from "react-router-dom"
|
||||
|
||||
import { useUISettingKey } from "~/atoms/settings/ui"
|
||||
import { useRealInWideMode } from "~/atoms/settings/ui"
|
||||
import { useFeedColumnShow, useFeedColumnTempShow } from "~/atoms/sidebar"
|
||||
import { m } from "~/components/common/Motion"
|
||||
import { FixedModalCloseButton } from "~/components/ui/modal/components/close"
|
||||
|
|
@ -24,7 +24,7 @@ export const Component = () => {
|
|||
const view = useRouteView()
|
||||
const navigate = useNavigateEntry()
|
||||
|
||||
const settingWideMode = useUISettingKey("wideMode")
|
||||
const settingWideMode = useRealInWideMode()
|
||||
const realEntryId = entryId === ROUTE_ENTRY_PENDING ? "" : entryId
|
||||
const showEntryContent = !(views[view].wideMode || (settingWideMode && !realEntryId))
|
||||
const wideMode = !!(settingWideMode && realEntryId)
|
||||
|
|
@ -45,7 +45,6 @@ export const Component = () => {
|
|||
)
|
||||
|
||||
const { feedId } = useRouteParams()
|
||||
const enableEntryWideMode = useUISettingKey("wideMode")
|
||||
|
||||
if (!showEntryContent) {
|
||||
return null
|
||||
|
|
@ -56,7 +55,7 @@ export const Component = () => {
|
|||
<EntryGridContainer showEntryContent={showEntryContent} wideMode={wideMode}>
|
||||
{wideMode && (
|
||||
<FixedModalCloseButton
|
||||
className="no-drag-region absolute left-4 top-4 z-10"
|
||||
className="no-drag-region absolute left-4 top-4 z-10 macos:translate-y-margin-macos-traffic-light-y"
|
||||
onClick={() => navigate({ entryId: null })}
|
||||
/>
|
||||
)}
|
||||
|
|
@ -71,7 +70,7 @@ export const Component = () => {
|
|||
: "",
|
||||
}}
|
||||
/>
|
||||
) : !enableEntryWideMode ? (
|
||||
) : !settingWideMode ? (
|
||||
<m.div
|
||||
className="center size-full flex-col"
|
||||
initial={{ opacity: 0.01, y: 300 }}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,12 @@ import { useMemo, useRef } from "react"
|
|||
import { useResizable } from "react-resizable-layout"
|
||||
import { Outlet } from "react-router-dom"
|
||||
|
||||
import { getUISettings, setUISetting, useUISettingKey } from "~/atoms/settings/ui"
|
||||
import {
|
||||
getUISettings,
|
||||
setUISetting,
|
||||
useRealInWideMode,
|
||||
useUISettingKey,
|
||||
} from "~/atoms/settings/ui"
|
||||
import { useRouteParams } from "~/hooks/biz/useRouteParams"
|
||||
import { EntryColumn } from "~/modules/entry-column"
|
||||
import { AppLayoutGridContainerProvider } from "~/providers/app-grid-layout-container-provider"
|
||||
|
|
@ -15,7 +20,7 @@ export function Component() {
|
|||
|
||||
// Memo this initial value to avoid re-render
|
||||
|
||||
const settingWideMode = useUISettingKey("wideMode")
|
||||
const settingWideMode = useRealInWideMode()
|
||||
const entryColWidth = useMemo(() => getUISettings().entryColWidth, [])
|
||||
const { view } = useRouteParams()
|
||||
const inWideMode = (view ? views[view].wideMode : false) || settingWideMode
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import { useResizable } from "react-resizable-layout"
|
|||
import { Outlet } from "react-router-dom"
|
||||
|
||||
import { setMainContainerElement } from "~/atoms/dom"
|
||||
import { getUISettings, setUISetting, useUISettingKey } from "~/atoms/settings/ui"
|
||||
import { getIsZenMode, getUISettings, setUISetting, useUISettingKey } from "~/atoms/settings/ui"
|
||||
import {
|
||||
getFeedColumnTempShow,
|
||||
setFeedColumnShow,
|
||||
|
|
@ -214,8 +214,8 @@ const FeedResponsiveResizerContainer = ({
|
|||
|
||||
const uiSettings = getUISettings()
|
||||
const feedColumnTempShow = getFeedColumnTempShow()
|
||||
const isInEntryContentWideMode = uiSettings.wideMode
|
||||
if (mouseY < 100 && isInEntryContentWideMode) return
|
||||
const isInEntryContentWideMode = uiSettings.wideMode || getIsZenMode()
|
||||
if (mouseY < 200 && isInEntryContentWideMode) return
|
||||
const threshold = feedColumnTempShow ? uiSettings.feedColWidth : 100
|
||||
|
||||
if (mouseX < threshold) {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { useTranslation } from "react-i18next"
|
|||
import { toast } from "sonner"
|
||||
|
||||
import { getGeneralSettings } from "~/atoms/settings/general"
|
||||
import { getUISettings } from "~/atoms/settings/ui"
|
||||
import { getUISettings, useToggleZenMode } from "~/atoms/settings/ui"
|
||||
import { useModalStack } from "~/components/ui/modal/stacked/hooks"
|
||||
import { useDiscoverRSSHubRouteModal } from "~/hooks/biz/useDiscoverRSSHubRoute"
|
||||
import { useFollow } from "~/hooks/biz/useFollow"
|
||||
|
|
@ -64,5 +64,12 @@ export const ExtensionExposeProvider = () => {
|
|||
},
|
||||
})
|
||||
}, [follow, present, presentDiscoverRSSHubRoute, presentUserProfile, t])
|
||||
|
||||
const toggleZenMode = useToggleZenMode()
|
||||
useEffect(() => {
|
||||
registerGlobalContext({
|
||||
zenMode: toggleZenMode,
|
||||
})
|
||||
}, [toggleZenMode])
|
||||
return null
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import { getIconCollections, iconsPlugin } from "@egoist/tailwindcss-icons"
|
|||
import { cleanupSVG, importDirectorySync, isEmptyColor, parseColors, runSVGO } from "@iconify/tools"
|
||||
import { compareColors, stringToColor } from "@iconify/utils/lib/colors"
|
||||
import type { Config } from "tailwindcss"
|
||||
import plugin from "tailwindcss/plugin"
|
||||
|
||||
export const baseConfig = {
|
||||
darkMode: ["class", '[data-theme="dark"]'],
|
||||
|
|
@ -22,9 +21,6 @@ export const baseConfig = {
|
|||
},
|
||||
|
||||
extend: {
|
||||
spacing: {
|
||||
"safe-inset-top": "var(--fo-window-padding-top, 0)",
|
||||
},
|
||||
fontFamily: {
|
||||
theme: "var(--fo-font-family)",
|
||||
default: "SN pro, sans-serif, system-ui",
|
||||
|
|
@ -147,37 +143,6 @@ export const baseConfig = {
|
|||
require("tailwindcss-motion"),
|
||||
|
||||
require(resolve(__dirname, "./tailwind-extend.css")),
|
||||
|
||||
plugin(({ addVariant }) => {
|
||||
addVariant("f-motion-reduce", '[data-motion-reduce="true"] &')
|
||||
addVariant("group-motion-reduce", ':merge(.group)[data-motion-reduce="true"] &')
|
||||
addVariant("peer-motion-reduce", ':merge(.peer)[data-motion-reduce="true"] ~ &')
|
||||
}),
|
||||
plugin(({ addUtilities, matchUtilities, theme }) => {
|
||||
addUtilities({
|
||||
".safe-inset-top": {
|
||||
top: "var(--fo-window-padding-top, 0)",
|
||||
},
|
||||
})
|
||||
|
||||
const safeInsetTopVariants = {}
|
||||
for (let i = 1; i <= 16; i++) {
|
||||
safeInsetTopVariants[`.safe-inset-top-${i}`] = {
|
||||
top: `calc(var(--fo-window-padding-top, 0px) + ${theme(`spacing.${i}`)})`,
|
||||
}
|
||||
}
|
||||
addUtilities(safeInsetTopVariants)
|
||||
|
||||
// Add arbitrary value support
|
||||
matchUtilities(
|
||||
{
|
||||
"safe-inset-top": (value) => ({
|
||||
top: `calc(var(--fo-window-padding-top, 0px) + ${value})`,
|
||||
}),
|
||||
},
|
||||
{ values: theme("spacing") },
|
||||
)
|
||||
}),
|
||||
],
|
||||
} satisfies Config
|
||||
|
||||
|
|
|
|||
|
|
@ -373,5 +373,6 @@
|
|||
"words.unread": "Unread",
|
||||
"words.user": "User",
|
||||
"words.which.all": "all",
|
||||
"words.zero_items": "Zero items"
|
||||
"words.zero_items": "Zero items",
|
||||
"zen.exit": "Exit Zen Mode"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@
|
|||
"menu.undo": "Undo",
|
||||
"menu.view": "View",
|
||||
"menu.window": "Window",
|
||||
"menu.zenMode": "Zen Mode",
|
||||
"menu.zoom": "Zoom",
|
||||
"menu.zoomIn": "Zoom In",
|
||||
"menu.zoomOut": "Zoom Out"
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
"keys.layout.showShortcuts": "Show/Hide Shortcuts",
|
||||
"keys.layout.toggleSidebar": "Show/Hide Feed Sidebar",
|
||||
"keys.layout.toggleWideMode": "Toggle Wide Mode",
|
||||
"keys.layout.zenMode": "Zen Mode",
|
||||
"keys.misc.quickSearch": "Quick Search",
|
||||
"keys.type.audio": "audio",
|
||||
"keys.type.entries": "entries",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
import type { SVGProps } from "react"
|
||||
|
||||
export function MdiMeditation(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24" {...props}>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M12 4c1.11 0 2 .89 2 2s-.89 2-2 2s-2-.89-2-2s.9-2 2-2m9 12v-2c-2.24 0-4.16-.96-5.6-2.68l-1.34-1.6A1.98 1.98 0 0 0 12.53 9H11.5c-.61 0-1.17.26-1.55.72l-1.34 1.6C7.16 13.04 5.24 14 3 14v2c2.77 0 5.19-1.17 7-3.25V15l-3.88 1.55c-.67.27-1.12.95-1.12 1.66C5 19.2 5.8 20 6.79 20H9v-.5a2.5 2.5 0 0 1 2.5-2.5h3c.28 0 .5.22.5.5s-.22.5-.5.5h-3c-.83 0-1.5.67-1.5 1.5v.5h7.21c.99 0 1.79-.8 1.79-1.79c0-.71-.45-1.39-1.12-1.66L14 15v-2.25c1.81 2.08 4.23 3.25 7 3.25"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
|
@ -33,6 +33,9 @@ interface RenderGlobalContext {
|
|||
// URL
|
||||
getWebUrl: () => string
|
||||
getApiUrl: () => string
|
||||
|
||||
// View
|
||||
zenMode: () => void
|
||||
}
|
||||
|
||||
export const registerGlobalContext = (context: Partial<RenderGlobalContext>) => {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import plugin from "tailwindcss/plugin"
|
||||
import resolveConfig from "tailwindcss/resolveConfig"
|
||||
|
||||
import { baseConfig } from "./configs/tailwind.base.config"
|
||||
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
export default resolveConfig({
|
||||
...baseConfig,
|
||||
content: [
|
||||
|
|
@ -13,4 +13,71 @@ export default resolveConfig({
|
|||
"./apps/web/index.html",
|
||||
"./packages/**/*.{ts,tsx}",
|
||||
],
|
||||
theme: {
|
||||
...baseConfig.theme,
|
||||
extend: {
|
||||
...baseConfig.theme?.extend,
|
||||
spacing: {
|
||||
"safe-inset-top": "var(--fo-window-padding-top, 0)",
|
||||
"margin-macos-traffic-light-x": "var(--fo-macos-traffic-light-width, 0)",
|
||||
"margin-macos-traffic-light-y": "var(--fo-macos-traffic-light-height, 0)",
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
...baseConfig.plugins,
|
||||
plugin(({ addVariant }) => {
|
||||
addVariant("f-motion-reduce", '[data-motion-reduce="true"] &')
|
||||
addVariant("group-motion-reduce", ':merge(.group)[data-motion-reduce="true"] &')
|
||||
addVariant("peer-motion-reduce", ':merge(.peer)[data-motion-reduce="true"] ~ &')
|
||||
|
||||
addVariant("zen-mode-macos", ":where(html[data-zen-mode='true'][data-os='macOS']) &")
|
||||
addVariant("zen-mode-windows", ":where(html[data-zen-mode='true'][data-os='Windows']) &")
|
||||
|
||||
addVariant("zen-mode", ":where(html[data-zen-mode='true']) &")
|
||||
addVariant("macos", ":where(html[data-os='macOS']) &")
|
||||
addVariant("windows", ":where(html[data-os='Windows']) &")
|
||||
}),
|
||||
|
||||
plugin(({ addUtilities, matchUtilities, theme }) => {
|
||||
addUtilities({
|
||||
".safe-inset-top": {
|
||||
top: "var(--fo-window-padding-top, 0)",
|
||||
},
|
||||
})
|
||||
|
||||
const safeInsetTopVariants = {}
|
||||
for (let i = 1; i <= 16; i++) {
|
||||
safeInsetTopVariants[`.safe-inset-top-${i}`] = {
|
||||
top: `calc(var(--fo-window-padding-top, 0px) + ${theme(`spacing.${i}`)})`,
|
||||
}
|
||||
}
|
||||
addUtilities(safeInsetTopVariants)
|
||||
|
||||
// left macos traffic light
|
||||
const leftMacosTrafficLightVariants = {}
|
||||
addUtilities({
|
||||
".left-macos-traffic-light": {
|
||||
left: "var(--fo-macos-traffic-light-width, 0)",
|
||||
},
|
||||
})
|
||||
|
||||
for (let i = 1; i <= 16; i++) {
|
||||
leftMacosTrafficLightVariants[`.left-macos-traffic-light-${i}`] = {
|
||||
left: `calc(var(--fo-macos-traffic-light-width, 0px) + ${theme(`spacing.${i}`)})`,
|
||||
}
|
||||
}
|
||||
addUtilities(leftMacosTrafficLightVariants)
|
||||
|
||||
// Add arbitrary value support
|
||||
matchUtilities(
|
||||
{
|
||||
"safe-inset-top": (value) => ({
|
||||
top: `calc(var(--fo-window-padding-top, 0px) + ${value})`,
|
||||
}),
|
||||
},
|
||||
{ values: theme("spacing") },
|
||||
)
|
||||
}),
|
||||
],
|
||||
})
|
||||
|
|
|
|||
Loading…
Reference in New Issue