feat: add support for dedicated AI panel width in all feed view

This commit is contained in:
DIYgod 2025-09-16 10:37:14 +08:00
parent 1762ffca82
commit 2902fce5f9
No known key found for this signature in database
2 changed files with 54 additions and 11 deletions

View File

@ -1,11 +1,11 @@
import { Spring } from "@follow/components/constants/spring.js"
import { PanelSplitter } from "@follow/components/ui/divider/index.js"
import { FeedViewType } from "@follow/constants"
import { defaultUISettings } from "@follow/shared/settings/defaults"
import { cn } from "@follow/utils"
import { AnimatePresence } from "motion/react"
import { memo, startTransition, useEffect, useMemo, useRef } from "react"
import { memo, startTransition, useCallback, useEffect, useMemo, useRef } from "react"
import { useResizable } from "react-resizable-layout"
import { useParams } from "react-router"
import { AIChatPanelStyle, useAIChatPanelStyle, useAIPanelVisibility } from "~/atoms/settings/ai"
import { getUISettings, setUISetting } from "~/atoms/settings/ui"
@ -53,33 +53,67 @@ import { AppLayoutGridContainerProvider } from "~/providers/app-grid-layout-cont
* // Provides enhanced timeline experience with AI assistance
*/
const AIEnhancedTimelineLayoutImpl = () => {
const { entryId } = useParams()
const aiPanelStyle = useAIChatPanelStyle()
const isAIPanelVisible = useAIPanelVisibility()
const aiSidebarVisible = aiPanelStyle === AIChatPanelStyle.Fixed && isAIPanelVisible
const { view, isAllFeeds, entryId } = useRouteParamsSelector((s) => ({
view: s.view,
isAllFeeds: s.isAllFeeds,
entryId: s.entryId,
}))
const realEntryId = entryId === ROUTE_ENTRY_PENDING ? "" : entryId
// AI chat resizable panel configuration
const aiColWidth = useMemo(() => getUISettings().aiColWidth, [])
const isAllView = view === FeedViewType.All && isAllFeeds && !realEntryId
const widthRange: [number, number] = isAllView ? [500, 1200] : [300, 1200]
const [minWidth, maxWidth] = widthRange
const clampWidth = useCallback(
(value: number) => Math.max(minWidth, Math.min(maxWidth, Math.round(value))),
[minWidth, maxWidth],
)
const getHalfScreenWidth = useCallback(
() => clampWidth(typeof window !== "undefined" ? window.innerWidth / 2 : 800),
[clampWidth],
)
const resolvePreferredWidth = useCallback(() => {
const ui = getUISettings()
const halfScreen = getHalfScreenWidth()
return isAllView ? (ui.aiColWidthAll ?? halfScreen) : ui.aiColWidth
}, [getHalfScreenWidth, isAllView])
// Calculate initial width depending on view
const initialAiWidth = useMemo(() => resolvePreferredWidth(), [resolvePreferredWidth])
const startDragPosition = useRef(0)
const { position, separatorProps, isDragging, separatorCursor, setPosition } = useResizable({
axis: "x",
min: 300,
max: 1200,
initial: aiColWidth,
min: minWidth,
max: maxWidth,
initial: initialAiWidth,
reverse: true,
onResizeStart({ position }) {
startDragPosition.current = position
},
onResizeEnd({ position }) {
if (position === startDragPosition.current) return
setUISetting("aiColWidth", position)
// Persist per-view width
setUISetting(isAllView ? "aiColWidthAll" : "aiColWidth", position)
// TODO: Remove this after useMeasure can get bounds in time
window.dispatchEvent(new Event("resize"))
},
})
// When view changes, switch to the saved width for that view
useEffect(() => {
const width = resolvePreferredWidth()
setPosition(width)
// Trigger layout update
window.dispatchEvent(new Event("resize"))
}, [resolvePreferredWidth])
return (
<div className="relative flex min-w-0 grow">
<div className={cn("h-full flex-1", aiPanelStyle === AIChatPanelStyle.Fixed && "border-r")}>
@ -129,8 +163,12 @@ const AIEnhancedTimelineLayoutImpl = () => {
cursor={separatorCursor}
isDragging={isDragging}
onDoubleClick={() => {
setUISetting("aiColWidth", defaultUISettings.aiColWidth)
setPosition(defaultUISettings.aiColWidth)
const resetWidth = isAllView
? getHalfScreenWidth()
: clampWidth(defaultUISettings.aiColWidth)
setUISetting(isAllView ? "aiColWidthAll" : "aiColWidth", resetWidth)
setPosition(resetWidth)
window.dispatchEvent(new Event("resize"))
}}
/>
<AIChatLayout

View File

@ -51,6 +51,11 @@ export interface UISettings {
accentColor: AccentColor
entryColWidth: number
aiColWidth: number
/**
* Dedicated AI panel width for `FeedViewType.All`.
* If not set, the runtime default falls back to half of the window width.
*/
aiColWidthAll?: number
feedColWidth: number
opaqueSidebar: boolean
sidebarShowUnreadCount: boolean