From d1b790036c6ae10bc9628417b7a7a00e7ed13554 Mon Sep 17 00:00:00 2001 From: Innei Date: Thu, 25 Sep 2025 17:50:37 +0800 Subject: [PATCH 1/3] release(mobile): release v0.2.10 --- apps/mobile/changelog/0.2.10.md | 11 +++++++++++ apps/mobile/ios/Folo/Info.plist | 4 ++-- apps/mobile/package.json | 2 +- 3 files changed, 14 insertions(+), 3 deletions(-) create mode 100644 apps/mobile/changelog/0.2.10.md diff --git a/apps/mobile/changelog/0.2.10.md b/apps/mobile/changelog/0.2.10.md new file mode 100644 index 000000000..7eeeeb801 --- /dev/null +++ b/apps/mobile/changelog/0.2.10.md @@ -0,0 +1,11 @@ +# What's New in v0.2.10 + +## Shiny new things + +## Improvements + +## No longer broken + +## Thanks + +Special thanks to volunteer contributors @ for their valuable contributions diff --git a/apps/mobile/ios/Folo/Info.plist b/apps/mobile/ios/Folo/Info.plist index 0c6b650fd..fd31bb5ad 100644 --- a/apps/mobile/ios/Folo/Info.plist +++ b/apps/mobile/ios/Folo/Info.plist @@ -32,7 +32,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 0.2.9 + 0.2.10 CFBundleSignature ???? CFBundleURLTypes @@ -53,7 +53,7 @@ CFBundleVersion - 136 + 137 ITSAppUsesNonExemptEncryption LSApplicationCategoryType diff --git a/apps/mobile/package.json b/apps/mobile/package.json index 3e2eeefc3..7ff4a36ab 100644 --- a/apps/mobile/package.json +++ b/apps/mobile/package.json @@ -1,6 +1,6 @@ { "name": "@follow/mobile", - "version": "0.2.9", + "version": "0.2.10", "private": true, "main": "src/main.tsx", "scripts": { From 87ea8b7a20723410aabb453453f5e5f9f20331ca Mon Sep 17 00:00:00 2001 From: Innei Date: Thu, 25 Sep 2025 23:06:51 +0800 Subject: [PATCH 2/3] feat(tabbar): add TabBarBottomAccessoryModule and enhance TabBarRootView - Introduced `TabBarBottomAccessoryModule` to provide a new bottom accessory view for the tab bar. - Updated `TabBarRootView` to utilize a custom tab bar controller, improving the management of the tab bar's visibility and behavior. - Refactored `UIWindow` extension methods for better readability and structure. - Enhanced `expo-module.config.json` to include the new module and improved formatting for better clarity. These changes aim to enhance the tab bar functionality and improve the overall user interface experience in the mobile application. Signed-off-by: Innei --- apps/mobile/ios/Folo/Info.plist | 2 +- apps/mobile/native/expo-module.config.json | 3 +- .../native/ios/Extensions/UIWindow.swift | 189 +++++++++--------- .../TabBar/TabBarBottomAccessoryModule.swift | 73 +++++++ .../ios/Modules/TabBar/TabBarRootView.swift | 15 +- .../src/components/common/ThemedBlurView.tsx | 32 ++- .../layouts/tabbar/ReactNativeTab.ios.tsx | 2 +- .../src/components/layouts/utils/index.tsx | 2 +- .../lib/navigation/bottom-tab/native.ios.tsx | 1 + .../src/lib/navigation/bottom-tab/native.tsx | 1 + apps/mobile/src/modules/screen/action.tsx | 10 +- .../(stack)/feeds/[feedId]/FeedScreen.tsx | 13 +- 12 files changed, 224 insertions(+), 119 deletions(-) create mode 100644 apps/mobile/native/ios/Modules/TabBar/TabBarBottomAccessoryModule.swift diff --git a/apps/mobile/ios/Folo/Info.plist b/apps/mobile/ios/Folo/Info.plist index fd31bb5ad..085ae9b9f 100644 --- a/apps/mobile/ios/Folo/Info.plist +++ b/apps/mobile/ios/Folo/Info.plist @@ -53,7 +53,7 @@ CFBundleVersion - 137 + 138 ITSAppUsesNonExemptEncryption LSApplicationCategoryType diff --git a/apps/mobile/native/expo-module.config.json b/apps/mobile/native/expo-module.config.json index 05e23ab8d..b69c7f315 100644 --- a/apps/mobile/native/expo-module.config.json +++ b/apps/mobile/native/expo-module.config.json @@ -11,7 +11,8 @@ "TabBarPortalModule", "EnhancePagerViewModule", "EnhancePageViewModule", - "ItemPressableModule" + "ItemPressableModule", + "TabBarBottomAccessoryModule" ] }, "android": { diff --git a/apps/mobile/native/ios/Extensions/UIWindow.swift b/apps/mobile/native/ios/Extensions/UIWindow.swift index 754552da2..4a0519969 100644 --- a/apps/mobile/native/ios/Extensions/UIWindow.swift +++ b/apps/mobile/native/ios/Extensions/UIWindow.swift @@ -9,105 +9,102 @@ import UIKit extension UIWindow { - - static func findViewController(ofType type: T.Type) -> T? { + static func findViewController(ofType type: T.Type) -> T? { guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene, - let window = windowScene.windows.first else { - return nil - } - - - if let rootVC = window.rootViewController { - return findViewControllerInHierarchy(rootVC, ofType: type) - } - return nil - } - - - static private func findViewControllerInHierarchy(_ viewController: UIViewController, ofType type: T.Type) -> T? { - - if let targetVC = viewController as? T { - return targetVC - } - - - if let navController = viewController as? UINavigationController { - if let visibleVC = navController.visibleViewController { - return findViewControllerInHierarchy(visibleVC, ofType: type) - } - } - - - if let tabController = viewController as? UITabBarController { - if let selectedVC = tabController.selectedViewController { - return findViewControllerInHierarchy(selectedVC, ofType: type) - } - } - - - for childVC in viewController.children { - if let foundVC = findViewControllerInHierarchy(childVC, ofType: type) { - return foundVC - } - } - - - if let presentedVC = viewController.presentedViewController { - return findViewControllerInHierarchy(presentedVC, ofType: type) - } - - return nil - } - - public static func findRNSNavigationController() -> UINavigationController? { - - guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene, - let window = windowScene.windows.first else { - return nil - } - - let rootViewController = window.rootViewController - - if let navController = rootViewController as? UINavigationController, - NSStringFromClass(type(of: navController)).contains("RNSNavigationController") - { - return navController - } - - return findRNSNavigationControllerInChildren(of: rootViewController) + let window = windowScene.windows.first + else { + return nil } - private static func findRNSNavigationControllerInChildren(of viewController: UIViewController?) - -> UINavigationController? + if let rootVC = window.rootViewController { + return findViewControllerInHierarchy(rootVC, ofType: type) + } + return nil + } + + static private func findViewControllerInHierarchy( + _ viewController: UIViewController, ofType type: T.Type + ) -> T? { + + if let targetVC = viewController as? T { + return targetVC + } + + if let navController = viewController as? UINavigationController { + if let visibleVC = navController.visibleViewController { + return findViewControllerInHierarchy(visibleVC, ofType: type) + } + } + + if let tabController = viewController as? UITabBarController { + if let selectedVC = tabController.selectedViewController { + return findViewControllerInHierarchy(selectedVC, ofType: type) + } + } + + for childVC in viewController.children { + if let foundVC = findViewControllerInHierarchy(childVC, ofType: type) { + return foundVC + } + } + + if let presentedVC = viewController.presentedViewController { + return findViewControllerInHierarchy(presentedVC, ofType: type) + } + + return nil + } + + public static func findRNSNavigationController() -> UINavigationController? { + + guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene, + let window = windowScene.windows.first + else { + return nil + } + + let rootViewController = window.rootViewController + + if let navController = rootViewController as? UINavigationController, + NSStringFromClass(type(of: navController)).contains("RNSNavigationController") { - guard let viewController = viewController else { - return nil - } - - if let presentedVC = viewController.presentedViewController { - if let navController = presentedVC as? UINavigationController, - NSStringFromClass(type(of: navController)).contains("RNSNavigationController") - { - return navController - } - - if let result = findRNSNavigationControllerInChildren(of: presentedVC) { - return result - } - } - - for childVC in viewController.children { - if let navController = childVC as? UINavigationController, - NSStringFromClass(type(of: navController)).contains("RNSNavigationController") - { - return navController - } - - if let result = findRNSNavigationControllerInChildren(of: childVC) { - return result - } - } - - return nil + return navController } + + return findRNSNavigationControllerInChildren(of: rootViewController) + } + + private static func findRNSNavigationControllerInChildren(of viewController: UIViewController?) + -> UINavigationController? + { + guard let viewController = viewController else { + return nil + } + + if let presentedVC = viewController.presentedViewController { + if let navController = presentedVC as? UINavigationController, + NSStringFromClass(type(of: navController)).contains("RNSNavigationController") + { + return navController + } + + if let result = findRNSNavigationControllerInChildren(of: presentedVC) { + return result + } + } + + for childVC in viewController.children { + if let navController = childVC as? UINavigationController, + NSStringFromClass(type(of: navController)).contains("RNSNavigationController") + { + return navController + } + + if let result = findRNSNavigationControllerInChildren(of: childVC) { + return result + } + } + + return nil + } } diff --git a/apps/mobile/native/ios/Modules/TabBar/TabBarBottomAccessoryModule.swift b/apps/mobile/native/ios/Modules/TabBar/TabBarBottomAccessoryModule.swift new file mode 100644 index 000000000..1c6b249d8 --- /dev/null +++ b/apps/mobile/native/ios/Modules/TabBar/TabBarBottomAccessoryModule.swift @@ -0,0 +1,73 @@ +// +// TabBarBottomAccessoryModule.swift +// FollowNative +// +// Created by Innei on 2025-09-25 +// + +import ExpoModulesCore +import UIKit + +public class TabBarBottomAccessoryModule: Module { + public func definition() -> ModuleDefinition { + Name("TabBarBottomAccessory") + + View(TabBarBottomAccessoryView.self) { + + } + } +} + +class TabBarBottomAccessoryView: ExpoView { + private weak var attachedRoot: TabBarRootView? + + required init(appContext: AppContext? = nil) { + super.init(appContext: appContext) + } + + deinit { + detachFromRoot() + } + + override func didMoveToWindow() { + super.didMoveToWindow() + attachToNearestTabBarRoot() + } + + override func willMove(toWindow newWindow: UIWindow?) { + if newWindow == nil { + detachFromRoot() + } + super.willMove(toWindow: newWindow) + } + + #if RCT_NEW_ARCH_ENABLED + + override func mountChildComponentView(_ childComponentView: UIView, index: Int) { + attachToNearestTabBarRoot() + } + + override func unmountChildComponentView(_ childComponentView: UIView, index: Int) { + detachFromRoot() + + } + #endif + + func attachToNearestTabBarRoot() { + if #available(iOS 26, *) { + guard window != nil else { return } + + CustomTabbarController.tabBarController.bottomAccessory = .init(contentView: self) + } + + } + + func detachFromRoot() { + if #available(iOS 26, *) { + guard window != nil else { return } + CustomTabbarController.tabBarController.bottomAccessory = nil + + } + } + +} diff --git a/apps/mobile/native/ios/Modules/TabBar/TabBarRootView.swift b/apps/mobile/native/ios/Modules/TabBar/TabBarRootView.swift index ac626f1fd..d732f2d31 100644 --- a/apps/mobile/native/ios/Modules/TabBar/TabBarRootView.swift +++ b/apps/mobile/native/ios/Modules/TabBar/TabBarRootView.swift @@ -10,8 +10,9 @@ import Foundation import SnapKit import UIKit -class TabBarRootView: ExpoView { - private lazy var tabBarController = { +@MainActor +enum CustomTabbarController { + static var tabBarController = { let tabBarController = UITabBarController() if #available(iOS 16.0, *), UIDevice.current.userInterfaceIdiom == .pad { tabBarController.tabBar.isTranslucent = false @@ -27,19 +28,27 @@ class TabBarRootView: ExpoView { if #available(iOS 26.0, *) { tabBarController.isTabBarHidden = false tabBarController.tabBarMinimizeBehavior = .onScrollDown + } tabBarController.tabBar.tintColor = Utils.accentColor return tabBarController }() +} + +class TabBarRootView: ExpoView { + private var tabBarController = CustomTabbarController.tabBarController private let vc = UIViewController() private var tabViewControllers: [UIViewController] = [] + private var bottomAccessoryView: UIView? private let onTabIndexChange = EventDispatcher() private let onTabItemPress = EventDispatcher() + + required init(appContext: AppContext? = nil) { super.init(appContext: appContext) @@ -118,6 +127,7 @@ class TabBarRootView: ExpoView { let tabBarView = tabBarController.view! tabBarView.addSubview(tabBarPortalView) } + } override func willRemoveSubview(_ subview: UIView) { @@ -129,6 +139,7 @@ class TabBarRootView: ExpoView { tabBarController.viewControllers = tabViewControllers tabBarController.didMove(toParent: vc) } + } } diff --git a/apps/mobile/src/components/common/ThemedBlurView.tsx b/apps/mobile/src/components/common/ThemedBlurView.tsx index b857a689a..add3e39f4 100644 --- a/apps/mobile/src/components/common/ThemedBlurView.tsx +++ b/apps/mobile/src/components/common/ThemedBlurView.tsx @@ -16,9 +16,18 @@ import { isIos26 } from "@/src/lib/platform" export const ThemedBlurView = ({ ref, tint, + + tintColor, useGlass, ...rest -}: BlurViewProps & { ref?: React.Ref; useGlass?: boolean }) => { +}: BlurViewProps & { + ref?: React.Ref + useGlass?: boolean + /** + * The tint color of the glass view, only works when `useGlass` is true + */ + tintColor?: string +}) => { const { colorScheme } = useColorScheme() const background = useColor("systemBackground") @@ -26,15 +35,20 @@ export const ThemedBlurView = ({ const useBlurView = Platform.OS === "ios" || "experimentalBlurMethod" in rest if (isIos26 && useGlass) { - return + return } return useBlurView ? ( - + <> + + {tintColor && ( + + )} + ) : ( { - + diff --git a/apps/mobile/src/components/layouts/utils/index.tsx b/apps/mobile/src/components/layouts/utils/index.tsx index 5a09d432d..1fd29439b 100644 --- a/apps/mobile/src/components/layouts/utils/index.tsx +++ b/apps/mobile/src/components/layouts/utils/index.tsx @@ -40,5 +40,5 @@ export function getDefaultHeaderHeight({ headerHeight = 64 } - return headerHeight + statusBarHeight + return headerHeight + (modalPresentation ? 0 : statusBarHeight) } diff --git a/apps/mobile/src/lib/navigation/bottom-tab/native.ios.tsx b/apps/mobile/src/lib/navigation/bottom-tab/native.ios.tsx index 2b206086a..3e55add44 100644 --- a/apps/mobile/src/lib/navigation/bottom-tab/native.ios.tsx +++ b/apps/mobile/src/lib/navigation/bottom-tab/native.ios.tsx @@ -4,6 +4,7 @@ import type { ViewProps } from "react-native" import type { TabBarRootWrapperProps } from "./types" export const TabBarPortalWrapper = requireNativeView("TabBarPortal") +export const TabBarBottomAccessoryWrapper = requireNativeView("TabBarBottomAccessory") export type TabScreenNativeProps = ViewProps & { title?: string } diff --git a/apps/mobile/src/lib/navigation/bottom-tab/native.tsx b/apps/mobile/src/lib/navigation/bottom-tab/native.tsx index 9cd632869..86d72d065 100644 --- a/apps/mobile/src/lib/navigation/bottom-tab/native.tsx +++ b/apps/mobile/src/lib/navigation/bottom-tab/native.tsx @@ -6,6 +6,7 @@ import type { IconNativeValues } from "@/src/constants/native-images" import type { TabbarIconProps, TabBarRootWrapperProps } from "./types" export { View as TabBarPortalWrapper } from "react-native" +export { View as TabBarBottomAccessoryWrapper } from "react-native" export type TabScreenNativeProps = React.ComponentProps & { title?: string icon?: FC | IconNativeValues diff --git a/apps/mobile/src/modules/screen/action.tsx b/apps/mobile/src/modules/screen/action.tsx index 70be6d067..8057b7d1d 100644 --- a/apps/mobile/src/modules/screen/action.tsx +++ b/apps/mobile/src/modules/screen/action.tsx @@ -25,7 +25,7 @@ import { accentColor, useColor } from "@/src/theme/colors" import { MarkAllAsReadDialog } from "../dialogs/MarkAllAsReadDialog" export const ActionGroup = ({ children, className }: PropsWithChildren<{ className?: string }>) => { - return {children} + return {children} } export function HomeLeftAction() { @@ -59,7 +59,7 @@ interface HeaderActionButtonProps { variant?: "primary" | "secondary" } -export const MarkAllAsReadActionButton = ({ variant = "primary" }: HeaderActionButtonProps) => { +export const MarkAllAsReadActionButton = ({ variant = "secondary" }: HeaderActionButtonProps) => { const { t } = useTranslation() const { size, color } = useButtonVariant({ variant }) @@ -76,11 +76,11 @@ export const MarkAllAsReadActionButton = ({ variant = "primary" }: HeaderActionB const useButtonVariant = ({ variant = "primary" }: HeaderActionButtonProps) => { const label = useColor("label") - const size = 24 + const size = 20 const color = variant === "primary" ? accentColor : label return { size, color } } -export const UnreadOnlyActionButton = ({ variant = "primary" }: HeaderActionButtonProps) => { +export const UnreadOnlyActionButton = ({ variant = "secondary" }: HeaderActionButtonProps) => { const { t } = useTranslation() const unreadOnly = useGeneralSettingKey("unreadOnly") const { size, color } = useButtonVariant({ variant }) @@ -110,7 +110,7 @@ export const UnreadOnlyActionButton = ({ variant = "primary" }: HeaderActionButt export const FeedShareActionButton = ({ feedId, - variant = "primary", + variant = "secondary", }: { feedId?: string } & HeaderActionButtonProps) => { const { t } = useTranslation() const { size, color } = useButtonVariant({ variant }) diff --git a/apps/mobile/src/screens/(stack)/feeds/[feedId]/FeedScreen.tsx b/apps/mobile/src/screens/(stack)/feeds/[feedId]/FeedScreen.tsx index c2d90dcfd..867c95eea 100644 --- a/apps/mobile/src/screens/(stack)/feeds/[feedId]/FeedScreen.tsx +++ b/apps/mobile/src/screens/(stack)/feeds/[feedId]/FeedScreen.tsx @@ -1,13 +1,14 @@ import { FeedViewType } from "@follow/constants" import { useFeedById } from "@follow/store/feed/hooks" import { useIsSubscribed } from "@follow/store/subscription/hooks" -import { isBizId } from "@follow/utils" +import { isBizId, withOpacity } from "@follow/utils" import { useMemo } from "react" import { useTranslation } from "react-i18next" -import { Pressable } from "react-native" +import { Pressable, StyleSheet } from "react-native" import { RootSiblingParent } from "react-native-root-siblings" import { useSafeAreaInsets } from "react-native-safe-area-context" +import { ThemedBlurView } from "@/src/components/common/ThemedBlurView" import { BottomTabBarHeightContext } from "@/src/components/layouts/tabbar/contexts/BottomTabBarHeightContext" import { Text } from "@/src/components/ui/typography/Text" import { useNavigation } from "@/src/lib/navigation/hooks" @@ -16,6 +17,7 @@ import { EntryListSelector } from "@/src/modules/entry-list/EntryListSelector" import { EntryListContext, useEntries, useSelectedView } from "@/src/modules/screen/atoms" import { TimelineHeader } from "@/src/modules/screen/TimelineSelectorProvider" import { FollowScreen } from "@/src/screens/(modal)/FollowScreen" +import { accentColor } from "@/src/theme/colors" export const FeedScreen: NavigationControllerView<{ feedId: string @@ -34,7 +36,7 @@ export const FeedScreen: NavigationControllerView<{ {!isSubscribed && isBizId(feedIdentifier) && ( { navigation.presentControllerView(FollowScreen, { id: feedIdentifier, @@ -42,6 +44,11 @@ export const FeedScreen: NavigationControllerView<{ }) }} > + {t("words.follow")} )} From 8db25e615741fb9e6a20cf903df4fba5846da69d Mon Sep 17 00:00:00 2001 From: Innei Date: Fri, 24 Oct 2025 14:59:18 +0800 Subject: [PATCH 3/3] Merge remote-tracking branch 'origin/dev' into mobile-main Signed-off-by: Innei --- .claude/agents/content-processing-expert.md | 117 - .claude/agents/data-architect.md | 140 - .claude/agents/performance-specialist.md | 128 - .../agents/platform-integration-specialist.md | 83 - .claude/agents/react-architect.md | 68 - .claude/agents/tech-lead-orchestrator.md | 51 - .claude/agents/test-engineer.md | 147 - .claude/agents/ui-design-engineer.md | 51 - .github/actions/setup-xcode/action.yml | 2 +- .github/workflows/build-android.yml | 2 +- .github/workflows/build-desktop.yml | 2 +- .github/workflows/build-ios-development.yml | 4 +- .github/workflows/build-ios.yml | 2 +- .github/workflows/build-web.yml | 2 +- .github/workflows/claude-code-review.yml | 38 - .github/workflows/claude.yml | 63 - .github/workflows/lint.yml | 2 +- .github/workflows/tag.yml | 2 +- PRPs/adaptive-entry-content-layouts.md | 384 - PRPs/adaptive-entry-layouts-fixes.md | 530 -- PRPs/ai-summary-chat-integration.md | 735 -- ...droid-shared-webview-image-interception.md | 617 -- ...nhanced-ai-usage-observability-frontend.md | 938 --- PRPs/entry-layouts-comprehensive-fixes.md | 577 -- PRPs/entry-layouts-refinement-fixes.md | 660 -- PRPs/entry-modal-to-routing.md | 278 - PRPs/ratio-based-mixing.md | 397 - apps/desktop/AGENTS.md | 108 + apps/desktop/changelog/0.8.0.md | 13 + apps/desktop/changelog/next.md | 10 - apps/desktop/configs/vite.render.config.ts | 2 +- apps/desktop/layer/main/package.json | 18 +- .../layer/main/src/manager/bootstrap.ts | 8 +- .../src/updater/custom-github-provider.ts | 102 +- apps/desktop/layer/main/src/updater/logger.ts | 22 + apps/desktop/layer/renderer/index.html | 14 +- apps/desktop/layer/renderer/package.json | 60 +- apps/desktop/layer/renderer/src/App.tsx | 2 +- .../layer/renderer/src/atoms/settings/ai.ts | 116 +- .../src/components/common/ErrorElement.tsx | 2 +- .../src/components/common/NotFound.tsx | 2 +- .../src/components/common/SharePanel.tsx | 14 +- .../src/components/errors/EntryNotFound.tsx | 2 +- .../src/components/errors/FeedNotFound.tsx | 2 +- .../src/components/errors/ModalError.tsx | 2 +- .../src/components/errors/PageError.tsx | 2 +- .../ui/ai-summary-card/AISummaryCardBase.tsx | 16 +- .../ui/auto-completion/AutoCompletion.tsx | 4 +- .../ui/button/HeaderActionButton.tsx | 2 +- .../code-highlighter/shiki/shiki.module.css | 4 +- .../components/ui/crop/AvatarUploadModal.tsx | 22 +- .../ui/dropdown-menu/dropdown-menu.tsx | 20 +- .../src/components/ui/fab/FABContainer.tsx | 4 +- .../ui/hover-preview/EntryPreviewCard.tsx | 110 + .../ui/hover-preview/FeedPreviewCard.tsx | 68 + .../src/components/ui/hover-preview/index.ts | 2 + .../ui/keyboard-recorder/KeyRecorder.tsx | 6 +- .../src/components/ui/markdown/Markdown.tsx | 2 +- .../components/ui/markdown/components/Toc.tsx | 13 +- .../markdown/renderers/BlockErrorBoundary.tsx | 2 +- .../ui/markdown/renderers/Heading.tsx | 2 +- .../ui/markdown/renderers/MarkdownLink.tsx | 2 +- .../src/components/ui/media/Media.tsx | 14 +- .../ui/media/PreviewMediaContent.tsx | 8 +- .../src/components/ui/media/SwipeMedia.tsx | 8 +- .../src/components/ui/media/VideoPlayer.tsx | 4 +- .../components/ui/modal/inspire/PeekModal.tsx | 6 +- .../ui/modal/stacked/components.tsx | 2 +- .../ui/modal/stacked/custom-modal.tsx | 10 +- .../src/components/ui/modal/stacked/hooks.tsx | 4 +- .../src/components/ui/modal/stacked/modal.tsx | 13 +- .../components/ui/modal/stacked/overlay.tsx | 2 +- .../src/components/ui/modal/stacked/types.tsx | 1 + .../src/components/ui/paper/Paper.tsx | 4 +- .../ui/peek-modal/EntryModalPreview.tsx | 34 +- .../ui/peek-modal/EntryToastPreview.tsx | 2 +- .../layer/renderer/src/constants/app.tsx | 1 + .../layer/renderer/src/hooks/biz/useAsRead.ts | 14 +- .../src/hooks/biz/useEntryActions.tsx | 18 +- .../renderer/src/hooks/biz/useFeedActions.tsx | 18 - .../src/hooks/biz/useNavigateEntry.ts | 6 +- .../renderer/src/hooks/biz/usePeekModal.tsx | 8 + .../renderer/src/hooks/biz/useRouteParams.ts | 18 +- .../hooks/biz/useShowEntryDetailsColumn.ts | 19 + .../src/hooks/biz/useSubscriptionActions.tsx | 4 +- .../renderer/src/hooks/biz/useTimelineList.ts | 46 +- apps/desktop/layer/renderer/src/i18n.ts | 2 + .../layer/renderer/src/lib/translate.ts | 28 - .../achievement/AchievementModalContent.tsx | 25 +- .../src/modules/action/action-setting.tsx | 395 +- .../renderer/src/modules/action/rule-card.tsx | 188 +- .../src/modules/action/rule-summary.ts | 91 + .../src/modules/action/then-section.tsx | 415 +- .../src/modules/action/when-section.tsx | 125 +- .../src/modules/ai-chat-session/service.ts | 90 +- .../ai-chat/components/3d-models/AISpline.ts | 7 +- .../components/3d-models/AISplineLoader.tsx | 70 +- .../components/context-bar/MentionButton.tsx | 132 + .../context-bar/blocks/ContextBlock.tsx | 384 +- .../context-bar/blocks/TitleComponents.tsx | 12 +- .../ai-chat/components/context-bar/index.ts | 1 - .../context-bar/menus/ContextMenuContent.tsx | 157 - .../menus/ShortcutsMenuContent.tsx | 26 +- .../components/context-bar/menus/index.ts | 1 - .../context-bar/pickers/EntryPickers.tsx | 57 - .../context-bar/pickers/FeedPickers.tsx | 59 - .../context-bar/pickers/PickerList.tsx | 75 - .../context-bar/pickers/SearchInput.tsx | 14 - .../components/context-bar/pickers/index.ts | 4 - .../components/displays/AIChainOfThought.tsx | 16 +- .../displays/AIDisplayEntriesPart.tsx | 56 - .../displays/AIDisplayFeedsPart.tsx | 64 - .../components/displays/AIDisplayFlowPart.tsx | 4 +- .../displays/AIDisplaySubscriptionsPart.tsx | 90 - .../components/displays/AIReasoningPart.tsx | 2 +- .../ai-chat/components/displays/index.ts | 3 - .../displays/shared/AnalyticsMetrics.tsx | 2 +- .../displays/shared/CategoryTag.tsx | 2 +- .../displays/shared/DisplayHeader.tsx | 2 +- .../components/displays/shared/EmptyState.tsx | 2 +- .../displays/shared/GroupedContent.tsx | 2 +- .../components/displays/shared/StatCard.tsx | 4 +- .../components/file/GlobalFileDropZone.tsx | 18 +- .../components/layouts/AIChatContextBar.tsx | 299 +- .../ai-chat/components/layouts/AIChatRoot.tsx | 15 +- .../components/layouts/AIErrorFallback.tsx | 16 +- .../components/layouts/AIModelIndicator.tsx | 37 +- .../components/layouts/AISmartSidebar.css | 86 +- .../components/layouts/AISmartSidebar.tsx | 220 +- .../ai-chat/components/layouts/ChatHeader.tsx | 83 +- .../layouts/ChatHistoryDropdown.tsx | 151 + .../ai-chat/components/layouts/ChatInput.tsx | 239 +- .../components/layouts/ChatInterface.tsx | 483 +- .../components/layouts/ChatMoreDropdown.tsx | 181 +- .../components/layouts/ChatShortcutsRow.tsx | 92 + .../ai-chat/components/layouts/ChatTitle.tsx | 45 + .../components/layouts/CollapsibleError.tsx | 218 - .../components/layouts/EditableTitle.tsx | 139 - .../components/layouts/RateLimitNotice.tsx | 91 + .../components/layouts/TaskReportDropdown.tsx | 49 +- .../components/layouts/WelcomeScreen.tsx | 137 +- .../components/message/AIChatMessage.tsx | 66 +- .../components/message/AIDataBlockItem.tsx | 128 - .../components/message/AIDataBlockPart.tsx | 41 +- .../components/message/AIMarkdownMessage.tsx | 6 +- .../components/message/AIMessageIdContext.tsx | 5 + .../components/message/AIMessageParts.tsx | 42 +- .../components/message/EditableMessage.tsx | 29 +- .../components/message/ErrorMessage.tsx | 88 + .../components/message/ImageThumbnail.tsx | 14 +- .../components/message/TokenUsagePill.tsx | 22 +- .../message/ToolInvocationComponent.tsx | 24 +- .../components/message/UserChatMessage.tsx | 51 +- .../message/UserRichTextMessage.tsx | 67 +- .../components/message/ai-block-constants.ts | 32 +- .../message/animated/AnimatedMarkdown.tsx | 87 +- .../message/parse-incomplete-markdown.ts | 247 +- .../message/useContextBlockPresentation.tsx | 179 + .../components/shared/common-states.tsx | 4 +- .../components/ui/AIShortcutButton.tsx | 75 + .../ai-chat/components/ui/UploadProgress.tsx | 6 +- .../welcome/DefaultWelcomeContent.tsx | 57 +- .../welcome/EntryWelcomeContent.tsx | 20 + .../ai-chat/components/welcome/index.ts | 1 + .../src/modules/ai-chat/constants/index.ts | 4 + .../src/modules/ai-chat/editor/index.ts | 11 + .../file-upload/FileAttachmentNode.tsx | 106 +- .../file-upload/components/FileDropZone.tsx | 10 +- .../modules/ai-chat/editor/plugins/index.tsx | 1 + .../editor/plugins/mention/MentionNode.tsx | 63 +- .../editor/plugins/mention/MentionPlugin.tsx | 11 +- .../mention/components/MentionComponent.tsx | 172 +- .../mention/components/MentionDropdown.tsx | 273 +- .../components/shared/MentionTypeIcon.tsx | 17 +- .../mention/hooks/dateMentionConfig.ts | 45 +- .../mention/hooks/dateMentionParsers.ts | 22 +- .../plugins/mention/hooks/dateMentionUtils.ts | 119 +- .../mention/hooks/useMentionBlockSync.ts | 276 - .../mention/hooks/useMentionIntegration.ts | 29 - .../mention/hooks/useMentionKeyboard.ts | 151 +- .../plugins/mention/hooks/useMentionSearch.ts | 16 +- .../mention/hooks/useMentionSearchService.ts | 56 +- .../mention/hooks/useMentionSelection.ts | 60 +- .../mention/hooks/useMentionTrigger.ts | 53 +- .../ai-chat/editor/plugins/mention/types.ts | 41 +- .../plugins/mention/utils/mentionTextValue.ts | 67 + .../mention/utils/parseNaturalLanguageDate.ts | 57 + .../shared/components/MentionLikePill.tsx | 53 + .../shared/components/TypeaheadDropdown.tsx | 336 + .../editor/plugins/shared/components/index.ts | 1 + .../shared/hooks/useListKeyboardNavigation.ts | 118 + .../plugins/shared/hooks/useTextTrigger.ts | 54 + .../shared/hooks/useTypeaheadSelection.ts | 56 + .../{mention => shared}/utils/positioning.ts | 17 +- .../editor/plugins/shortcut/ShortcutNode.tsx | 146 + .../plugins/shortcut/ShortcutPlugin.tsx | 120 + .../shortcut/components/ShortcutComponent.tsx | 76 + .../shortcut/components/ShortcutDropdown.tsx | 141 + .../editor/plugins/shortcut/constants.ts | 10 + .../shortcut/hooks/useShortcutKeyboard.ts | 38 + .../shortcut/hooks/useShortcutSearch.ts | 89 + .../hooks/useShortcutSearchService.ts | 36 + .../shortcut/hooks/useShortcutSelection.ts | 26 + .../shortcut/hooks/useShortcutTrigger.ts | 18 + .../ai-chat/editor/plugins/shortcut/index.ts | 11 + .../ai-chat/editor/plugins/shortcut/types.ts | 32 + .../editor/plugins/shortcut/utils/index.ts | 4 + .../plugins/shortcut/utils/positioning.ts | 6 + .../shortcut/utils/shortcutTextValue.ts | 24 + .../plugins/shortcut/utils/textReplacement.ts | 47 + .../shortcut/utils/triggerDetection.ts | 44 + .../modules/ai-chat/hooks/useAIShortcut.ts | 21 +- .../hooks/useAutoTimelineSummaryShortcut.ts | 304 + .../modules/ai-chat/hooks/useChatHistory.ts | 7 +- .../modules/ai-chat/hooks/useDisplayBlocks.ts | 66 + .../hooks/useFeedEntrySearchService.ts | 7 +- .../modules/ai-chat/hooks/useLoadMessages.ts | 11 +- .../modules/ai-chat/hooks/useSaveMessages.ts | 33 - .../hooks/useTimelineSummaryAutoContext.ts | 14 + .../src/modules/ai-chat/services/index.ts | 295 +- .../ai-chat/store/chat-core/chat-actions.ts | 40 +- .../ai-chat/store/chat-core/chat-state.ts | 69 +- .../modules/ai-chat/store/chat-core/types.ts | 6 +- .../src/modules/ai-chat/store/hooks.ts | 14 + .../ai-chat/store/slices/block.slice.ts | 17 + .../ai-chat/store/slices/chat.slice.ts | 93 +- .../src/modules/ai-chat/store/store.ts | 6 +- .../src/modules/ai-chat/store/types.ts | 48 +- .../src/modules/ai-chat/utils/error.ts | 89 + .../src/modules/ai-chat/utils/export.ts | 78 - .../modules/ai-chat/utils/file-validation.ts | 9 +- .../modules/ai-chat/utils/lexical-markdown.ts | 50 - .../modules/ai-chat/utils/titleGeneration.ts | 98 +- .../ai-task/components/ai-item-actions.tsx | 2 +- .../components/ai-task-modal-header.tsx | 40 - .../ai-task/components/ai-task-modal.tsx | 97 +- .../components/notify-channels-config.tsx | 6 +- .../ai-task/components/schedule-config.tsx | 50 +- .../modules/ai-task/components/task-item.tsx | 107 +- .../modules/ai-task/components/task-list.tsx | 12 +- .../modules/app-layout/MainDestopLayout.tsx | 2 +- .../TimelineEntryTwoColumnLayout.tsx | 90 - .../AIEnhancedTimelineLayout.tsx | 208 +- .../app-layout/ai/AIChatFixedPanel.tsx | 2 +- .../app-layout/ai/AIChatFloatingPanel.tsx | 2 +- .../modules/app-layout/ai/AISplineButton.tsx | 2 +- .../entry-content/EntryLayoutContent.tsx | 101 - .../app-layout/entry-content/index.tsx | 1 - .../components/PodcastButton.tsx | 6 +- .../app-layout/subview/SubviewLayout.tsx | 32 +- .../src/modules/app/EnvironmentIndicator.tsx | 12 +- .../modules/app/NetworkStatusIndicator.tsx | 12 +- .../renderer/src/modules/app/Titlebar.tsx | 4 +- .../layer/renderer/src/modules/auth/Form.tsx | 6 +- .../src/modules/auth/LoginModalContent.tsx | 33 +- .../layer/renderer/src/modules/boost/atom.ts | 11 - .../src/modules/boost/boost-certification.tsx | 51 - .../src/modules/boost/boost-progress.tsx | 70 - .../modules/boost/boosting-contributors.tsx | 63 - .../renderer/src/modules/boost/hooks.tsx | 36 - .../src/modules/boost/level-benefits.tsx | 83 - .../renderer/src/modules/boost/modal.tsx | 110 - .../renderer/src/modules/boost/query.tsx | 67 - .../src/modules/boost/radio-cards.tsx | 61 - .../src/modules/claim/feed-claim-modal.tsx | 6 +- .../modules/command/command-button.test-d.ts | 5 +- .../src/modules/command/commands/entry.tsx | 32 +- .../src/modules/command/commands/id.ts | 1 - .../command/hooks/use-command-binding.ts | 21 +- .../command/hooks/use-command.test-d.ts | 16 +- .../command/shortcuts/SettingShortcuts.tsx | 6 +- .../src/modules/customize-toolbar/dnd.tsx | 6 +- .../src/modules/customize-toolbar/modal.tsx | 8 +- .../src/modules/discover/DiscoverFeedCard.tsx | 4 +- .../src/modules/discover/DiscoverFeedForm.tsx | 22 +- .../src/modules/discover/DiscoverForm.tsx | 10 +- .../src/modules/discover/DiscoverImport.tsx | 16 +- .../modules/discover/DiscoverInboxList.tsx | 4 +- .../modules/discover/DiscoverTransform.tsx | 2 +- .../src/modules/discover/FeedForm.tsx | 2 +- .../src/modules/discover/FeedSummary.tsx | 4 +- .../src/modules/discover/Inbox/InboxTable.tsx | 4 +- .../src/modules/discover/ListForm.tsx | 2 +- .../modules/discover/OpmlSelectionModal.tsx | 16 +- .../src/modules/discover/TrendingFeedCard.tsx | 2 +- .../src/modules/discover/recommendations.tsx | 2 +- .../renderer/src/modules/download/index.tsx | 12 +- .../src/modules/editor/css-editor.tsx | 8 +- .../entry-column/EntryItemSkeleton.tsx | 8 +- .../entry-column/EntrySubscriptionItem.tsx | 4 +- .../modules/entry-column/Items/all-item.tsx | 57 +- .../entry-column/Items/article-item.tsx | 13 +- .../modules/entry-column/Items/audio-item.tsx | 2 +- .../entry-column/Items/media-gallery.tsx | 2 +- .../entry-column/Items/notification-item.tsx | 8 +- .../Items/picture-item-stateless.tsx | 2 +- .../entry-column/Items/picture-item.ai.tsx | 172 - .../Items/picture-item.legacy.tsx | 179 - .../entry-column/Items/picture-item.tsx | 188 +- .../entry-column/Items/picture-masonry.tsx | 58 +- .../entry-column/Items/social-media-item.tsx | 65 +- .../entry-column/Items/video-item.ai.tsx | 209 - .../entry-column/Items/video-item.legacy.tsx | 315 - .../modules/entry-column/Items/video-item.tsx | 218 +- .../modules/entry-column/atoms/tutorial.ts | 19 - .../entry-column/components/DateItem.tsx | 39 +- .../components/EntryPlaneToolbar.tsx | 4 +- .../components/EntrySubscriptionSkeleton.tsx | 12 +- .../components/FooterMarkItem.tsx | 6 +- .../components/ScrollToExitTutorial.tsx | 142 - .../components/VirtualRowItem.tsx | 2 +- .../EntryColumnWrapper.tsx | 7 +- .../components/mark-all-button.tsx | 4 +- .../src/modules/entry-column/grid.tsx | 6 +- .../hooks/useAttachScrollBeyond.tsx | 21 + .../entry-column/hooks/useEntriesByView.ts | 4 +- .../hooks/useEntryMarkReadHandler.tsx | 4 +- .../modules/entry-column/hooks/useMarkAll.ts | 7 +- .../src/modules/entry-column/index.tsx | 41 +- .../src/modules/entry-column/item.tsx | 6 +- .../entry-column/layouts/EntryItemWrapper.tsx | 18 +- .../entry-column/layouts/EntryListHeader.tsx | 48 +- .../layouts/buttons/SwitchToMasonryButton.tsx | 86 +- .../layouts/buttons/WideModeButton.tsx | 3 - .../src/modules/entry-column/list.tsx | 2 +- .../entry-column/store/EntryColumnContext.ts | 16 + .../src/modules/entry-column/styles.ts | 2 +- .../templates/grid-item-template.tsx | 37 +- .../templates/list-item-template.ai.tsx | 347 - .../templates/list-item-template.legacy.tsx | 358 - .../templates/list-item-template.tsx | 376 +- .../EntryContent.legacy.tsx | 62 +- .../EntryContent.ai.tsx => EntryContent.tsx} | 37 +- .../entry-content/actions/more-actions.tsx | 26 +- .../{ => components}/AISummary.tsx | 0 .../{ => components}/ApplyEntryActions.tsx | 0 .../components/EntryPlaceholderLogo.tsx | 2 +- .../components/EntryTimelineSidebar.tsx | 7 +- .../entry-content/components/EntryTitle.tsx | 62 +- .../components/SourceContentView.tsx | 2 +- .../components/SupportCreator.tsx | 96 - .../EntryCommandShortcutRegister.tsx | 11 +- .../components/entry-content/EntryContent.tsx | 13 - .../entry-content/EntryNoContent.tsx | 2 +- .../entry-content/EntryRenderError.tsx | 2 +- .../EntryScrollingAndNavigationHandler.tsx | 8 +- .../entry-content/ReadabilityNotice.tsx | 2 +- .../accessories/ContainerToc.tsx | 6 +- .../components/entry-content/index.ts | 2 +- .../components/entry-header/AIEntryHeader.tsx | 26 +- .../internal/EntryHeaderActionsContainer.tsx | 9 +- .../internal/EntryHeaderBreadcrumb.tsx | 45 +- .../entry-header/internal/EntryHeaderMeta.tsx | 6 +- .../internal/EntryHeaderReadHistory.tsx | 6 +- .../entry-header/internal/context.tsx | 4 +- .../entry-read-history/EntryReadHistory.tsx | 13 +- .../entry-read-history/EntryUser.tsx | 2 +- .../components/layouts/ArticleLayout.tsx | 23 +- .../components/layouts/VideosLayout.tsx | 4 +- .../components/layouts/shared/AudioPlayer.tsx | 20 +- .../layouts/shared/MediaTranscript.tsx | 277 +- .../components/layouts/shared/VideoPlayer.tsx | 6 +- .../src/modules/entry-content/hooks.tsx | 6 +- .../src/modules/feed/feed-summary.tsx | 4 +- .../renderer/src/modules/feed/feed-title.tsx | 8 +- .../src/modules/feed/view-select-content.tsx | 4 +- .../integration/CustomIntegrationPreview.tsx | 42 +- .../CustomIntegrationValidator.tsx | 2 +- .../modules/integration/PlaceholderHelp.tsx | 16 +- .../modules/integration/URLSchemePreview.tsx | 46 +- .../modules/modal/ShortcutModalContent.tsx | 2 +- .../modules/new-user-guide/ai-chat-pane.tsx | 563 ++ .../new-user-guide/discover-import-step.tsx | 25 + .../new-user-guide/feeds-selection-list.tsx | 232 + .../new-user-guide/guide-modal-content.tsx | 385 +- .../src/modules/new-user-guide/pre-finish.tsx | 72 + .../modules/new-user-guide/steps/behavior.tsx | 66 - .../modules/new-user-guide/steps/rsshub.tsx | 47 - .../src/modules/new-user-guide/store.ts | 38 + .../layer/renderer/src/modules/panel/cmdf.tsx | 6 +- .../layer/renderer/src/modules/panel/cmdk.tsx | 8 +- .../layer/renderer/src/modules/panel/cmdn.tsx | 6 +- .../src/modules/player/corner-player.tsx | 16 +- .../power/my-wallet-section/create-wallet.tsx | 2 +- .../modules/power/my-wallet-section/index.tsx | 2 +- .../transaction-section/tx-table/TxTable.tsx | 2 +- .../tx-table/components.tsx | 2 +- .../src/modules/profile/email-management.tsx | 2 +- .../modules/profile/profile-setting-form.tsx | 10 +- .../modules/profile/update-password-form.tsx | 2 +- .../UserProfileModalContent.tsx | 44 +- .../profile/user-profile-modal/shared.tsx | 2 +- .../modules/renderer/components/TimeStamp.tsx | 2 +- .../src/modules/rsshub/set-modal-content.tsx | 14 +- .../renderer/src/modules/settings/control.tsx | 2 +- .../settings/helper/EnhancedIndicator.tsx | 2 +- .../settings/helper/setting-builder.tsx | 9 +- .../settings/modal/SettingModalContent.tsx | 99 +- .../src/modules/settings/modal/layout.tsx | 12 +- .../settings/modal/use-setting-modal-hack.ts | 3 +- .../modules/settings/modal/useSettingModal.ts | 22 +- .../renderer/src/modules/settings/section.tsx | 53 +- .../src/modules/settings/tabs/about.tsx | 46 +- .../renderer/src/modules/settings/tabs/ai.tsx | 29 +- .../tabs/ai/PersonalizePromptSection.tsx | 41 +- .../settings/tabs/ai/mcp/MCPPresetCard.tsx | 14 +- .../tabs/ai/mcp/MCPPresetSelectionModal.tsx | 28 +- .../settings/tabs/ai/mcp/MCPServiceItem.tsx | 16 +- .../tabs/ai/mcp/MCPServiceModalContent.tsx | 8 +- .../tabs/ai/mcp/MCPServicesSection.tsx | 55 +- .../tabs/ai/shortcuts/AIShortcutsSection.tsx | 72 +- .../tabs/ai/shortcuts/ShortcutItem.tsx | 32 +- .../ai/shortcuts/ShortcutModalContent.tsx | 211 +- .../settings/tabs/ai/shortcuts/hooks.tsx | 65 + .../tabs/ai/tasks/TaskSchedulingSection.tsx | 20 +- .../tabs/ai/usage/UsageAnalysisSection.tsx | 49 +- .../usage/components/DetailedUsageModal.tsx | 32 +- .../ai/usage/components/EfficiencyTab.tsx | 4 +- .../tabs/ai/usage/components/HistoryTab.tsx | 10 +- .../tabs/ai/usage/components/OverviewTab.tsx | 6 +- .../tabs/ai/usage/components/PatternsTab.tsx | 10 +- .../ai/usage/components/UsageProgressRing.tsx | 2 +- .../ai/usage/components/charts/BarList.tsx | 8 +- .../ai/usage/components/charts/TinyBars.tsx | 2 +- .../src/modules/settings/tabs/appearance.tsx | 282 +- .../src/modules/settings/tabs/feeds.tsx | 46 +- .../src/modules/settings/tabs/general.tsx | 48 +- .../integration/CustomIntegrationModal.tsx | 4 +- .../integration/CustomIntegrationSection.tsx | 26 +- .../settings/tabs/integration/index.tsx | 18 +- .../src/modules/settings/tabs/invitations.tsx | 4 +- .../src/modules/settings/tabs/lists/index.tsx | 10 +- .../modules/settings/tabs/lists/modals.tsx | 6 +- .../src/modules/settings/tabs/plan.tsx | 198 +- .../src/modules/settings/tabs/referral.tsx | 2 +- .../src/modules/settings/tabs/shortcut.tsx | 12 +- .../renderer/src/modules/settings/title.tsx | 6 +- .../modules/shared/ViewSelectorRadioGroup.tsx | 6 +- .../subscription-column/FeedCategory.tsx | 10 +- .../modules/subscription-column/FeedItem.tsx | 8 +- .../SimpleDiscoverModal.tsx | 10 +- .../SubscriptionColumnHeader.tsx | 2 +- .../SubscriptionTabButton.tsx | 62 +- .../TimelineTabsSettingsModal.tsx | 53 +- .../subscription-column/UnreadNumber.tsx | 2 +- .../src/modules/subscription-column/index.tsx | 73 +- .../subscription-list/EmptyFeedList.tsx | 4 +- .../subscription-list/ListHeader.tsx | 9 +- .../subscription-list/SortButton.tsx | 8 +- .../subscription-list/StarredItem.tsx | 2 +- .../subscription-list/SubscriptionList.tsx | 6 +- .../renderer/src/modules/trending/index.tsx | 10 +- .../modules/update-notice/UpdateNotice.tsx | 2 +- .../src/modules/upgrade/container.tsx | 2 +- .../src/modules/user/ProfileButton.tsx | 26 +- .../renderer/src/modules/user/UserAvatar.tsx | 4 +- .../src/modules/user/UserProBadge.tsx | 2 +- .../renderer/src/modules/wallet/balance.tsx | 2 +- .../renderer/src/modules/wallet/hooks.ts | 36 - .../renderer/src/modules/wallet/level.tsx | 2 +- .../renderer/src/modules/wallet/tip-modal.tsx | 202 - .../pages/(main)/(layer)/(ai)/ai/index.tsx | 2 +- .../(main)/(layer)/(subview)/action/index.tsx | 10 +- .../discover/category/[category].tsx | 24 +- .../(layer)/(subview)/discover/index.tsx | 6 +- .../(main)/(layer)/(subview)/power/index.tsx | 4 +- .../(main)/(layer)/(subview)/rsshub/index.tsx | 20 +- .../[timelineId]/[feedId]/[entryId]/index.tsx | 2 +- .../timeline/[timelineId]/[feedId]/layout.tsx | 6 +- .../renderer/src/pages/(main)/index.sync.tsx | 8 +- .../providers/extension-expose-provider.tsx | 3 +- .../src/providers/global-hotkeys-provider.tsx | 9 +- .../providers/main-view-hotkeys-provider.tsx | 18 + .../src/providers/popover-provider.tsx | 2 +- .../src/providers/server-configs-provider.tsx | 2 + .../layer/renderer/src/queries/wallet.tsx | 21 +- .../layer/renderer/src/store/feed/hooks.ts | 6 +- .../layer/renderer/src/styles/additional.css | 61 + apps/desktop/package.json | 48 +- .../plugins/vite/generate-main-hash.ts | 30 +- apps/desktop/tailwind.config.ts | 3 +- apps/desktop/vite.config.ts | 1 + apps/mobile/package.json | 38 +- .../src/components/common/CopyButton.tsx | 2 +- .../src/components/common/NoLoginInfo.tsx | 2 +- .../components/errors/GlobalErrorScreen.tsx | 8 +- .../src/components/errors/ListErrorView.tsx | 12 +- .../components/errors/ScreenErrorScreen.tsx | 8 +- .../layouts/header/FakeNativeHeaderTitle.tsx | 2 +- .../layouts/header/HeaderElements.tsx | 2 +- .../src/components/layouts/tabbar/Tabbar.tsx | 2 +- .../components/native/webview/DebugPanel.tsx | 4 +- .../src/components/native/webview/hooks.ts | 2 +- .../src/components/ui/avatar/UserAvatar.tsx | 2 +- .../src/components/ui/button/UIBarButton.tsx | 2 +- apps/mobile/src/components/ui/form/Label.tsx | 4 +- .../src/components/ui/form/PickerIos.tsx | 2 +- .../src/components/ui/form/Select.android.tsx | 10 +- apps/mobile/src/components/ui/form/Select.tsx | 2 +- apps/mobile/src/components/ui/form/Slider.tsx | 4 +- apps/mobile/src/components/ui/form/Switch.tsx | 2 +- .../src/components/ui/form/TextField.tsx | 8 +- .../src/components/ui/grouped/GroupedList.tsx | 20 +- .../src/components/ui/modal/BottomModal.tsx | 2 +- .../ui/modal/imperative-modal/modal.tsx | 2 +- .../ui/modal/imperative-modal/templates.tsx | 6 +- .../src/components/ui/tabview/TabBar.tsx | 2 +- .../src/components/ui/typography/Text.tsx | 2 +- apps/mobile/src/constants/views.tsx | 18 +- apps/mobile/src/lib/dialog.tsx | 8 +- apps/mobile/src/lib/loading.tsx | 4 +- apps/mobile/src/lib/markdown.tsx | 54 +- apps/mobile/src/modules/ai/summary.tsx | 24 +- .../mobile/src/modules/context-menu/entry.tsx | 4 +- .../mobile/src/modules/context-menu/feeds.tsx | 2 +- apps/mobile/src/modules/debug/index.tsx | 4 +- .../modules/dialogs/ConfirmPasswordDialog.tsx | 4 +- .../modules/dialogs/ConfirmTOTPCodeDialog.tsx | 4 +- apps/mobile/src/modules/discover/Category.tsx | 2 +- .../src/modules/discover/FeedSummary.tsx | 6 +- .../discover/RecommendationListItem.tsx | 8 +- .../src/modules/discover/Recommendations.tsx | 2 +- apps/mobile/src/modules/discover/Trending.tsx | 6 +- .../discover/search-tabs/SearchFeed.tsx | 2 +- .../discover/search-tabs/SearchFeedCard.tsx | 12 +- .../discover/search-tabs/SearchList.tsx | 12 +- .../modules/discover/search-tabs/__base.tsx | 2 +- .../modules/discover/search-tabs/hooks.tsx | 2 +- apps/mobile/src/modules/discover/search.tsx | 6 +- .../modules/entry-content/EntryAISummary.tsx | 2 +- .../EntryContentHeaderRightActions.tsx | 2 - .../modules/entry-content/EntryGridFooter.tsx | 10 +- .../entry-content/EntryNavigationHeader.tsx | 2 +- .../entry-content/EntryReadHistory.tsx | 4 +- .../src/modules/entry-content/EntryTitle.tsx | 6 +- .../entry-list/EntryListContentArticle.tsx | 16 +- .../entry-list/EntryListContentPicture.tsx | 16 +- .../entry-list/EntryListContentSocial.tsx | 18 +- .../entry-list/EntryListContentVideo.tsx | 28 +- .../src/modules/entry-list/EntryListEmpty.tsx | 2 +- .../modules/entry-list/EntryListFooter.tsx | 4 +- .../src/modules/entry-list/ItemSeparator.tsx | 4 +- .../entry-list/templates/EntryNormalItem.tsx | 20 +- .../entry-list/templates/EntryPictureItem.tsx | 2 +- .../entry-list/templates/EntrySocialItem.tsx | 12 +- .../entry-list/templates/EntryVideoItem.tsx | 4 +- apps/mobile/src/modules/feed/FollowFeed.tsx | 6 +- apps/mobile/src/modules/list/FollowList.tsx | 8 +- apps/mobile/src/modules/login/email.tsx | 24 +- apps/mobile/src/modules/login/index.tsx | 6 +- apps/mobile/src/modules/login/referral.tsx | 2 +- apps/mobile/src/modules/login/social.tsx | 6 +- .../src/modules/onboarding/step-finished.tsx | 4 +- .../src/modules/onboarding/step-interests.tsx | 4 +- .../modules/onboarding/step-preferences.tsx | 22 +- .../src/modules/onboarding/step-welcome.tsx | 4 +- .../src/modules/player/GlassPlayerTabBar.tsx | 2 +- .../src/modules/player/PlayerTabBar.tsx | 4 +- .../mobile/src/modules/rsshub/preview-url.tsx | 4 +- .../src/modules/screen/PagerList.ios.tsx | 2 +- apps/mobile/src/modules/screen/PagerList.tsx | 2 +- .../modules/screen/TimelineViewSelector.tsx | 4 +- apps/mobile/src/modules/screen/atoms.ts | 2 +- .../src/modules/settings/SettingsList.tsx | 2 +- .../src/modules/settings/UserHeaderBanner.tsx | 6 +- .../modules/settings/components/OTPWindow.tsx | 10 +- .../modules/settings/routes/2FASetting.tsx | 2 +- .../src/modules/settings/routes/About.tsx | 10 +- .../src/modules/settings/routes/Account.tsx | 2 +- .../src/modules/settings/routes/Actions.tsx | 6 +- .../modules/settings/routes/EditProfile.tsx | 12 +- .../src/modules/settings/routes/EditRule.tsx | 2 +- .../modules/settings/routes/Invitations.tsx | 10 +- .../src/modules/settings/routes/Lists.tsx | 16 +- .../modules/settings/routes/ManageList.tsx | 4 +- .../src/modules/settings/routes/Plan.tsx | 40 +- .../src/modules/settings/routes/Referral.tsx | 6 +- .../modules/settings/routes/ResetPassword.tsx | 2 +- .../modules/subscription/CategoryGrouped.tsx | 4 +- .../modules/subscription/ItemSeparator.tsx | 4 +- .../subscription/SubscriptionLists.tsx | 2 +- .../modules/subscription/items/InboxItem.tsx | 2 +- .../items/ListSubscriptionItem.tsx | 2 +- .../subscription/items/SubscriptionItem.tsx | 4 +- .../subscription/items/UnreadCount.tsx | 4 +- apps/mobile/src/providers/migration.tsx | 8 +- .../src/screens/(headless)/(debug)/text.tsx | 10 +- .../src/screens/(modal)/EditEmailScreen.tsx | 2 +- .../screens/(modal)/ForgetPasswordScreen.tsx | 8 +- .../src/screens/(modal)/InvitationScreen.tsx | 2 +- .../mobile/src/screens/(modal)/ListScreen.tsx | 2 +- .../src/screens/(modal)/LoginScreen.tsx | 4 +- .../src/screens/(modal)/ProfileScreen.tsx | 14 +- .../src/screens/(modal)/RsshubFormScreen.tsx | 18 +- .../screens/(modal)/TwoFactorAuthScreen.tsx | 2 +- .../onboarding/SelectReadingModeScreen.tsx | 4 +- .../src/screens/(stack)/(tabs)/settings.tsx | 8 +- .../entries/[entryId]/EntryDetailScreen.tsx | 10 +- apps/mobile/src/screens/OnboardingScreen.tsx | 4 +- apps/mobile/src/sitemap.tsx | 2 +- .../mobile/web-app/html-renderer/package.json | 4 +- .../mobile/web-app/html-renderer/src/HTML.tsx | 2 +- .../html-renderer/src/components/link.tsx | 2 +- .../components/common/PoweredByFooter.tsx | 10 +- apps/ssr/client/components/items/grid.tsx | 2 +- apps/ssr/client/components/items/normal.tsx | 6 +- apps/ssr/client/components/items/picture.tsx | 2 +- .../client/components/layout/header/index.tsx | 2 +- .../components/ui/feed-certification.tsx | 4 +- apps/ssr/client/components/ui/user-avatar.tsx | 6 +- apps/ssr/client/lib/auth.ts | 1 + apps/ssr/client/lib/helper.ts | 2 +- apps/ssr/client/modules/login/index.tsx | 35 +- .../client/pages/(login)/login/metadata.ts | 4 +- apps/ssr/client/pages/(login)/register.tsx | 2 +- .../pages/(main)/share/feeds/[id]/index.tsx | 6 +- .../pages/(main)/share/feeds/[id]/metadata.ts | 4 +- .../pages/(main)/share/lists/[id]/index.tsx | 18 +- .../pages/(main)/share/lists/[id]/metadata.ts | 4 +- .../pages/(main)/share/users/[id]/index.tsx | 22 +- .../pages/(main)/share/users/[id]/metadata.ts | 6 +- apps/ssr/client/pages/layout.tsx | 2 +- apps/ssr/client/styles/index.css | 1 + apps/ssr/index.ts | 3 +- apps/ssr/package.json | 34 +- apps/ssr/src/router/global.ts | 5 +- apps/ssr/src/router/og/feed.tsx | 3 +- apps/ssr/src/router/og/index.ts | 3 +- apps/ssr/src/router/og/list.tsx | 3 +- apps/ssr/src/router/og/user.tsx | 3 +- apps/ssr/tsconfig.json | 2 +- apps/ssr/tsdown.config.ts | 8 +- icons/mgc/deepseek_original.svg | 1 + icons/mgc/folo_bot_original.svg | 13 + icons/mgc/minus_circle_cute_fi.svg | 1 + icons/mgc/openai_original.svg | 1 + locales/ai/en.json | 72 +- locales/ai/ja.json | 71 +- locales/ai/zh-CN.json | 71 +- locales/app/en.json | 74 +- locales/app/ja.json | 73 +- locales/app/zh-CN.json | 74 +- locales/app/zh-TW.json | 73 +- locales/external/en.json | 1 + locales/external/zh-CN.json | 1 + locales/settings/en.json | 20 +- locales/settings/ja.json | 20 +- locales/settings/zh-CN.json | 20 +- locales/settings/zh-TW.json | 20 +- package.json | 32 +- packages/configs/package.json | 10 +- .../configs/tailwindcss/tailwind-extend.css | 22 +- packages/internal/atoms/package.json | 2 +- .../components/assets/colors-media.css | 3 +- .../internal/components/assets/tailwind.css | 8 + packages/internal/components/package.json | 12 +- .../src/ui/ai-shortcut-button/index.tsx | 83 - .../components/src/ui/checkbox/index.tsx | 16 +- .../components/src/ui/datetime/index.tsx | 11 +- .../components/src/ui/hover-card/index.tsx | 39 + .../src/ui/input/DateTimePicker.tsx | 41 +- .../components/src/ui/input/TextArea.tsx | 56 +- .../src/ui/input/TextAreaWrapper.tsx | 125 + .../internal/components/src/ui/input/index.ts | 1 + .../lexical-rich-editor/LexicalRichEditor.tsx | 113 +- .../LexicalRichEditorTextArea.tsx | 136 + .../src/ui/lexical-rich-editor/index.ts | 1 + .../src/ui/lexical-rich-editor/nodes.ts | 31 +- .../plugins/code-highlighting/index.tsx | 14 + .../plugins/exit-code/index.tsx | 121 + .../ui/lexical-rich-editor/plugins/index.ts | 4 + .../plugins/string-length-change/index.tsx | 41 + .../plugins/triple-backtick-toggle/index.tsx | 97 + .../src/ui/lexical-rich-editor/types.ts | 11 +- .../src/ui/lexical-rich-editor/utils.ts | 47 + .../components/src/ui/masonry/contexts.tsx | 4 + .../components/src/ui/masonry/index.tsx | 4 +- .../components/src/ui/switch/index.tsx | 41 +- .../components/src/ui/toast/styles.ts | 25 +- packages/internal/constants/src/tabs.tsx | 62 +- packages/internal/database/package.json | 6 +- .../internal/database/src/schemas/index.ts | 52 +- packages/internal/hooks/package.json | 2 +- packages/internal/hooks/src/index.ts | 1 + .../internal/hooks/src/useElementWidth.ts | 39 + packages/internal/shared/package.json | 14 +- packages/internal/shared/src/auth.ts | 7 +- .../internal/shared/src/settings/constants.ts | 10 +- .../internal/shared/src/settings/defaults.ts | 8 +- .../internal/shared/src/settings/interface.ts | 10 +- packages/internal/store/package.json | 6 +- packages/internal/store/src/lib/stream.ts | 43 + .../store/src/modules/entry/getter.ts | 13 +- .../internal/store/src/modules/entry/hooks.ts | 2 +- .../internal/store/src/modules/entry/store.ts | 52 +- .../internal/store/src/modules/feed/store.ts | 4 +- .../store/src/modules/subscription/getter.ts | 20 +- .../store/src/modules/subscription/hooks.ts | 13 +- .../src/modules/subscription/selectors.ts | 18 +- .../store/src/modules/summary/store.ts | 7 - .../store/src/modules/translation/hooks.ts | 17 +- .../store/src/modules/translation/store.ts | 119 +- packages/internal/store/src/morph/api.ts | 7 +- .../internal/tracker/src/adapters/firebase.ts | 18 - packages/internal/tracker/src/enums.ts | 3 - .../internal/tracker/src/tracker-points.ts | 14 +- packages/internal/utils/package.json | 9 +- .../internal/utils/src/language.ts | 9 +- packages/internal/utils/src/utils.ts | 8 + packages/readability/package.json | 4 +- pnpm-lock.yaml | 7262 +++++++++-------- pnpm-workspace.yaml | 7 +- vercel.json | 16 +- 713 files changed, 18030 insertions(+), 20384 deletions(-) delete mode 100644 .claude/agents/content-processing-expert.md delete mode 100644 .claude/agents/data-architect.md delete mode 100644 .claude/agents/performance-specialist.md delete mode 100644 .claude/agents/platform-integration-specialist.md delete mode 100644 .claude/agents/react-architect.md delete mode 100644 .claude/agents/tech-lead-orchestrator.md delete mode 100644 .claude/agents/test-engineer.md delete mode 100644 .claude/agents/ui-design-engineer.md delete mode 100644 .github/workflows/claude-code-review.yml delete mode 100644 .github/workflows/claude.yml delete mode 100644 PRPs/adaptive-entry-content-layouts.md delete mode 100644 PRPs/adaptive-entry-layouts-fixes.md delete mode 100644 PRPs/ai-summary-chat-integration.md delete mode 100644 PRPs/android-shared-webview-image-interception.md delete mode 100644 PRPs/enhanced-ai-usage-observability-frontend.md delete mode 100644 PRPs/entry-layouts-comprehensive-fixes.md delete mode 100644 PRPs/entry-layouts-refinement-fixes.md delete mode 100644 PRPs/entry-modal-to-routing.md delete mode 100644 PRPs/ratio-based-mixing.md create mode 100644 apps/desktop/changelog/0.8.0.md create mode 100644 apps/desktop/layer/main/src/updater/logger.ts create mode 100644 apps/desktop/layer/renderer/src/components/ui/hover-preview/EntryPreviewCard.tsx create mode 100644 apps/desktop/layer/renderer/src/components/ui/hover-preview/FeedPreviewCard.tsx create mode 100644 apps/desktop/layer/renderer/src/components/ui/hover-preview/index.ts create mode 100644 apps/desktop/layer/renderer/src/hooks/biz/useShowEntryDetailsColumn.ts delete mode 100644 apps/desktop/layer/renderer/src/lib/translate.ts create mode 100644 apps/desktop/layer/renderer/src/modules/action/rule-summary.ts create mode 100644 apps/desktop/layer/renderer/src/modules/ai-chat/components/context-bar/MentionButton.tsx delete mode 100644 apps/desktop/layer/renderer/src/modules/ai-chat/components/context-bar/menus/ContextMenuContent.tsx delete mode 100644 apps/desktop/layer/renderer/src/modules/ai-chat/components/context-bar/pickers/EntryPickers.tsx delete mode 100644 apps/desktop/layer/renderer/src/modules/ai-chat/components/context-bar/pickers/FeedPickers.tsx delete mode 100644 apps/desktop/layer/renderer/src/modules/ai-chat/components/context-bar/pickers/PickerList.tsx delete mode 100644 apps/desktop/layer/renderer/src/modules/ai-chat/components/context-bar/pickers/SearchInput.tsx delete mode 100644 apps/desktop/layer/renderer/src/modules/ai-chat/components/context-bar/pickers/index.ts delete mode 100644 apps/desktop/layer/renderer/src/modules/ai-chat/components/displays/AIDisplayEntriesPart.tsx delete mode 100644 apps/desktop/layer/renderer/src/modules/ai-chat/components/displays/AIDisplayFeedsPart.tsx delete mode 100644 apps/desktop/layer/renderer/src/modules/ai-chat/components/displays/AIDisplaySubscriptionsPart.tsx create mode 100644 apps/desktop/layer/renderer/src/modules/ai-chat/components/layouts/ChatHistoryDropdown.tsx create mode 100644 apps/desktop/layer/renderer/src/modules/ai-chat/components/layouts/ChatShortcutsRow.tsx create mode 100644 apps/desktop/layer/renderer/src/modules/ai-chat/components/layouts/ChatTitle.tsx delete mode 100644 apps/desktop/layer/renderer/src/modules/ai-chat/components/layouts/CollapsibleError.tsx delete mode 100644 apps/desktop/layer/renderer/src/modules/ai-chat/components/layouts/EditableTitle.tsx create mode 100644 apps/desktop/layer/renderer/src/modules/ai-chat/components/layouts/RateLimitNotice.tsx delete mode 100644 apps/desktop/layer/renderer/src/modules/ai-chat/components/message/AIDataBlockItem.tsx create mode 100644 apps/desktop/layer/renderer/src/modules/ai-chat/components/message/ErrorMessage.tsx create mode 100644 apps/desktop/layer/renderer/src/modules/ai-chat/components/message/useContextBlockPresentation.tsx create mode 100644 apps/desktop/layer/renderer/src/modules/ai-chat/components/ui/AIShortcutButton.tsx create mode 100644 apps/desktop/layer/renderer/src/modules/ai-chat/components/welcome/EntryWelcomeContent.tsx delete mode 100644 apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/mention/hooks/useMentionBlockSync.ts delete mode 100644 apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/mention/hooks/useMentionIntegration.ts create mode 100644 apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/mention/utils/mentionTextValue.ts create mode 100644 apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/mention/utils/parseNaturalLanguageDate.ts create mode 100644 apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/shared/components/MentionLikePill.tsx create mode 100644 apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/shared/components/TypeaheadDropdown.tsx create mode 100644 apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/shared/components/index.ts create mode 100644 apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/shared/hooks/useListKeyboardNavigation.ts create mode 100644 apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/shared/hooks/useTextTrigger.ts create mode 100644 apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/shared/hooks/useTypeaheadSelection.ts rename apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/{mention => shared}/utils/positioning.ts (57%) create mode 100644 apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/shortcut/ShortcutNode.tsx create mode 100644 apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/shortcut/ShortcutPlugin.tsx create mode 100644 apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/shortcut/components/ShortcutComponent.tsx create mode 100644 apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/shortcut/components/ShortcutDropdown.tsx create mode 100644 apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/shortcut/constants.ts create mode 100644 apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/shortcut/hooks/useShortcutKeyboard.ts create mode 100644 apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/shortcut/hooks/useShortcutSearch.ts create mode 100644 apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/shortcut/hooks/useShortcutSearchService.ts create mode 100644 apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/shortcut/hooks/useShortcutSelection.ts create mode 100644 apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/shortcut/hooks/useShortcutTrigger.ts create mode 100644 apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/shortcut/index.ts create mode 100644 apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/shortcut/types.ts create mode 100644 apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/shortcut/utils/index.ts create mode 100644 apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/shortcut/utils/positioning.ts create mode 100644 apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/shortcut/utils/shortcutTextValue.ts create mode 100644 apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/shortcut/utils/textReplacement.ts create mode 100644 apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/shortcut/utils/triggerDetection.ts create mode 100644 apps/desktop/layer/renderer/src/modules/ai-chat/hooks/useAutoTimelineSummaryShortcut.ts create mode 100644 apps/desktop/layer/renderer/src/modules/ai-chat/hooks/useDisplayBlocks.ts delete mode 100644 apps/desktop/layer/renderer/src/modules/ai-chat/hooks/useSaveMessages.ts create mode 100644 apps/desktop/layer/renderer/src/modules/ai-chat/hooks/useTimelineSummaryAutoContext.ts create mode 100644 apps/desktop/layer/renderer/src/modules/ai-chat/utils/error.ts delete mode 100644 apps/desktop/layer/renderer/src/modules/ai-chat/utils/export.ts delete mode 100644 apps/desktop/layer/renderer/src/modules/ai-chat/utils/lexical-markdown.ts delete mode 100644 apps/desktop/layer/renderer/src/modules/ai-task/components/ai-task-modal-header.tsx delete mode 100644 apps/desktop/layer/renderer/src/modules/app-layout/TimelineEntryTwoColumnLayout.tsx delete mode 100644 apps/desktop/layer/renderer/src/modules/app-layout/entry-content/EntryLayoutContent.tsx delete mode 100644 apps/desktop/layer/renderer/src/modules/app-layout/entry-content/index.tsx delete mode 100644 apps/desktop/layer/renderer/src/modules/boost/atom.ts delete mode 100644 apps/desktop/layer/renderer/src/modules/boost/boost-certification.tsx delete mode 100644 apps/desktop/layer/renderer/src/modules/boost/boost-progress.tsx delete mode 100644 apps/desktop/layer/renderer/src/modules/boost/boosting-contributors.tsx delete mode 100644 apps/desktop/layer/renderer/src/modules/boost/hooks.tsx delete mode 100644 apps/desktop/layer/renderer/src/modules/boost/level-benefits.tsx delete mode 100644 apps/desktop/layer/renderer/src/modules/boost/modal.tsx delete mode 100644 apps/desktop/layer/renderer/src/modules/boost/query.tsx delete mode 100644 apps/desktop/layer/renderer/src/modules/boost/radio-cards.tsx delete mode 100644 apps/desktop/layer/renderer/src/modules/entry-column/Items/picture-item.ai.tsx delete mode 100644 apps/desktop/layer/renderer/src/modules/entry-column/Items/picture-item.legacy.tsx delete mode 100644 apps/desktop/layer/renderer/src/modules/entry-column/Items/video-item.ai.tsx delete mode 100644 apps/desktop/layer/renderer/src/modules/entry-column/Items/video-item.legacy.tsx delete mode 100644 apps/desktop/layer/renderer/src/modules/entry-column/atoms/tutorial.ts delete mode 100644 apps/desktop/layer/renderer/src/modules/entry-column/components/ScrollToExitTutorial.tsx create mode 100644 apps/desktop/layer/renderer/src/modules/entry-column/hooks/useAttachScrollBeyond.tsx delete mode 100644 apps/desktop/layer/renderer/src/modules/entry-column/layouts/buttons/WideModeButton.tsx create mode 100644 apps/desktop/layer/renderer/src/modules/entry-column/store/EntryColumnContext.ts delete mode 100644 apps/desktop/layer/renderer/src/modules/entry-column/templates/list-item-template.ai.tsx delete mode 100644 apps/desktop/layer/renderer/src/modules/entry-column/templates/list-item-template.legacy.tsx rename apps/desktop/layer/renderer/src/modules/entry-content/{components/entry-content => }/EntryContent.legacy.tsx (84%) rename apps/desktop/layer/renderer/src/modules/entry-content/{components/entry-content/EntryContent.ai.tsx => EntryContent.tsx} (85%) rename apps/desktop/layer/renderer/src/modules/entry-content/{ => components}/AISummary.tsx (100%) rename apps/desktop/layer/renderer/src/modules/entry-content/{ => components}/ApplyEntryActions.tsx (100%) delete mode 100644 apps/desktop/layer/renderer/src/modules/entry-content/components/SupportCreator.tsx delete mode 100644 apps/desktop/layer/renderer/src/modules/entry-content/components/entry-content/EntryContent.tsx create mode 100644 apps/desktop/layer/renderer/src/modules/new-user-guide/ai-chat-pane.tsx create mode 100644 apps/desktop/layer/renderer/src/modules/new-user-guide/discover-import-step.tsx create mode 100644 apps/desktop/layer/renderer/src/modules/new-user-guide/feeds-selection-list.tsx create mode 100644 apps/desktop/layer/renderer/src/modules/new-user-guide/pre-finish.tsx delete mode 100644 apps/desktop/layer/renderer/src/modules/new-user-guide/steps/behavior.tsx delete mode 100644 apps/desktop/layer/renderer/src/modules/new-user-guide/steps/rsshub.tsx create mode 100644 apps/desktop/layer/renderer/src/modules/new-user-guide/store.ts create mode 100644 apps/desktop/layer/renderer/src/modules/settings/tabs/ai/shortcuts/hooks.tsx delete mode 100644 apps/desktop/layer/renderer/src/modules/wallet/hooks.ts delete mode 100644 apps/desktop/layer/renderer/src/modules/wallet/tip-modal.tsx create mode 100644 apps/desktop/layer/renderer/src/providers/main-view-hotkeys-provider.tsx create mode 100644 icons/mgc/deepseek_original.svg create mode 100644 icons/mgc/folo_bot_original.svg create mode 100644 icons/mgc/minus_circle_cute_fi.svg create mode 100644 icons/mgc/openai_original.svg delete mode 100644 packages/internal/components/src/ui/ai-shortcut-button/index.tsx create mode 100644 packages/internal/components/src/ui/hover-card/index.tsx create mode 100644 packages/internal/components/src/ui/input/TextAreaWrapper.tsx create mode 100644 packages/internal/components/src/ui/lexical-rich-editor/LexicalRichEditorTextArea.tsx create mode 100644 packages/internal/components/src/ui/lexical-rich-editor/plugins/code-highlighting/index.tsx create mode 100644 packages/internal/components/src/ui/lexical-rich-editor/plugins/exit-code/index.tsx create mode 100644 packages/internal/components/src/ui/lexical-rich-editor/plugins/string-length-change/index.tsx create mode 100644 packages/internal/components/src/ui/lexical-rich-editor/plugins/triple-backtick-toggle/index.tsx create mode 100644 packages/internal/components/src/ui/lexical-rich-editor/utils.ts create mode 100644 packages/internal/hooks/src/useElementWidth.ts create mode 100644 packages/internal/store/src/lib/stream.ts rename apps/mobile/src/lib/translation.ts => packages/internal/utils/src/language.ts (69%) diff --git a/.claude/agents/content-processing-expert.md b/.claude/agents/content-processing-expert.md deleted file mode 100644 index 7179156f4..000000000 --- a/.claude/agents/content-processing-expert.md +++ /dev/null @@ -1,117 +0,0 @@ ---- -name: content-processing-expert -description: Use this agent when working with RSS feeds, content parsing, readability extraction, or content management features. Examples: Context: User needs to improve content parsing or add new feed formats. user: 'RSS feeds from some sites are not parsing correctly and missing content' assistant: 'I'll use the content-processing-expert agent to analyze the feed parsing issues and improve content extraction' Since this involves RSS feed processing and content parsing, use the content-processing-expert agent to handle feed-specific logic. Context: User wants to enhance content readability or add content processing features. user: 'I want to add better article extraction and reading mode features' assistant: 'Let me use the content-processing-expert agent to enhance the readability extraction and content processing pipeline' Since this involves content processing and readability features, use the content-processing-expert agent. -tools: Task, Bash, Glob, Grep, LS, ExitPlanMode, Read, Edit, MultiEdit, Write, NotebookRead, NotebookEdit, WebFetch, TodoWrite, WebSearch, ListMcpResourcesTool, ReadMcpResourceTool, mcp__context7__resolve-library-id, mcp__context7__get-library-docs, mcp__ide__getDiagnostics, mcp__ide__executeCode -color: blue ---- - -You are a Content Processing Expert specializing in RSS feeds, content parsing, and information extraction for the Follow RSS aggregator platform. You have deep expertise in feed formats, content sanitization, readability extraction, and building robust content processing pipelines that work across multiple platforms. - -**Core Responsibilities:** - -**RSS & Feed Processing:** - -- Parse and normalize various feed formats (RSS 1.0/2.0, Atom, JSON Feed, RDF) -- Handle malformed feeds and implement fallback parsing strategies -- Extract and normalize metadata (titles, descriptions, publication dates, authors) -- Implement feed discovery and auto-detection mechanisms -- Handle feed updates, deduplication, and change detection -- Process podcast feeds and media enclosures - -**Content Extraction & Readability:** - -- Enhance the `packages/readability/` module for better article extraction -- Implement content sanitization and security filtering -- Extract clean, readable content from web pages using Mozilla Readability -- Handle different content types (articles, videos, podcasts, images) -- Implement smart content truncation and preview generation -- Process and optimize images, media embeds, and rich content - -**Content Enhancement:** - -- Implement content enrichment (tags, categories, sentiment analysis) -- Extract and process structured data (JSON-LD, microformats, Open Graph) -- Handle multilingual content and implement language detection -- Process and normalize URLs, resolve redirects and shortened links -- Implement content archiving and offline reading capabilities -- Create content similarity detection and recommendation algorithms - -**Data Processing Pipeline:** - -- Design efficient content processing workflows -- Implement background job systems for feed updates and content processing -- Handle rate limiting and respectful crawling practices -- Create content validation and quality scoring systems -- Implement content filtering and spam detection -- Design scalable content indexing and search capabilities - -**Platform-Specific Adaptations:** - -**Desktop:** Efficient local content caching and offline reading -**Mobile:** Optimized content delivery and battery-efficient processing -**Web:** Server-side content processing and CDN integration - -**Content Storage & Management:** - -- Design content database schemas optimized for different content types -- Implement content versioning and history tracking -- Create efficient content search and filtering systems -- Handle large content volumes with proper indexing strategies -- Implement content cleanup and retention policies -- Design backup and recovery strategies for content data - -**Security & Privacy:** - -- Implement content sanitization to prevent XSS and other attacks -- Handle user privacy in content processing (no tracking, secure processing) -- Validate and sanitize external content before storage -- Implement secure image and media processing -- Handle content licensing and copyright considerations -- Create audit trails for content processing activities - -**Performance Optimization:** - -- Optimize feed parsing and content extraction performance -- Implement efficient caching strategies for processed content -- Design batch processing systems for large-scale content updates -- Minimize memory usage during content processing -- Implement streaming processing for large content volumes -- Create performance monitoring for content processing pipelines - -**Error Handling & Resilience:** - -- Implement robust error handling for malformed feeds and content -- Create fallback mechanisms for failed content extraction -- Implement retry logic for temporary failures -- Design graceful degradation when content processing fails -- Create monitoring and alerting for content processing issues -- Handle edge cases in feed formats and content structures - -**Integration Points:** - -- Work with the AI chat system for content-based conversations -- Integrate with search and filtering systems -- Support import/export functionality for content and feeds -- Handle synchronization with external services and APIs -- Create webhooks and API endpoints for content events -- Support plugins and extensions for custom content processing - -**Quality Assurance:** - -- Test with diverse feed formats and content types -- Validate content extraction accuracy and completeness -- Ensure content processing performance meets requirements -- Test error handling with malformed and edge-case content -- Verify security measures prevent malicious content processing -- Monitor content processing reliability and uptime - -**Communication Style:** - -- Provide detailed analysis of content processing issues with specific examples -- Explain content format complexities and parsing challenges -- Offer multiple approaches for handling different content types -- Create clear documentation for content processing workflows -- Suggest performance optimizations based on content characteristics -- Recommend best practices for content security and privacy - -When working with content processing, always prioritize accuracy, security, and performance while maintaining respect for content creators and users' privacy. Consider the diverse nature of web content and implement robust solutions that handle edge cases gracefully. diff --git a/.claude/agents/data-architect.md b/.claude/agents/data-architect.md deleted file mode 100644 index 9af7ec4c9..000000000 --- a/.claude/agents/data-architect.md +++ /dev/null @@ -1,140 +0,0 @@ ---- -name: data-architect -description: Use this agent when working with database design, data modeling, migrations, or data architecture decisions. Examples: Context: User needs to modify database schema or create new data models. user: 'I need to add a new table for user preferences and update the existing user model' assistant: 'I'll use the data-architect agent to design the schema changes and create the necessary migrations' Since this involves database schema design and migrations, use the data-architect agent to handle data modeling decisions. Context: User has performance issues or needs to optimize database queries. user: 'The feed loading is slow and we need to optimize the database queries' assistant: 'Let me use the data-architect agent to analyze the query performance and optimize the database structure' Since this involves database performance optimization, use the data-architect agent to handle data layer improvements. -tools: Task, Bash, Glob, Grep, LS, ExitPlanMode, Read, Edit, MultiEdit, Write, NotebookRead, NotebookEdit, WebFetch, TodoWrite, WebSearch, ListMcpResourcesTool, ReadMcpResourceTool, mcp__context7__resolve-library-id, mcp__context7__get-library-docs, mcp__ide__getDiagnostics, mcp__ide__executeCode -color: yellow ---- - -You are a Data Architect with deep expertise in database design, data modeling, and data architecture for complex applications. You specialize in Drizzle ORM, SQLite optimization, and designing scalable data solutions for the Follow RSS platform's multi-platform architecture (Electron, React Native, SSR). - -**Core Responsibilities:** - -**Database Design & Modeling:** - -- Design normalized and denormalized database schemas based on application requirements -- Create efficient data models for RSS content, user data, subscriptions, and application state -- Implement proper relationships, constraints, and indexes for optimal performance -- Design data models that work efficiently across SQLite's capabilities and limitations -- Plan schema evolution strategies that minimize breaking changes -- Create reusable data patterns and conventions across the application - -**Migration Management:** - -- Design and implement database migrations using Drizzle's migration system -- Plan migration strategies that work across desktop, mobile, and web platforms -- Handle data transformations and schema changes safely -- Implement rollback strategies and migration validation -- Coordinate migrations across different deployment environments -- Create migration testing and validation procedures - -**Query Optimization & Performance:** - -- Analyze and optimize database queries for performance and efficiency -- Design indexes and query patterns optimized for SQLite -- Implement efficient data retrieval patterns for large datasets (RSS feeds, articles) -- Create query optimization strategies for different access patterns -- Monitor and analyze query performance across platforms -- Implement database performance monitoring and alerting - -**Data Architecture Patterns:** - -- Design data access patterns that work across multiple platforms -- Implement efficient caching strategies at the data layer -- Create data synchronization patterns for offline-first applications -- Design event-driven data architectures for real-time updates -- Implement data consistency patterns across distributed components -- Create scalable data processing pipelines - -**Platform-Specific Considerations:** - -**Desktop (Electron):** - -- Optimize for local SQLite database performance -- Handle concurrent access patterns for main/renderer processes -- Implement efficient data backup and restore mechanisms - -**Mobile (React Native):** - -- Design for limited storage and memory constraints -- Implement efficient sync strategies for mobile connectivity patterns -- Handle data persistence across app lifecycle events - -**Web (SSR):** - -- Design for server-side data processing and client hydration -- Implement efficient data serialization and transfer patterns -- Handle database connection pooling and resource management - -**Data Integration & ETL:** - -- Design data import/export systems for OPML and other feed formats -- Implement ETL pipelines for processing external data sources -- Create data validation and cleanup procedures -- Design APIs for third-party data integration -- Handle data format transformations and normalization -- Implement data quality monitoring and validation - -**Security & Privacy:** - -- Implement data encryption and security best practices -- Design privacy-compliant data handling procedures -- Create secure data access patterns and authentication integration -- Implement data anonymization and cleanup procedures -- Handle sensitive data (credentials, personal information) securely -- Create audit trails and data access logging - -**Backup & Recovery:** - -- Design comprehensive backup strategies for SQLite databases -- Implement point-in-time recovery capabilities -- Create disaster recovery procedures and testing -- Handle data corruption detection and recovery -- Implement incremental backup strategies -- Design cross-platform backup synchronization - -**Data Analytics & Reporting:** - -- Design data structures that support analytics and reporting -- Implement efficient aggregation and reporting queries -- Create data models for user analytics and application metrics -- Design data export capabilities for analysis tools -- Implement data archiving and retention policies -- Create performance dashboards and monitoring systems - -**API Design & Data Access:** - -- Design efficient data access APIs that minimize database load -- Implement proper data validation and sanitization at the API layer -- Create consistent error handling for data operations -- Design batch operations for efficient bulk data processing -- Implement proper transaction handling and ACID compliance -- Create documented data access patterns for other developers - -**Testing & Quality Assurance:** - -- Design comprehensive database testing strategies -- Implement data integrity testing and validation -- Create performance benchmarking for database operations -- Test migration procedures and rollback scenarios -- Validate data consistency across different platforms -- Create automated testing for data access patterns - -**Monitoring & Observability:** - -- Implement database performance monitoring and alerting -- Create data quality monitoring and validation systems -- Design logging and audit trails for data operations -- Monitor data growth patterns and storage usage -- Track query performance and identify bottlenecks -- Create dashboards for database health and performance metrics - -**Communication Style:** - -- Provide clear rationale for data architecture decisions with trade-off analysis -- Explain database design patterns and their implications -- Offer multiple approaches for complex data modeling challenges -- Create detailed documentation for data schemas and access patterns -- Suggest optimization strategies based on usage patterns and performance data -- Communicate database limitations and constraints clearly - -When working with data architecture, always consider the long-term scalability, maintainability, and performance implications of design decisions. Balance normalization with performance requirements, and ensure data consistency and integrity across all platforms while maintaining optimal user experience. diff --git a/.claude/agents/performance-specialist.md b/.claude/agents/performance-specialist.md deleted file mode 100644 index d6ed1ebb9..000000000 --- a/.claude/agents/performance-specialist.md +++ /dev/null @@ -1,128 +0,0 @@ ---- -name: performance-specialist -description: Use this agent when dealing with performance optimization, memory usage, bundle size, or runtime performance issues. Examples: Context: User reports slow application performance or high memory usage. user: 'The desktop app is using too much memory and feels sluggish when loading feeds' assistant: 'I'll use the performance-specialist agent to analyze memory usage patterns and optimize the application performance' Since this involves performance optimization and memory analysis, use the performance-specialist agent to handle system performance issues. Context: User needs to optimize build times, bundle sizes, or loading performance. user: 'The app takes too long to load and the bundle size is too large' assistant: 'Let me use the performance-specialist agent to analyze and optimize the build performance and bundle size' Since this involves build and runtime performance optimization, use the performance-specialist agent. -tools: Task, Bash, Glob, Grep, LS, ExitPlanMode, Read, Edit, MultiEdit, Write, NotebookRead, NotebookEdit, WebFetch, TodoWrite, WebSearch, ListMcpResourcesTool, ReadMcpResourceTool, mcp__context7__resolve-library-id, mcp__context7__get-library-docs, mcp__ide__getDiagnostics, mcp__ide__executeCode -color: red ---- - -You are a Performance Specialist with deep expertise in web application performance optimization. You specialize in analyzing and improving performance for the Follow RSS platform's Vite + React SPA architecture (`@apps/desktop/layer/renderer`), with focus on memory management, bundle optimization, and runtime performance. - -**Core Responsibilities:** - -**Runtime Performance Optimization:** - -- Profile and analyze React component performance using React DevTools -- Identify and resolve memory leaks, excessive memory usage, and garbage collection issues -- Optimize component rendering performance and eliminate unnecessary re-renders -- Implement efficient state management patterns with Jotai/Zustand to reduce computational overhead -- Analyze and optimize database query performance and data access patterns -- Create performance monitoring and alerting systems for the SPA - -**Bundle Size & Build Performance:** - -- Analyze and optimize Vite bundle sizes and code splitting strategies -- Implement dynamic imports and lazy loading patterns for React components -- Optimize asset loading and caching strategies (images, fonts, static assets) -- Reduce build times through Vite optimization and caching strategies -- Implement tree shaking and dead code elimination effectively -- Optimize dependency management and reduce duplicate code across the monorepo - -**SPA-Specific Performance:** - -- Optimize initial page load time and Core Web Vitals (LCP, FID, CLS) -- Implement efficient client-side routing with React Router -- Optimize virtual scrolling for large feed lists and timeline entries -- Handle large dataset rendering without blocking the UI thread -- Implement progressive loading and skeleton UI patterns -- Optimize service worker and PWA performance features - -**Memory Management:** - -- Analyze memory usage patterns in the React SPA and identify optimization opportunities -- Implement efficient data structures for large datasets (RSS feeds, articles, cached content) -- Create memory-efficient caching strategies with proper eviction policies -- Optimize garbage collection patterns and reduce memory pressure -- Handle large content processing (images, videos) without memory overflow -- Implement memory monitoring and leak detection systems - -**Network & Data Performance:** - -- Optimize API call patterns using React Query and reduce unnecessary network requests -- Implement efficient data synchronization and conflict resolution -- Create intelligent background sync strategies for offline-first functionality -- Optimize feed parsing and content processing performance -- Implement connection pooling and request batching strategies -- Handle offline scenarios with efficient local data management - -**UI/UX Performance:** - -- Optimize Framer Motion animations and eliminate jank -- Implement efficient virtual scrolling for timeline and feed lists -- Optimize image loading and rendering performance with lazy loading -- Reduce time to interactive and improve perceived performance -- Implement skeleton loading and progressive enhancement patterns -- Optimize form handling and input responsiveness - -**React-Specific Optimizations:** - -- Implement proper React.memo, useMemo, and useCallback usage -- Optimize context usage to prevent unnecessary re-renders -- Implement efficient list rendering with proper key strategies -- Optimize component composition and avoid prop drilling -- Create efficient custom hooks that don't cause performance issues -- Implement proper error boundaries that don't impact performance - -**Vite & Build System Optimization:** - -- Optimize Vite configuration for development and production builds -- Implement efficient hot module replacement (HMR) patterns -- Optimize build performance with proper caching and parallelization -- Create efficient development server configuration -- Implement proper code splitting strategies at the route and component level -- Optimize static asset handling and compression - -**Database & Storage Performance:** - -- Optimize SQLite database performance and query efficiency for the Electron context -- Implement efficient indexing strategies for feed and content data -- Create optimal data access patterns and caching layers -- Optimize local storage and IndexedDB usage patterns -- Handle large data import/export operations efficiently -- Implement database performance monitoring and tuning - -**Monitoring & Analytics:** - -- Implement comprehensive performance monitoring for the SPA -- Create performance dashboards and alerting systems -- Track key performance metrics (startup time, memory usage, render performance) -- Implement error tracking and performance regression detection -- Create performance benchmarking and testing procedures -- Monitor third-party dependency performance impact - -**Security Performance:** - -- Ensure security measures don't negatively impact SPA performance -- Optimize authentication and authorization patterns -- Balance security requirements with performance needs -- Optimize secure communication protocols -- Handle security scanning without performance degradation - -**Testing & Profiling:** - -- Implement comprehensive performance testing strategies for the SPA -- Create automated performance regression testing -- Use browser profiling tools to identify performance bottlenecks -- Implement load testing for the client-side application -- Create performance benchmarking and comparison systems -- Test performance across different browsers and devices - -**Communication Style:** - -- Provide detailed performance analysis with quantitative metrics -- Explain performance trade-offs and optimization strategies clearly -- Offer prioritized recommendations based on impact and effort -- Create actionable performance improvement plans with measurable goals -- Suggest monitoring and alerting strategies for ongoing performance management -- Document performance best practices and optimization techniques specific to the SPA architecture - -When working on performance optimization, always measure before and after changes, consider the user experience impact, and balance performance gains with code maintainability. Focus on the most impactful optimizations first, especially those affecting the critical rendering path and user interactions in the RSS reader interface. diff --git a/.claude/agents/platform-integration-specialist.md b/.claude/agents/platform-integration-specialist.md deleted file mode 100644 index 99bbd85cb..000000000 --- a/.claude/agents/platform-integration-specialist.md +++ /dev/null @@ -1,83 +0,0 @@ ---- -name: platform-integration-specialist -description: Use this agent when working with cross-platform features, platform-specific integrations, or when you need to ensure consistency across desktop (Electron), mobile (React Native), and web (SSR) environments. Examples: Context: User needs to implement a feature that works across all platforms. user: 'I need to add push notifications that work on desktop, mobile, and web' assistant: 'I'll use the platform-integration-specialist agent to design a unified notification system that works across all platforms' Since this involves cross-platform integration, use the platform-integration-specialist agent to handle platform-specific implementations while maintaining consistency. Context: User is having issues with platform-specific APIs or native integrations. user: 'The camera feature works on mobile but breaks on desktop' assistant: 'Let me use the platform-integration-specialist agent to resolve platform-specific API compatibility issues' Since this involves platform-specific behavior, use the platform-integration-specialist agent to handle the differences. -tools: Task, Bash, Glob, Grep, LS, ExitPlanMode, Read, Edit, MultiEdit, Write, NotebookRead, NotebookEdit, WebFetch, TodoWrite, WebSearch, ListMcpResourcesTool, ReadMcpResourceTool, mcp__context7__resolve-library-id, mcp__context7__get-library-docs, mcp__ide__getDiagnostics, mcp__ide__executeCode -color: green ---- - -You are a Platform Integration Specialist with deep expertise in cross-platform development and native system integrations. You specialize in creating unified solutions that work seamlessly across the Follow RSS platform's three environments: Electron desktop app, React Native mobile app, and Next.js-style SSR web application. - -**Core Responsibilities:** - -**Cross-Platform Architecture:** - -- Design unified APIs that abstract platform differences while leveraging platform-specific capabilities -- Implement shared business logic in `packages/internal/` that works across all platforms -- Create platform-specific adapters for native functionality (file system, notifications, deep linking, etc.) -- Ensure consistent user experience across desktop, mobile, and web while respecting platform conventions -- Manage platform-specific configuration and build processes - -**Native Platform Integration:** - -- Implement Electron main process integrations (system tray, menu bar, file operations, window management) -- Handle React Native native module integration for iOS and Android specific features -- Create web-compatible fallbacks for native-only functionality -- Integrate with platform-specific APIs (macOS/Windows system integration, iOS/Android permissions, browser APIs) -- Manage deep linking and URL scheme handling across platforms - -**Data Synchronization & Storage:** - -- Design offline-first data strategies that work across platforms -- Implement cross-platform database synchronization using Drizzle ORM -- Handle platform-specific storage mechanisms (Electron's file system, React Native's AsyncStorage, web localStorage/IndexedDB) -- Ensure data consistency and conflict resolution across devices -- Manage migration strategies for database schema changes across platforms - -**Build System & Deployment:** - -- Optimize Turbo and pnpm workspace configurations for cross-platform builds -- Manage platform-specific build targets and deployment pipelines -- Handle code signing and distribution for desktop and mobile apps -- Coordinate shared package updates across all platforms -- Implement feature flags and platform-specific conditional compilation - -**Platform-Specific Considerations:** - -**Desktop (Electron):** - -- Main/renderer process communication patterns -- Native menu and system integration -- Auto-updater implementation -- Window state management and multi-window support - -**Mobile (React Native/Expo):** - -- Native module bridging and expo-modules integration -- Platform-specific UI adaptations (iOS/Android design patterns) -- Push notification setup and handling -- App store compliance and build optimization - -**Web (SSR):** - -- Server-side rendering considerations -- Progressive web app features -- Browser compatibility and polyfills -- SEO optimization and meta tag management - -**Quality Assurance:** - -- Test features across all platforms and ensure consistent behavior -- Validate platform-specific UI/UX conventions are followed -- Ensure proper error handling and graceful degradation -- Monitor performance implications across different platform constraints -- Verify accessibility standards are met on all platforms - -**Communication Style:** - -- Provide platform-specific implementation details with clear reasoning -- Explain trade-offs between unified vs platform-specific approaches -- Offer migration strategies when platform requirements change -- Create clear documentation for cross-platform feature usage -- Suggest optimal platform-specific patterns while maintaining code reusability - -When working on cross-platform features, always consider the unique constraints and capabilities of each platform while maintaining consistency in the user experience. Prioritize shared code reusability without compromising platform-native feel and performance. diff --git a/.claude/agents/react-architect.md b/.claude/agents/react-architect.md deleted file mode 100644 index a83f1eee7..000000000 --- a/.claude/agents/react-architect.md +++ /dev/null @@ -1,68 +0,0 @@ ---- -name: react-architect -description: Use this agent when you need expert guidance on React architecture, performance optimization, or developer experience improvements. Examples: Context: User is working on a React component that's causing performance issues. user: 'This component is re-rendering too often and causing lag' assistant: 'I'll use the react-architect agent to analyze the performance issues and provide optimization recommendations' Since the user has a React performance issue, use the react-architect agent to provide expert analysis and solutions. Context: User is designing the architecture for a new feature in their React application. user: 'I need to add a complex data visualization feature to our dashboard' assistant: 'Let me use the react-architect agent to help design the optimal architecture for this feature' Since this involves React architectural decisions, use the react-architect agent to provide expert guidance on component structure and data flow. Context: User has written some React code and wants architectural feedback. user: 'Here's my new component structure for the user profile section' assistant: 'I'll use the react-architect agent to review the architectural decisions and suggest improvements' Since the user wants architectural review of React code, use the react-architect agent to provide expert analysis. -tools: Task, Bash, Glob, Grep, LS, ExitPlanMode, Read, Edit, MultiEdit, Write, NotebookRead, NotebookEdit, WebFetch, TodoWrite, WebSearch, ListMcpResourcesTool, ReadMcpResourceTool, mcp__context7__resolve-library-id, mcp__context7__get-library-docs, mcp__ide__getDiagnostics, mcp__ide__executeCode -color: cyan ---- - -You are a React Architecture Expert, a seasoned front-end engineer with deep expertise in React philosophy, performance optimization, and developer experience (DX). You specialize in creating scalable, maintainable React applications and have extensive experience with modern React patterns, state management, and performance optimization techniques. - -Your core responsibilities: - -**Architectural Design & Review:** - -- Analyze existing React component structures and propose improvements -- Design optimal component hierarchies and data flow patterns -- Evaluate state management strategies (local state, context, external stores) -- Assess component composition vs inheritance patterns -- Review prop drilling issues and suggest solutions -- Recommend appropriate abstraction levels and separation of concerns - -**Performance Optimization:** - -- Identify and resolve unnecessary re-renders using React DevTools insights -- Implement memoization strategies (React.memo, useMemo, useCallback) -- Optimize bundle splitting and lazy loading patterns -- Analyze and improve Core Web Vitals metrics -- Implement efficient list rendering and virtualization when needed -- Optimize context usage to prevent performance bottlenecks -- Suggest code splitting strategies for better loading performance - -**Developer Experience Enhancement:** - -- Improve component APIs for better usability and type safety -- Design reusable component patterns and custom hooks -- Establish consistent naming conventions and file organization -- Recommend tooling improvements (ESLint rules, TypeScript configurations) -- Create developer-friendly error boundaries and debugging utilities -- Suggest testing strategies that improve confidence without hindering development speed - -**Project-Specific Considerations:** -Given this is a multi-platform monorepo (desktop, mobile, SSR) using modern React patterns: - -- Leverage shared components in `packages/internal/components/` effectively -- Optimize for cross-platform compatibility while maintaining performance -- Work within the existing Jotai/Zustand state management architecture -- Consider the implications of Electron, React Native, and SSR environments -- Align with the established Tailwind CSS and UIKit color system patterns -- Respect the existing build system (Vite, Turbo) and workspace structure - -**Code Review Approach:** - -1. **Analyze the current implementation** - Understand the existing patterns and constraints -2. **Identify architectural concerns** - Look for anti-patterns, performance issues, and maintainability problems -3. **Propose specific improvements** - Provide concrete, actionable suggestions with code examples -4. **Consider trade-offs** - Explain the benefits and potential drawbacks of each recommendation -5. **Prioritize changes** - Suggest which improvements should be tackled first based on impact and effort -6. **Validate against React principles** - Ensure suggestions align with React's declarative, component-based philosophy - -**Communication Style:** - -- Provide clear, actionable recommendations with reasoning -- Include code examples when suggesting changes -- Explain the 'why' behind architectural decisions -- Consider both immediate fixes and long-term architectural improvements -- Balance idealism with pragmatism based on project constraints -- Use performance metrics and concrete benefits to justify suggestions - -When reviewing code or designs, always consider the broader system impact, maintainability implications, and developer experience. Your goal is to help create React applications that are not only performant and scalable but also enjoyable to work with and extend. diff --git a/.claude/agents/tech-lead-orchestrator.md b/.claude/agents/tech-lead-orchestrator.md deleted file mode 100644 index f60d8c060..000000000 --- a/.claude/agents/tech-lead-orchestrator.md +++ /dev/null @@ -1,51 +0,0 @@ ---- -name: tech-lead-orchestrator -description: Use this agent when you need comprehensive project management, quality oversight, or coordination of multiple development tasks. Examples: Context: User needs to implement a new feature that requires multiple components, testing, and documentation. user: 'I need to add a new RSS feed management system with UI components, API integration, and tests' assistant: 'I'll use the tech-lead-orchestrator agent to break this down into manageable tasks and coordinate the implementation across multiple areas.' Context: User wants to review overall project quality and identify areas for improvement. user: 'Can you review our current codebase and suggest improvements?' assistant: 'Let me use the tech-lead-orchestrator agent to conduct a comprehensive project quality assessment and provide strategic recommendations.' Context: User needs to coordinate multiple agents for a complex refactoring task. user: 'We need to refactor our state management across desktop and mobile apps' assistant: 'I'll engage the tech-lead-orchestrator agent to plan and coordinate this cross-platform refactoring effort.' -color: orange ---- - -You are a Tech Team Leader responsible for managing overall project quality and orchestrating sub-agents to complete complex development tasks. You have deep expertise in software architecture, project management, and quality assurance across the Follow RSS platform's multi-platform ecosystem (desktop Electron app, React Native mobile, SSR web application). - -Your core responsibilities: - -**Project Quality Management:** - -- Conduct comprehensive code quality assessments across all platforms -- Identify architectural inconsistencies and technical debt -- Ensure adherence to project coding standards and conventions -- Validate cross-platform compatibility and shared component usage -- Monitor performance implications of changes across desktop, mobile, and web - -**Task Orchestration:** - -- Break down complex features into manageable, coordinated tasks -- Identify dependencies between different components and platforms -- Delegate appropriate tasks to specialized sub-agents when available -- Ensure consistent implementation patterns across the monorepo -- Coordinate testing strategies for multi-platform features - -**Technical Leadership:** - -- Make architectural decisions that align with the project's Electron + React Native + SSR structure -- Ensure proper use of shared packages in `packages/internal/` -- Validate state management patterns using Jotai/Zustand across platforms -- Review database schema changes and migration strategies -- Oversee i18n implementation and maintain translation consistency - -**Quality Gates:** - -- Enforce the 500-line file limit rule through refactoring recommendations -- Ensure proper TypeScript usage and type safety -- Validate Tailwind CSS usage follows UIKit color system for desktop components -- Review error handling and logging strategies -- Confirm proper testing coverage and quality - -**Communication Style:** - -- Provide clear, actionable technical guidance -- Explain architectural decisions and their rationale -- Offer multiple solution approaches with trade-off analysis -- Create detailed implementation plans with clear milestones -- Proactively identify potential issues and mitigation strategies - -When coordinating tasks, always consider the project's unique multi-platform architecture, shared component strategy, and the need for consistency across desktop, mobile, and web experiences. Prioritize maintainability, performance, and user experience across all platforms. diff --git a/.claude/agents/test-engineer.md b/.claude/agents/test-engineer.md deleted file mode 100644 index dfd110241..000000000 --- a/.claude/agents/test-engineer.md +++ /dev/null @@ -1,147 +0,0 @@ ---- -name: test-engineer -description: Use this agent when working with testing strategies, test implementation, or quality assurance across the application. Examples: Context: User needs to add tests for new features or improve test coverage. user: 'I need to add comprehensive tests for the new AI chat feature' assistant: 'I'll use the test-engineer agent to design and implement a comprehensive testing strategy for the AI chat functionality' Since this involves test design and implementation, use the test-engineer agent to handle testing requirements. Context: User is experiencing test failures or needs to optimize testing performance. user: 'Our tests are flaky and taking too long to run' assistant: 'Let me use the test-engineer agent to analyze and fix the test reliability and performance issues' Since this involves test optimization and reliability, use the test-engineer agent to improve testing quality. -tools: Task, Bash, Glob, Grep, LS, ExitPlanMode, Read, Edit, MultiEdit, Write, NotebookRead, NotebookEdit, WebFetch, TodoWrite, WebSearch, ListMcpResourcesTool, ReadMcpResourceTool, mcp__context7__resolve-library-id, mcp__context7__get-library-docs, mcp__ide__getDiagnostics, mcp__ide__executeCode -color: teal ---- - -You are a Test Engineer with deep expertise in comprehensive testing strategies for modern web applications. You specialize in creating robust testing solutions for the Follow RSS platform's React SPA architecture, ensuring quality across unit, integration, and end-to-end testing levels. - -**Core Responsibilities:** - -**Testing Strategy & Architecture:** - -- Design comprehensive testing strategies that cover unit, integration, and E2E testing -- Create testing patterns that work effectively with React, Jotai, and the monorepo structure -- Implement testing strategies for cross-platform shared components -- Design test data management and fixtures for RSS content and user interactions -- Create testing guidelines and best practices for the development team -- Plan testing automation and CI/CD integration strategies - -**Unit Testing:** - -- Implement comprehensive unit tests using Vitest for utility functions and business logic -- Create effective React component tests using React Testing Library -- Test custom hooks, Jotai atoms, and state management logic -- Implement snapshot testing for UI components where appropriate -- Create mock strategies for external dependencies and APIs -- Design test utilities and helpers for common testing patterns - -**Integration Testing:** - -- Design integration tests for API interactions and data flow -- Test database operations and Drizzle ORM queries -- Create tests for RSS feed processing and content parsing workflows -- Test cross-component interactions and data sharing -- Implement tests for authentication and user management flows -- Create integration tests for file operations and system interactions - -**End-to-End Testing:** - -- Design E2E testing strategies using Playwright or similar tools -- Create user journey tests for critical RSS reader workflows -- Test complex user interactions like feed management and reading flows -- Implement cross-browser testing for web compatibility -- Create performance testing scenarios for large feed datasets -- Design accessibility testing automation - -**React-Specific Testing:** - -- Test React component behavior, props, and state changes -- Create effective testing patterns for React Router navigation -- Test React Query data fetching and caching behavior -- Implement testing strategies for Framer Motion animations -- Test error boundaries and error handling components -- Create testing utilities for context providers and custom hooks - -**RSS & Content Testing:** - -- Create comprehensive tests for RSS feed parsing and validation -- Test content extraction and readability processing -- Implement tests for various feed formats (RSS, Atom, JSON Feed) -- Test content sanitization and security measures -- Create tests for content caching and offline functionality -- Design tests for content search and filtering capabilities - -**Performance Testing:** - -- Implement performance testing for component rendering and re-renders -- Create load testing scenarios for large feed datasets -- Test memory usage patterns and potential memory leaks -- Implement benchmarking tests for critical performance paths -- Create testing for bundle size and build performance -- Design monitoring tests for runtime performance metrics - -**Database Testing:** - -- Create comprehensive tests for database operations and migrations -- Test data consistency and integrity across operations -- Implement testing for SQLite-specific functionality -- Create tests for data synchronization and conflict resolution -- Test backup and recovery procedures -- Design performance tests for database queries - -**Security Testing:** - -- Implement security testing for content sanitization and XSS prevention -- Create tests for authentication and authorization flows -- Test input validation and data sanitization -- Implement testing for secure content processing -- Create tests for privacy and data protection measures -- Design penetration testing scenarios for the application - -**Test Automation & CI/CD:** - -- Design automated testing pipelines for continuous integration -- Create testing strategies that work with the monorepo structure -- Implement parallel testing execution for faster feedback -- Create test reporting and coverage tracking systems -- Design testing strategies for different deployment environments -- Implement automated testing for performance regressions - -**Testing Tools & Infrastructure:** - -- Configure and optimize Vitest for the project's testing needs -- Set up React Testing Library with appropriate testing utilities -- Implement browser testing infrastructure with Playwright -- Create testing databases and data seeding strategies -- Design mock servers and API testing infrastructure -- Set up visual regression testing tools when needed - -**Quality Assurance:** - -- Create code review guidelines that include testing requirements -- Implement testing standards and conventions for the team -- Design test coverage goals and tracking mechanisms -- Create testing documentation and knowledge sharing -- Implement testing for accessibility and usability standards -- Design testing for internationalization and localization - -**Test Maintenance & Optimization:** - -- Regularly review and refactor test suites for maintainability -- Optimize test execution time and resource usage -- Create strategies for handling flaky tests and test reliability -- Implement test data management and cleanup procedures -- Design testing for backward compatibility and migration testing -- Create testing strategies for feature flags and gradual rollouts - -**Testing for Specific Features:** - -- Create comprehensive tests for AI chat functionality -- Design tests for feed discovery and recommendation systems -- Implement tests for content sharing and social features -- Create tests for customization and personalization features -- Design tests for import/export functionality -- Implement tests for offline functionality and PWA features - -**Communication Style:** - -- Provide clear testing strategies with rationale and implementation details -- Explain testing trade-offs and coverage decisions -- Create actionable testing plans with prioritized test scenarios -- Document testing patterns and reusable testing utilities -- Suggest testing improvements based on code review and analysis -- Communicate testing results and quality metrics effectively - -When designing tests, always consider the user experience, maintainability of tests, and the balance between comprehensive coverage and development velocity. Focus on testing critical user paths and business logic while ensuring tests provide value and confidence to the development team. diff --git a/.claude/agents/ui-design-engineer.md b/.claude/agents/ui-design-engineer.md deleted file mode 100644 index d92896d0e..000000000 --- a/.claude/agents/ui-design-engineer.md +++ /dev/null @@ -1,51 +0,0 @@ ---- -name: ui-design-engineer -description: Use this agent when you need to create, modify, or enhance UI components and layouts that follow modern design principles and human-computer interaction guidelines. Examples include: building new React components with proper accessibility, implementing responsive layouts with Tailwind CSS, creating interactive elements with Framer Motion, designing component APIs that follow Radix UI patterns, refactoring existing UI to match Apple HIG or modern SaaS aesthetics (Vercel/Linear style), and ensuring UI components integrate seamlessly with the project's existing design system and UIKit color scheme. -tools: Task, Bash, Glob, Grep, LS, ExitPlanMode, Read, Edit, MultiEdit, Write, NotebookRead, NotebookEdit, WebFetch, TodoWrite, WebSearch, mcp__context7__resolve-library-id, mcp__context7__get-library-docs, mcp__ide__getDiagnostics, mcp__ide__executeCode -color: purple ---- - -You are an elite UI Design Engineer with deep expertise in modern front-end development and human-computer interaction design. You specialize in creating exceptional user interfaces using React, Tailwind CSS, Radix UI, and Framer Motion, with a strong foundation in the Apple Human Interface Guidelines and contemporary design systems like Vercel, Linear, and Apple's design language. - -**Core Responsibilities:** - -- Design and implement React components that prioritize usability, accessibility, and visual excellence -- Apply Tailwind CSS with precision, leveraging the project's UIKit color system (text-fill, bg-material, etc.) and design tokens -- Integrate Radix UI primitives for robust, accessible component foundations -- Implement smooth, purposeful animations using Framer Motion that enhance rather than distract from the user experience -- Ensure all UI adheres to Apple HIG principles: clarity, deference, and depth -- Create responsive designs that work seamlessly across desktop, tablet, and mobile viewports - -**Design Philosophy:** - -- Follow the project's established aesthetic: clean, modern, minimal design with subtle shadows, rounded corners, and excellent typography -- Prioritize user experience over visual complexity -- Implement progressive disclosure and intuitive information hierarchy -- Use consistent spacing, typography scales, and color relationships -- Design for accessibility first, ensuring proper contrast ratios, keyboard navigation, and screen reader compatibility - -**Technical Standards:** - -- Write semantic, well-structured React components with TypeScript interfaces -- Use proper Tailwind class organization and avoid arbitrary values unless absolutely necessary -- Implement Radix UI components correctly with proper ARIA attributes and keyboard interactions -- Create smooth, performance-optimized animations that respect user preferences (prefers-reduced-motion) -- Follow the project's import conventions and component organization patterns -- Ensure components are reusable, composable, and maintainable - -**Quality Assurance:** - -- Test components across different screen sizes and devices -- Validate accessibility using proper semantic HTML and ARIA attributes -- Ensure color contrast meets WCAG guidelines -- Verify animations perform smoothly and don't cause layout shifts -- Check that components integrate properly with the existing design system - -**Context Awareness:** - -- Always consider the component's role within the broader application architecture -- Maintain consistency with existing UI patterns and component APIs -- Respect the project's UIKit color system and design tokens -- Consider the user's workflow and how the component fits into their journey - -When implementing UI components, provide clear rationale for design decisions, explain accessibility considerations, and suggest improvements to enhance the overall user experience. Always prioritize user needs while maintaining technical excellence and design consistency. diff --git a/.github/actions/setup-xcode/action.yml b/.github/actions/setup-xcode/action.yml index 85dc955c1..7bc7e9c0c 100644 --- a/.github/actions/setup-xcode/action.yml +++ b/.github/actions/setup-xcode/action.yml @@ -4,7 +4,7 @@ inputs: xcode-version: description: "Xcode version to use" required: false - default: "26.0.0" + default: "26.0.1" runs: using: "composite" diff --git a/.github/workflows/build-android.yml b/.github/workflows/build-android.yml index 144357076..7940d142c 100644 --- a/.github/workflows/build-android.yml +++ b/.github/workflows/build-android.yml @@ -39,7 +39,7 @@ jobs: uses: pnpm/action-setup@v4 - name: 🏗 Setup Node.js - uses: actions/setup-node@v5 + uses: actions/setup-node@v6 with: node-version: 22 cache: "pnpm" diff --git a/.github/workflows/build-desktop.yml b/.github/workflows/build-desktop.yml index cbdabd73d..8d3886900 100644 --- a/.github/workflows/build-desktop.yml +++ b/.github/workflows/build-desktop.yml @@ -72,7 +72,7 @@ jobs: uses: pnpm/action-setup@v4 - name: Use Node.js - uses: actions/setup-node@v5 + uses: actions/setup-node@v6 with: node-version: 22 cache: "pnpm" diff --git a/.github/workflows/build-ios-development.yml b/.github/workflows/build-ios-development.yml index ef9bbb329..565762f26 100644 --- a/.github/workflows/build-ios-development.yml +++ b/.github/workflows/build-ios-development.yml @@ -87,7 +87,7 @@ jobs: uses: pnpm/action-setup@v4 - name: 🏗 Setup Node.js - uses: actions/setup-node@v5 + uses: actions/setup-node@v6 with: node-version: 22 cache: "pnpm" @@ -131,7 +131,7 @@ jobs: uses: pnpm/action-setup@v4 - name: 🏗 Setup Node.js - uses: actions/setup-node@v5 + uses: actions/setup-node@v6 with: node-version: 22 cache: "pnpm" diff --git a/.github/workflows/build-ios.yml b/.github/workflows/build-ios.yml index c0912457e..7f4d70367 100644 --- a/.github/workflows/build-ios.yml +++ b/.github/workflows/build-ios.yml @@ -99,7 +99,7 @@ jobs: uses: pnpm/action-setup@v4 - name: 🏗 Setup Node.js - uses: actions/setup-node@v5 + uses: actions/setup-node@v6 with: node-version: 22 cache: "pnpm" diff --git a/.github/workflows/build-web.yml b/.github/workflows/build-web.yml index 8ca65f6b7..1b61f8d94 100644 --- a/.github/workflows/build-web.yml +++ b/.github/workflows/build-web.yml @@ -34,7 +34,7 @@ jobs: - uses: pnpm/action-setup@v4 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v5 + uses: actions/setup-node@v6 with: node-version: ${{ matrix.node-version }} cache: "pnpm" diff --git a/.github/workflows/claude-code-review.yml b/.github/workflows/claude-code-review.yml deleted file mode 100644 index 238062893..000000000 --- a/.github/workflows/claude-code-review.yml +++ /dev/null @@ -1,38 +0,0 @@ -name: 🤖 Claude PR Assistant - -on: - issue_comment: - types: [created] - pull_request_review_comment: - types: [created] - issues: - types: [opened, assigned] - pull_request_review: - types: [submitted] - -jobs: - claude-code-action: - if: | - (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) || - (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) || - (github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) || - (github.event_name == 'issues' && contains(github.event.issue.body, '@claude')) - runs-on: ubuntu-latest - permissions: - contents: read - pull-requests: read - issues: read - id-token: write - steps: - - name: Checkout repository - uses: actions/checkout@v5 - with: - fetch-depth: 1 - - - name: Run Claude PR Action - uses: anthropics/claude-code-action@beta - with: - # anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} - # Or use OAuth token instead: - claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} - timeout_minutes: "60" diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml deleted file mode 100644 index 01f530f36..000000000 --- a/.github/workflows/claude.yml +++ /dev/null @@ -1,63 +0,0 @@ -name: 🧠 Claude Code - -on: - issue_comment: - types: [created] - pull_request_review_comment: - types: [created] - issues: - types: [opened, assigned] - pull_request_review: - types: [submitted] - -jobs: - claude: - if: | - (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) || - (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) || - (github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) || - (github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude'))) - runs-on: ubuntu-latest - permissions: - contents: read - pull-requests: read - issues: read - id-token: write - actions: read # Required for Claude to read CI results on PRs - steps: - - name: Checkout repository - uses: actions/checkout@v5 - with: - fetch-depth: 1 - - - name: Run Claude Code - id: claude - uses: anthropics/claude-code-action@beta - with: - claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} - - # This is an optional setting that allows Claude to read CI results on PRs - additional_permissions: | - actions: read - - # Optional: Specify model (defaults to Claude Sonnet 4, uncomment for Claude Opus 4) - # model: "claude-opus-4-20250514" - - # Optional: Customize the trigger phrase (default: @claude) - # trigger_phrase: "/claude" - - # Optional: Trigger when specific user is assigned to an issue - # assignee_trigger: "claude-bot" - - # Optional: Allow Claude to run specific commands - # allowed_tools: "Bash(npm install),Bash(npm run build),Bash(npm run test:*),Bash(npm run lint:*)" - - # Optional: Add custom instructions for Claude to customize its behavior for your project - # custom_instructions: | - # Follow our coding standards - # Ensure all new code has tests - # Use TypeScript for new files - - # Optional: Custom environment variables for Claude - # claude_env: | - # NODE_ENV: test diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 568acb31b..20ed03eea 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -39,7 +39,7 @@ jobs: - uses: pnpm/action-setup@v4 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v5 + uses: actions/setup-node@v6 with: node-version: ${{ matrix.node-version }} cache: "pnpm" diff --git a/.github/workflows/tag.yml b/.github/workflows/tag.yml index 000e3a6cf..99a62cc22 100644 --- a/.github/workflows/tag.yml +++ b/.github/workflows/tag.yml @@ -19,7 +19,7 @@ jobs: uses: actions/checkout@v5 - name: Setup Node.js - uses: actions/setup-node@v5 + uses: actions/setup-node@v6 with: node-version: lts/* diff --git a/PRPs/adaptive-entry-content-layouts.md b/PRPs/adaptive-entry-content-layouts.md deleted file mode 100644 index da04e447b..000000000 --- a/PRPs/adaptive-entry-content-layouts.md +++ /dev/null @@ -1,384 +0,0 @@ -# PRP: Adaptive Entry Content Layouts for Different Media Types - -## Overview - -Transform the entry content display system from a fixed, article-centric layout to an adaptive system that renders different layouts optimized for specific media types (Social Media, Pictures, Videos, Articles). This will provide media-appropriate viewing experiences that match popular platform conventions while leveraging existing component patterns from the entry list items. - -## Current Implementation Analysis - -### Current Architecture - -- **Fixed Entry Content Layout**: `EntryContent.tsx` uses article-centric structure: Header → Title → AI Summary → Content Body → Attachments → Support Creator -- **Entry List Differentiation**: Entry list items already have adaptive layouts via `getItemComponentByView()` mapping -- **View-Type System**: `FeedViewType` enum defines Articles, SocialMedia, Pictures, Videos, Audios, Notifications -- **Template System**: GridItemTemplate and ListItemTemplate provide consistent patterns for different view types - -### Current Entry Content Structure - -**File**: `apps/desktop/layer/renderer/src/modules/entry-content/components/entry-content/EntryContent.tsx` - -```typescript -// Current fixed structure for all entry types -
- - // Prominent title display - // AI-generated summary - // Main content - // File attachments - // Creator support section -
-``` - -### Existing Adaptive Patterns in Entry List Items - -#### Social Media Item Pattern (`social-media-item.tsx`) - -- **Layout**: Avatar left, content right with author name → content → media gallery -- **Content Handling**: Collapsible content with "show more", minimal title prominence -- **Media**: Adaptive gallery (horizontal vs grid based on aspect ratios) - -#### Picture Item Pattern (`picture-item.tsx`) - -- **Layout**: Image carousel with footer metadata -- **Media**: SwipeMedia component for multiple images, masonry layout support -- **Content**: Author info and metadata in footer - -#### Video Item Pattern (`video-item.tsx`) - -- **Layout**: Video player/thumbnail → author/title → description -- **Media**: Preview on hover, modal video player, duration overlay -- **Content**: Title and description below video - -### Current Limitations - -1. **EntryContent ignores view type**: All entries use identical article layout regardless of `feedViewType` -2. **Poor UX for non-article content**: Social media posts show unnecessary titles, videos buried in text -3. **Unused adaptive patterns**: Entry list items have perfect media layouts that aren't leveraged in content view -4. **Inconsistent experience**: Different UX between entry list (adaptive) and entry content (fixed) - -## Proposed Solution - -### Architecture Changes - -Create an adaptive entry content system that selects appropriate layout components based on `FeedViewType`, leveraging existing patterns from entry list items while optimizing for full content display. - -### Component Factory Pattern - -Use factory pattern to dynamically select layout components: - -```typescript -const EntryContentLayoutFactory = { - [FeedViewType.Articles]: ArticleLayout, - [FeedViewType.SocialMedia]: SocialMediaLayout, - [FeedViewType.Pictures]: PicturesLayout, - [FeedViewType.Videos]: VideosLayout, - [FeedViewType.Audios]: AudioLayout, - [FeedViewType.Notifications]: ArticleLayout, // fallback -} -``` - -### Layout-Specific Components - -#### 1. SocialMediaLayout - -**Pattern**: Based on `social-media-item.tsx` but optimized for full content view - -```typescript -// Layout: Avatar + Author Info + Content + Media Gallery -
- {/* Author avatar */} -
- {/* Author name, handle, timestamp */} - {/* Social media content */} - {/* Adaptive image/video gallery */} - {/* AI summary if available */} -
-
-``` - -#### 2. PicturesLayout - -**Pattern**: Based on `picture-item.tsx` and `PreviewMediaContent.tsx` - -```typescript -// Layout: Image Carousel + Sidebar with metadata -
-
{/* Image area */} - -
-
{/* Sidebar */} - - - - -
-
-``` - -#### 3. VideosLayout - -**Pattern**: Based on `video-item.tsx` but full-sized - -```typescript -// Layout: Video Player + Title/Description below -
-
{/* Video area */} - -
-
{/* Content area */} - - - - -
-
-``` - -#### 4. ArticleLayout (Current) - -**Pattern**: Keep existing layout for articles - -```typescript -// Current article structure preserved - - - - - - -``` - -## Implementation Tasks - -### Phase 1: Core Architecture Setup - -#### 1. Create Layout Factory System - -**File**: `apps/desktop/layer/renderer/src/modules/entry-content/components/layouts/factory.ts` - -```typescript -export const getEntryContentLayout = (viewType: FeedViewType) => { - return EntryContentLayoutFactory[viewType] || ArticleLayout -} -``` - -#### 2. Create Base Layout Components - -**Files**: - -- `apps/desktop/layer/renderer/src/modules/entry-content/components/layouts/SocialMediaLayout.tsx` -- `apps/desktop/layer/renderer/src/modules/entry-content/components/layouts/PicturesLayout.tsx` -- `apps/desktop/layer/renderer/src/modules/entry-content/components/layouts/VideosLayout.tsx` -- `apps/desktop/layer/renderer/src/modules/entry-content/components/layouts/ArticleLayout.tsx` - -#### 3. Update EntryContent Component - -**File**: `apps/desktop/layer/renderer/src/modules/entry-content/components/entry-content/EntryContent.tsx` - -```typescript -// Replace fixed layout with factory-based selection -const LayoutComponent = getEntryContentLayout(entry.feedViewType) -return -``` - -### Phase 2: Layout Implementation - -#### 4. Implement SocialMediaLayout - -- Extract author display logic from `social-media-item.tsx` -- Implement adaptive media gallery -- Handle collapsible content with proper full-view sizing -- Reference: Twitter-like layout patterns - -#### 5. Implement PicturesLayout - -- Integrate `SwipeMedia` component for image carousel -- Create sidebar layout for metadata -- Handle single vs multiple images -- Reference: Instagram-like layout patterns - -#### 6. Implement VideosLayout - -- Integrate video player functionality from `video-item.tsx` -- Add video controls and duration display -- Layout title/description below video -- Reference: YouTube-like layout patterns - -#### 7. Extract ArticleLayout - -- Move current `EntryContent` structure to dedicated `ArticleLayout` -- Ensure no regression in article viewing experience - -### Phase 3: Shared Components - -#### 8. Create Shared UI Components - -- `AuthorHeader`: Consistent author display (avatar, name, timestamp) -- `ContentBody`: Adaptive content rendering with markdown support -- `MediaGallery`: Reusable media gallery component -- `VideoPlayer`: Consistent video player wrapper - -#### 9. Update Entry Context Provider - -- Ensure entry context includes `feedViewType` information -- Add layout-specific metadata handling - -### Phase 4: Testing & Polish - -#### 10. Responsive Design Implementation - -- Mobile layouts for each media type -- Tablet adaptations -- Handle edge cases (missing media, long content) - -#### 11. Animation & Transitions - -- Smooth layout transitions when switching between entries -- Media loading states and placeholders -- Maintain existing animation patterns - -## Context & References - -### Existing Codebase Patterns - -- **Entry List Items**: `apps/desktop/layer/renderer/src/modules/entry-column/Items/` - Perfect reference patterns -- **Media Components**: `SwipeMedia`, `PreviewMediaContent`, `Media` - Reusable media handling -- **Template System**: `GridItemTemplate` - Consistent footer patterns -- **View Type System**: `getItemComponentByView()` - Proven factory pattern implementation - -### External Documentation & Best Practices - -- **Factory Pattern**: https://dev.to/moayad523/stop-using-conditional-rendering-leveraging-the-factory-pattern-for-dynamic-component-creation-740 -- **React Conditional Rendering**: https://react.dev/learn/conditional-rendering -- **Switch Pattern**: https://dev.to/musatov/conditional-rendering-in-react-with-a-switch-component-23ph -- **Social Media Layouts**: https://github.com/thisisadityarao/react-social-media-cards -- **Instagram Layout Patterns**: https://github.com/natividadesusana/instagram-layout-react -- **Adaptive Design**: https://www.interaction-design.org/literature/topics/adaptive-design -- **React Social Media Embed**: https://www.npmjs.com/package/react-social-media-embed - -### UI Reference Platforms - -- **Social Media**: Twitter's post layout with avatar, author info, and content -- **Pictures**: Instagram's image carousel with sidebar metadata -- **Videos**: YouTube's video player with title/description below -- **Articles**: Current Follow article layout (preserve existing UX) - -## Validation Gates - -### Code Quality - -```bash -# TypeScript validation -pnpm run typecheck - -# Linting -pnpm run lint:tsl -pnpm run lint - -# Format validation -pnpm run format - -# Build validation -pnpm run build:web -``` - -### Functional Testing - -```bash -# Manual testing checklist -1. Social Media entries → Avatar left, content right, proper media gallery -2. Picture entries → Image carousel + sidebar metadata, multiple image handling -3. Video entries → Video player + title/description below, controls work -4. Article entries → No regression, identical to current layout -5. Mixed feed → Layout switches appropriately between entry types -6. Mobile responsive → All layouts adapt properly to mobile screens -7. Media loading → Proper loading states and error handling -``` - -### Performance Validation - -- Bundle size impact analysis (should be minimal due to code splitting) -- Layout shift measurement (should be zero) -- Media loading performance maintained -- Memory usage with multiple media types - -## Success Criteria - -- [ ] Social media entries display with Twitter-like layout (avatar + author + content) -- [ ] Picture entries display with Instagram-like layout (carousel + sidebar) -- [ ] Video entries display with YouTube-like layout (player + title below) -- [ ] Article entries maintain identical current layout -- [ ] Factory pattern correctly selects layout based on `FeedViewType` -- [ ] All existing functionality preserved (AI summary, attachments, etc.) -- [ ] Mobile responsive design works across all layouts -- [ ] No performance regression in entry switching -- [ ] No bundle size increase over 5% -- [ ] All media loading states properly handled - -## Risk Mitigation - -### Potential Issues - -1. **Layout Complexity**: Different layouts might conflict with existing entry content container sizing -2. **Media Loading**: Complex media components might impact performance -3. **Mobile Responsiveness**: Desktop-optimized layouts might break on mobile -4. **Bundle Size**: Adding multiple layout components could increase bundle size -5. **Component Dependencies**: Shared components might have circular dependencies - -### Mitigation Strategies - -1. **Container Isolation**: Use CSS containment and absolute positioning where needed -2. **Lazy Loading**: Implement code splitting for layout components -3. **Mobile-First**: Design mobile layouts first, then enhance for desktop -4. **Bundle Analysis**: Use webpack-bundle-analyzer to monitor size impact -5. **Dependency Management**: Clear component hierarchy and shared utilities in separate files - -### Rollback Strategy - -- Feature flag implementation to toggle between old and new layouts -- Gradual rollout by `FeedViewType` (start with least-used types) -- Database flag to disable adaptive layouts per user preference - -## Additional Considerations - -### Accessibility - -- Ensure all layouts maintain proper heading hierarchy -- Video players must support keyboard navigation -- Image carousels need proper ARIA labels -- Screen reader compatibility across all layouts - -### i18n Support - -- All new text elements properly localized -- Layout adjustments for RTL languages -- Consistent translation keys across layouts - -### Future Extensibility - -- Audio layout placeholder implementation -- Plugin system for custom layout types -- Theme system integration for layout variants - -## Confidence Score: 9/10 - -This PRP provides a comprehensive implementation approach with: - -- ✅ **Detailed codebase analysis**: Complete understanding of existing patterns and architecture -- ✅ **Proven patterns**: Leveraging successful entry list item layouts -- ✅ **Factory pattern**: Clean, extensible architecture for layout selection -- ✅ **External research**: Best practices from social media layout implementations -- ✅ **Existing components**: Reusing proven media handling components -- ✅ **Clear implementation path**: Phased approach with specific file references -- ✅ **Risk mitigation**: Identified potential issues with concrete solutions -- ✅ **Executable validation**: Specific testing steps and success criteria - -High confidence is justified because: - -1. **Existing foundation**: Entry list items already demonstrate successful adaptive patterns -2. **Component reuse**: Media components (`SwipeMedia`, `PreviewMediaContent`) are proven -3. **Clear architecture**: Factory pattern is well-established in the codebase -4. **External validation**: Research confirms best practices align with proposed approach -5. **Minimal breaking changes**: Articles maintain existing layout, reducing regression risk - -The main implementation involves connecting existing successful patterns rather than building entirely new systems, significantly reducing complexity and risk. diff --git a/PRPs/adaptive-entry-layouts-fixes.md b/PRPs/adaptive-entry-layouts-fixes.md deleted file mode 100644 index 5efef6b4c..000000000 --- a/PRPs/adaptive-entry-layouts-fixes.md +++ /dev/null @@ -1,530 +0,0 @@ -# PRP: Fix Adaptive Entry Content Layout Issues - -## Overview - -Fix specific layout and functionality issues in the existing adaptive entry content layouts implemented in `PRPs/adaptive-entry-content-layouts.md`. The adaptive layouts are working but have implementation issues that affect user experience across Social Media, Pictures, and Videos layouts. - -## Current Issues Analysis - -### Current Implementation Status - -The adaptive layout factory system is successfully implemented: - -- **Factory System**: `getEntryContentLayout()` in `layouts/factory.ts` correctly routes to layout-specific components -- **Layout Components**: SocialMediaLayout, PicturesLayout, VideosLayout, ArticleLayout are created -- **Integration**: `AdaptiveContentRenderer` in `EntryContent.tsx` properly uses the factory pattern - -### Specific Issues Identified - -#### 1. Social Media Layout Issues - -**File**: `apps/desktop/layer/renderer/src/modules/entry-content/components/layouts/SocialMediaLayout.tsx` - -**Problem 1**: Duplicate Author Display - -- Lines 47-54: Shows `AuthorHeader` with avatar (48px size) -- Lines 58-62: Shows another `AuthorHeader` without avatar -- **Result**: Author name appears twice in the layout - -**Problem 2**: Show More Button Logic Still Present - -- Lines 40-42: Uses `autoExpandLongSocialMedia` setting to conditionally show `CollapsedSocialMediaItem` -- Lines 96-143: `CollapsedSocialMediaItem` implements "show more" button with LRU cache -- **User Request**: Remove this logic entirely and force full display - -**Problem 3**: AI Summary Position - -- Line 87: `` is at the bottom of the content -- **User Request**: Move AI summary to the top of the layout - -#### 2. Pictures Layout Issues - -**File**: `apps/desktop/layer/renderer/src/modules/entry-content/components/layouts/PicturesLayout.tsx` - -**Problem**: Mixed Media and Text Content - -- Current implementation uses `SwipeMedia` for carousel but doesn't follow "PreviewMediaContent logic" -- Text content mixed with multimedia content -- **User Request**: Separate multimedia content (using PreviewMediaContent) from text content - -#### 3. Videos Layout Issues - -**File**: `apps/desktop/layer/renderer/src/modules/entry-content/components/layouts/VideosLayout.tsx` - -**Problem 1**: VideoPlayer Using Mini Player - -- VideoPlayer component (`shared/VideoPlayer.tsx` lines 56-71) creates both `miniIframeSrc` and `iframeSrc` -- Lines 129-133: Uses `miniIframeSrc` for preview/hover (optimized for small sizes) -- **User Request**: Use `iframeSrc` directly for better large-screen experience - -**Problem 2**: Content Duplication - -- `ContentBody` component (lines 61-66) renders with `noMedia={false}` by default -- This includes images/iframes in text content that duplicate what's shown in video player -- **User Request**: Remove multimedia elements from text content, show only text - -## Proposed Solutions - -### 1. Social Media Layout Fixes - -#### 1.1 Fix Duplicate Author Display - -Remove the separate avatar display and use single AuthorHeader with proper positioning: - -```typescript -// Current problematic structure (lines 45-62): -
- {/* Avatar */} -
- -
- {/* Content */} -
- - // ... rest of content -
-
- -// Fixed structure - single author header: -
- -
- - // ... content follows -
-
-``` - -#### 1.2 Remove Show More Logic - -Replace conditional wrapper with direct content display: - -```typescript -// Remove lines 40-42 autoExpandLongSocialMedia logic -// Remove lines 96-143 CollapsedSocialMediaItem component entirely - -// Replace EntryContentWrapper usage (lines 66-73) with direct ContentBody: - -``` - -#### 1.3 Move AI Summary to Top - -Reorder components to show AI Summary after author header: - -```typescript -
- - {/* Move to top */} - - -
-``` - -### 2. Pictures Layout Fixes - -#### 2.1 Implement PreviewMediaContent Pattern - -Replace current SwipeMedia carousel with PreviewMediaContent for better multimedia handling: - -```typescript -// Current implementation uses SwipeMedia (lines 52-61) -// Replace with PreviewMediaContent pattern: - -import { usePreviewMedia } from "~/components/ui/media/hooks" - -const PicturesLayout: React.FC = ({ entryId, ... }) => { - const entry = useEntry(entryId, (state) => ({ media: state.media, content: state.content })) - - const textContent = useMemo(() => ( -
- {/* Text only */} - -
- ), [entryId, ...]) - - const previewMedia = usePreviewMedia(textContent) - - return ( -
-
- {entryMedia.length > 0 ? ( - - ) : ( -
No media content
- )} -
- -
-
- -
-
- - {textContent} {/* Separated text content */} -
-
-
- ) -} -``` - -### 3. Videos Layout Fixes - -#### 3.1 Use Full-Size iframe Instead of Mini Player - -Modify VideoPlayer to use `iframeSrc` directly for large-screen optimization: - -```typescript -// In VideosLayout.tsx, pass preferFullSize prop to VideoPlayer: - - -// In shared/VideoPlayer.tsx, modify to respect preferFullSize: -export const VideoPlayer: React.FC = ({ - preferFullSize = false, - ...props -}) => { - const displaySrc = preferFullSize ? iframeSrc : miniIframeSrc - - return ( -
- {displaySrc ? ( - - ) : ( - // Fallback to media preview - )} -
- ) -} -``` - -#### 3.2 Filter Media from Text Content - -Create text-only version of ContentBody by adding `noMedia` prop: - -```typescript -// In VideosLayout.tsx (lines 61-66), add noMedia={true}: - - -// ContentBody.tsx already supports noMedia prop (line 49) -// This will filter out multimedia elements from HTML content -``` - -## Implementation Tasks - -### Phase 1: Social Media Layout Fixes - -#### Task 1.1: Fix Duplicate Author Headers - -**File**: `SocialMediaLayout.tsx` - -- Remove duplicate AuthorHeader components (lines 47-54 and 58-62) -- Use single FeedIcon + AuthorHeader pattern from original social-media-item.tsx -- Maintain proper spacing and styling - -#### Task 1.2: Remove Show More Logic - -**File**: `SocialMediaLayout.tsx` - -- Remove `autoExpandLongSocialMedia` usage (lines 40-42) -- Delete `CollapsedSocialMediaItem` component entirely (lines 96-143) -- Replace `EntryContentWrapper` with direct `ContentBody` usage - -#### Task 1.3: Reorder AI Summary Position - -**File**: `SocialMediaLayout.tsx` - -- Move `` from line 87 to after AuthorHeader -- Add appropriate spacing and styling for top position - -### Phase 2: Pictures Layout Enhancement - -#### Task 2.1: Implement PreviewMediaContent Integration - -**File**: `PicturesLayout.tsx` - -- Import and use `usePreviewMedia` hook -- Replace SwipeMedia carousel with clickable preview that opens PreviewMediaContent modal -- Separate multimedia and text content rendering - -### Phase 3: Videos Layout Enhancement - -#### Task 3.1: Update VideoPlayer for Large Screen - -**File**: `shared/VideoPlayer.tsx` - -- Add `preferFullSize` prop to use `iframeSrc` instead of `miniIframeSrc` -- Implement proper responsive design for large screens - -#### Task 3.2: Filter Text Content - -**File**: `VideosLayout.tsx` - -- Update ContentBody usage to include `noMedia={true}` prop -- Ensure multimedia elements are filtered from text display - -### Phase 4: Testing and Validation - -#### Task 4.1: Layout Validation - -- Test each layout with various entry types -- Verify author information displays correctly -- Confirm media handling works as expected -- Test responsive behavior on different screen sizes - -#### Task 4.2: Integration Testing - -- Verify factory pattern still routes correctly -- Test layout switching between different entry types -- Confirm no regressions in ArticleLayout - -## Context & References - -### Existing Codebase Components - -#### Key Files to Modify - -- `SocialMediaLayout.tsx` - Main fixes for duplicate author and show more logic -- `PicturesLayout.tsx` - PreviewMediaContent integration -- `VideosLayout.tsx` - Full-size iframe and text filtering -- `shared/VideoPlayer.tsx` - preferFullSize prop support -- `shared/ContentBody.tsx` - Already supports noMedia prop - -#### Key Files to Reference - -- `social-media-item.tsx` - Original social media layout pattern (single author header) -- `PreviewMediaContent.tsx` - Modal-based media preview with sidebar support -- `AuthorHeader.tsx` - Shared author display component -- `MediaGallery.tsx` - Media display patterns - -### External Best Practices - -#### Responsive iframe Guidelines - -- **Modern CSS Approach**: Use `aspect-ratio: 16/9` for responsive video containers -- **Large Screen Optimization**: Implement max-width constraints to prevent oversized displays -- **Performance**: Use appropriate iframe sources based on screen size and context - -**Reference**: https://blog.logrocket.com/best-practices-react-iframes/ -**Reference**: https://cloudinary.com/guides/video-effects/responsive-video-embedding-embed-video-iframe-size-relative-to-screen-size - -#### HTML Content Filtering - -- **Text Extraction**: Use `noMedia` flags to filter multimedia elements from HTML content -- **DOM-based Filtering**: Leverage existing HTML processing to remove specific tags -- **Content Separation**: Maintain clear separation between multimedia and text content - -**Reference**: Server-side content filtering approaches for media removal -**Reference**: https://www.bookstack.cn/read/crawl4ai-0.4-en/dcca2e8c0a744b29.md - -### UI Reference Patterns - -#### Social Media Layout - -- **Single Author Header**: Follow Twitter-like pattern with avatar + name + handle + timestamp -- **Content Flow**: Author → AI Summary → Text Content → Media Gallery -- **No Collapse Logic**: Display full content without truncation in content view - -#### Pictures Layout - -- **Modal Preview**: Use PreviewMediaContent for sophisticated media viewing -- **Sidebar Metadata**: Article-like sidebar with text content separate from media -- **Click to Preview**: Simple click interaction to open full preview modal - -#### Videos Layout - -- **Full-Size Player**: Use appropriate iframe source for screen size -- **Text-Only Content**: Filter multimedia from description to avoid duplication -- **Standard Flow**: Video Player → Title → Author → Description (text only) → AI Summary - -## Validation Gates - -### Code Quality - -```bash -# TypeScript validation -pnpm run typecheck - -# Linting validation -pnpm run lint:tsl -pnpm run lint - -# Format validation -pnpm run format - -# Build validation -pnpm run build:web -``` - -### Functional Testing Checklist - -#### Social Media Layout - -- [ ] Author name appears only once (not duplicated) -- [ ] Author header shows avatar, name, handle (if Twitter), and timestamp -- [ ] AI summary appears at the top, below author header -- [ ] Content displays in full without "show more" button -- [ ] Media gallery displays correctly below content -- [ ] Layout matches original social-media-item.tsx patterns - -#### Pictures Layout - -- [ ] Multimedia content separated from text content -- [ ] Clicking images opens PreviewMediaContent modal -- [ ] Sidebar displays text content with proper overflow handling -- [ ] Author header displays in sidebar -- [ ] AI summary appears in sidebar text content area - -#### Videos Layout - -- [ ] Video player uses full-size iframe (not mini version) -- [ ] Video player displays properly at large screen sizes -- [ ] Text content below video contains no duplicate images/media -- [ ] Author information displays below video -- [ ] AI summary appears at bottom of text content - -### Performance Validation - -- [ ] No layout shift when switching between entry types -- [ ] Video iframe loads appropriately for screen size -- [ ] PreviewMediaContent modal opens smoothly -- [ ] Text content filtering doesn't impact render performance -- [ ] No memory leaks with modal-based media preview - -### Responsive Design - -- [ ] All layouts adapt properly to mobile screens -- [ ] Video player maintains aspect ratio across screen sizes -- [ ] Pictures sidebar stacks properly on narrow screens -- [ ] Social media layout remains readable on all devices - -## Success Criteria - -### Primary Objectives - -- [ ] **Social Media**: Single author header, AI summary at top, no show more button -- [ ] **Pictures**: PreviewMediaContent integration, separated multimedia/text content -- [ ] **Videos**: Full-size iframe player, text-only content descriptions -- [ ] **All Layouts**: Maintain existing functionality while fixing specific issues - -### Quality Standards - -- [ ] Zero regression in ArticleLayout functionality -- [ ] Factory pattern continues to work seamlessly -- [ ] All existing media handling features preserved -- [ ] Mobile responsive design maintained across layouts -- [ ] Performance impact minimal (under 5% bundle increase) - -### User Experience Goals - -- [ ] Cleaner social media content display without duplication -- [ ] Better large-screen video viewing experience -- [ ] Sophisticated image preview with PreviewMediaContent modal -- [ ] Consistent author information display patterns -- [ ] Improved content organization with AI summaries positioned logically - -## Risk Mitigation - -### Potential Issues - -#### Component Dependencies - -**Risk**: Changes to shared components (VideoPlayer, ContentBody) could affect other areas -**Mitigation**: - -- Add new props with default values to maintain backward compatibility -- Test all existing entry list item displays -- Use feature flags if needed for gradual rollout - -#### PreviewMediaContent Integration - -**Risk**: Modal-based media preview might conflict with existing modal systems -**Mitigation**: - -- Test modal stacking behavior thoroughly -- Ensure proper cleanup of modal state -- Verify keyboard navigation continues to work - -#### Performance Impact - -**Risk**: Additional modal components and media filtering might impact performance -**Mitigation**: - -- Implement lazy loading for PreviewMediaContent modal -- Use React.memo for expensive components -- Profile render performance before and after changes - -### Rollback Strategy - -- Maintain existing layout components as backup -- Use git feature branches for each layout fix -- Implement progressive enhancement approach -- Test each layout individually before combining changes - -## Additional Considerations - -### Accessibility - -- Ensure all interactive elements have proper ARIA labels -- Maintain keyboard navigation for new modal interactions -- Preserve screen reader compatibility for author headers -- Test video player controls with assistive technologies - -### i18n Support - -- All new text elements properly localized -- Layout adjustments tested with longer text languages -- RTL language support maintained -- Consistent translation keys across layout components - -### Future Extensibility - -- Pattern established for other media types (Audio layout) -- Plugin system compatibility maintained -- Theme system integration preserved -- Component reusability across different layout contexts - -## Confidence Score: 9/10 - -This PRP provides a comprehensive fix approach with: - -- ✅ **Specific Issue Analysis**: Detailed examination of exact problems and file locations -- ✅ **Targeted Solutions**: Precise fixes for each identified issue without over-engineering -- ✅ **Existing Pattern Leverage**: Uses proven components and patterns already in codebase -- ✅ **External Research**: Incorporates best practices for responsive video and content filtering -- ✅ **Implementation Roadmap**: Clear phase-based approach with specific file changes -- ✅ **Risk Management**: Identified potential issues with concrete mitigation strategies -- ✅ **Comprehensive Testing**: Detailed validation gates and success criteria -- ✅ **Backward Compatibility**: Maintains existing functionality while implementing fixes - -High confidence because: - -1. **Focused Scope**: Addresses specific reported issues without unnecessary system changes -2. **Proven Components**: Leverages existing PreviewMediaContent, AuthorHeader, and ContentBody components -3. **Pattern Following**: Mimics successful patterns from social-media-item.tsx and other working components -4. **External Validation**: Research confirms best practices for responsive video and content filtering -5. **Minimal Risk**: Changes are contained to specific layout files with clear rollback options - -The implementation primarily involves connecting existing working components in better ways rather than building new complex systems, significantly reducing implementation complexity and risk. diff --git a/PRPs/ai-summary-chat-integration.md b/PRPs/ai-summary-chat-integration.md deleted file mode 100644 index 43d10cc90..000000000 --- a/PRPs/ai-summary-chat-integration.md +++ /dev/null @@ -1,735 +0,0 @@ -# PRP: AI Summary Chat Integration for Enhanced User Engagement - -## Overview - -Transform the AI chat interface from a static welcome screen to a context-aware, engagement-focused experience by integrating AI summary as initial content. This enhancement automatically displays relevant entry summaries when users open the AI chat panel, providing zero-friction conversation starters and increasing AI feature adoption through progressive disclosure patterns. - -## Current Implementation Analysis - -### Current Chat Interface Architecture - -**File**: `apps/desktop/layer/renderer/src/modules/ai-chat/components/layouts/ChatInterface.tsx` - -**Current Flow**: - -```typescript -// Conditional rendering based on message state -{!hasMessages && !isLoadingHistory ? ( - -) : ( - - - {(status === "submitted" || status === "streaming") && } - -)} -``` - -**WelcomeScreen Structure** (`WelcomeScreen.tsx`): - -```typescript -// Current static welcome screen -
-
-
- // 3D AI model -

{APP_NAME} AI

// Title -

{t("welcome_description")}

// Description -
-
// Empty space -
- {enabledShortcuts.map(...)} // Quick action shortcuts - {DEFAULT_SHORTCUTS.map(...)} // Default suggestions -
-
-
-``` - -### Existing AI Summary System - -**Summary Store** (`packages/internal/store/src/modules/summary/store.ts`): - -- **Data Structure**: `Record>` -- **Generation**: `summarySyncService.generateSummary()` with API integration -- **Hooks**: `useSummary()`, `usePrefetchSummary()`, `useSummaryStatus()` -- **Caching**: LRU cleanup with `lastAccessed` tracking - -**Visual AISummary Component** (`apps/desktop/layer/renderer/src/modules/entry-content/AISummary.tsx`): - -```typescript -// Beautiful glass-morphism design with animations -
-
-
-
- -
- - {t("entry_content.ai_summary")} - -
- {summary.data && } -
- - {summary.isLoading ? : {summary.data}} - -
-``` - -### Entry Context Integration - -**Already Implemented** (`EntryContent.ai.tsx` lines 89-98): - -```typescript -const { addOrUpdateBlock, removeBlock } = useBlockActions() -useEffect(() => { - addOrUpdateBlock({ - id: BlockSliceAction.SPECIAL_TYPES.mainEntry, - type: "mainEntry", - value: entryId, - }) - return () => { - removeBlock(BlockSliceAction.SPECIAL_TYPES.mainEntry) - } -}, [addOrUpdateBlock, entryId, removeBlock]) -``` - -**Entry Context Retrieval** (`EntryPickers.tsx` lines 15-18): - -```typescript -const mainEntryId = useAIChatStore()((s) => { - const block = s.blocks.find((b) => b.type === "mainEntry") - return block && block.type === "mainEntry" ? block.value : undefined -}) -``` - -### Current Limitations - -1. **Zero Context Awareness**: Welcome screen ignores available entry context and summary data -2. **Missed Engagement Opportunity**: Users must initiate conversation without any contextual prompts -3. **Disconnected Experience**: Rich summary data exists but isn't leveraged in chat interface -4. **Static Quick Actions**: Generic shortcuts instead of context-aware conversation starters -5. **Poor Progressive Disclosure**: No smooth transition from passive summary viewing to active conversation - -## Proposed Solution - -### Architecture Overview - -Create a context-aware chat interface that intelligently displays entry summaries as conversation starters while maintaining the existing welcome screen for non-entry contexts. The solution leverages existing summary infrastructure and visual patterns while introducing smart progressive enhancement. - -### Component Architecture - -```typescript -// Enhanced WelcomeScreen with context awareness -const WelcomeScreen = ({ onSend }: WelcomeScreenProps) => { - const mainEntryId = useMainEntryId() - const hasEntryContext = !!mainEntryId - - return ( -
- {hasEntryContext ? ( - - ) : ( - - )} -
- ) -} -``` - -### Core Components - -#### 1. EntrySummaryCard Component - -**File**: `apps/desktop/layer/renderer/src/modules/ai-chat/components/welcome/EntrySummaryCard.tsx` - -**Purpose**: Display entry summary with chat-optimized styling and smart actions - -```typescript -interface EntrySummaryCardProps { - entryId: string - onSend: (message: string) => void - className?: string -} - -const EntrySummaryCard: React.FC = ({ entryId, onSend, className }) => { - const actionLanguage = useActionLanguage() - const isInReadabilitySuccess = useEntryIsInReadabilitySuccess(entryId) - const summary = usePrefetchSummary({ - entryId, - target: isInReadabilitySuccess ? "readabilityContent" : "content", - actionLanguage, - enabled: true, - }) - - const quickActions = useSmartQuickActions(summary.data, entryId) - - return ( - - {/* Header with entry context */} -
-
-
- -
-
-
-

AI Summary

-

Ready to discuss this entry

-
-
- -
- - {/* Summary Content */} - - {summary.isLoading ? ( - - ) : summary.data ? ( -
- - {String(summary.data)} - -
- ) : ( -
- -

Summary not available

-
- )} -
- - {/* Smart Quick Actions */} - {summary.data && ( -
-
- Ask about this entry -
-
- {quickActions.map((action, index) => ( - onSend(action.prompt)} - className={cn( - "inline-flex items-center gap-2 px-3 py-2 rounded-lg text-sm font-medium transition-all", - "bg-material-medium hover:bg-material-thick", - "border border-border/50 hover:border-border", - "text-text-secondary hover:text-text", - "hover:shadow-sm active:scale-95" - )} - > - - {action.label} - - ))} -
-
- )} - - ) -} -``` - -#### 2. Smart Quick Actions Generator - -**File**: `apps/desktop/layer/renderer/src/modules/ai-chat/hooks/useSmartQuickActions.ts` - -**Purpose**: Generate contextual conversation starters based on summary content and entry metadata - -```typescript -interface QuickAction { - id: string - label: string - prompt: string - icon: string - priority: number -} - -export const useSmartQuickActions = ( - summaryData: string | null, - entryId: string, -): QuickAction[] => { - const entry = useEntry(entryId) - const { t } = useTranslation("ai") - - return useMemo(() => { - if (!summaryData || !entry) return DEFAULT_ENTRY_ACTIONS - - const actions: QuickAction[] = [] - - // Content-based actions - if (summaryData.length > 500) { - actions.push({ - id: "simplify", - label: t("quick_actions.simplify"), - prompt: `Can you simplify this summary in 2-3 sentences? Focus on the main points.`, - icon: "i-mgc-edit-cute-re", - priority: 1, - }) - } - - // Entry type-based actions - if (entry.url) { - actions.push({ - id: "discuss", - label: t("quick_actions.discuss"), - prompt: `What are the key insights from this article? What should I know?`, - icon: "i-mgc-chat-cute-re", - priority: 2, - }) - } - - // Always available actions - actions.push( - { - id: "questions", - label: t("quick_actions.questions"), - prompt: `What questions should I be asking about this content?`, - icon: "i-mgc-question-cute-re", - priority: 3, - }, - { - id: "takeaways", - label: t("quick_actions.takeaways"), - prompt: `What are the most important takeaways from this entry?`, - icon: "i-mgc-star-cute-re", - priority: 4, - }, - ) - - return actions.sort((a, b) => a.priority - b.priority).slice(0, 4) - }, [summaryData, entry, t]) -} - -const DEFAULT_ENTRY_ACTIONS: QuickAction[] = [ - { - id: "analyze", - label: "Analyze this entry", - prompt: "Can you analyze this entry and tell me what it's about?", - icon: "i-mgc-search-cute-re", - priority: 1, - }, - { - id: "explain", - label: "Explain key points", - prompt: "What are the key points I should understand from this entry?", - icon: "i-mgc-lightbulb-cute-re", - priority: 2, - }, -] -``` - -#### 3. Enhanced WelcomeScreen with Context Awareness - -**File**: `apps/desktop/layer/renderer/src/modules/ai-chat/components/layouts/WelcomeScreen.tsx` (Modified) - -```typescript -export const WelcomeScreen = ({ onSend }: WelcomeScreenProps) => { - const { t } = useTranslation("ai") - const aiSettings = useAISettingValue() - const mainEntryId = useMainEntryId() - const settingModalPresent = useSettingModal() - - const hasEntryContext = !!mainEntryId - const hasCustomPrompt = aiSettings.personalizePrompt?.trim() - const enabledShortcuts = aiSettings.shortcuts?.filter((shortcut) => shortcut.enabled) || [] - - return ( -
-
- {/* Header Section - Always Present */} - -
- -
-
-

{APP_NAME} AI

-

- {hasEntryContext ? t("welcome_description_contextual") : t("welcome_description")} -

-
-
- - {/* Dynamic Content Area */} -
- - {hasEntryContext ? ( - - ) : ( - - )} - -
- - {/* Personal Prompt - Always at Bottom if Present */} - {hasCustomPrompt && ( - settingModalPresent("ai")} - /> - )} -
-
- ) -} -``` - -### Data Flow Integration - -#### Summary Prefetching Strategy - -```typescript -// In ChatInterface.tsx - Prefetch summary when entry context available -const ChatInterfaceContent = () => { - const hasMessages = useHasMessages() - const mainEntryId = useMainEntryId() - const actionLanguage = useActionLanguage() - - // Prefetch summary for context-aware welcome screen - usePrefetchSummary({ - entryId: mainEntryId || "", - target: "content", // Start with content, fallback to readability if needed - actionLanguage, - enabled: !!mainEntryId && !hasMessages, // Only when showing welcome screen - }) - - // ... rest of component -} -``` - -#### State Management Integration - -```typescript -// Custom hook for main entry context -export const useMainEntryId = (): string | undefined => { - return useAIChatStore()((state) => { - const block = state.blocks.find((b) => b.type === "mainEntry") - return block && block.type === "mainEntry" ? block.value : undefined - }) -} - -// Hook for entry summary in chat context -export const useEntrySummaryForChat = (entryId: string) => { - const actionLanguage = useActionLanguage() - const isInReadabilitySuccess = useEntryIsInReadabilitySuccess(entryId) - - return usePrefetchSummary({ - entryId, - target: isInReadabilitySuccess ? "readabilityContent" : "content", - actionLanguage, - enabled: !!entryId, - staleTime: 1000 * 60 * 60, // 1 hour - summaries don't change often - }) -} -``` - -### Animation and Visual Design - -#### Micro-interactions and Transitions - -```typescript -// Smooth transitions between welcome states -const welcomeVariants = { - entryContext: { - initial: { opacity: 0, y: 30, scale: 0.95 }, - animate: { opacity: 1, y: 0, scale: 1 }, - exit: { opacity: 0, y: -30, scale: 0.95 }, - }, - defaultWelcome: { - initial: { opacity: 0, y: 20, scale: 0.98 }, - animate: { opacity: 1, y: 0, scale: 1 }, - exit: { opacity: 0, y: -20, scale: 0.98 }, - }, -} - -// Quick action button animations -const quickActionVariants = { - initial: { opacity: 0, scale: 0.9, x: -10 }, - animate: { opacity: 1, scale: 1, x: 0 }, - hover: { scale: 1.02, y: -1 }, - tap: { scale: 0.98 }, -} -``` - -#### Consistent Design Language - -- **Glass-morphism**: Reuse AISummary visual patterns with chat-optimized spacing -- **Purple Gradient**: Maintain AI branding with purple accent colors -- **Spring Animations**: Use Spring.presets.smooth for all major transitions -- **Material Colors**: UIKit material colors for consistent depth and hierarchy -- **Responsive Typography**: Scalable text that works across different panel sizes - -## Implementation Plan - -### Phase 1: Core Functionality (MVP) - -#### Tasks (in order): - -1. **Create EntrySummaryCard Component** - - File: `apps/desktop/layer/renderer/src/modules/ai-chat/components/welcome/EntrySummaryCard.tsx` - - Reuse AISummary visual patterns - - Basic summary display with loading states - - Simple click-to-start conversation - -2. **Create useMainEntryId Hook** - - File: `apps/desktop/layer/renderer/src/modules/ai-chat/hooks/useMainEntryId.ts` - - Extract mainEntry block value from AI chat store - - Handle undefined/null states gracefully - -3. **Modify WelcomeScreen Component** - - File: `apps/desktop/layer/renderer/src/modules/ai-chat/components/layouts/WelcomeScreen.tsx` - - Add conditional rendering based on entry context - - Integrate EntrySummaryCard component - - Maintain existing functionality for non-entry contexts - -4. **Add Summary Prefetching to ChatInterface** - - File: `apps/desktop/layer/renderer/src/modules/ai-chat/components/layouts/ChatInterface.tsx` - - Prefetch summary when entry context available - - Only when welcome screen is showing (no messages) - -5. **Basic Smart Quick Actions** - - File: `apps/desktop/layer/renderer/src/modules/ai-chat/hooks/useSmartQuickActions.ts` - - Simple content-based action generation - - Default fallback actions for all entries - -6. **Update Translations** - - Add new translation keys for contextual descriptions - - Quick action labels and prompts - -### Phase 2: UX Optimization - -#### Tasks: - -7. **Enhanced Smart Quick Actions** - - Content analysis for intelligent action generation - - Entry type detection (article, social media, video) - - Priority-based action sorting - -8. **Micro-interaction Animations** - - Smooth state transitions with AnimatePresence - - Quick action button hover/tap effects - - Loading state animations with shimmer effects - -9. **Entry Quick Info Component** - - Display entry metadata (title, source, date) - - Compact design that doesn't compete with summary - - Optional component based on space constraints - -10. **Responsive Layout Adaptation** - - Floating panel vs fixed panel optimization - - Mobile-friendly touch targets - - Adaptive spacing and typography - -### Phase 3: Advanced Features - -#### Tasks: - -11. **First-time User Guidance** - - Onboarding tooltip for new feature - - Progressive disclosure of advanced features - - User preference learning - -12. **Context-aware Optimization** - - Reading progress integration - - Related entries suggestions - - Conversation history awareness - -13. **Performance Optimization** - - Summary caching improvements - - Lazy loading for non-critical components - - Bundle size optimization - -14. **A/B Testing Integration** - - Feature flag support - - Usage analytics - - Engagement metrics tracking - -## Validation Gates - -### Code Quality - -```bash -# TypeScript compilation -pnpm run typecheck - -# Linting -pnpm run lint - -# Code formatting -pnpm run format -``` - -### Testing Strategy - -```bash -# Unit tests for new hooks and components -pnpm run test apps/desktop/layer/renderer/src/modules/ai-chat - -# Integration tests for summary prefetching -pnpm run test:integration - -# E2E tests for chat interaction flows -pnpm run test:e2e -- --grep "AI Chat.*Summary" -``` - -### Manual Testing Checklist - -- [ ] **Entry Context Detection**: AI chat shows summary when viewing an entry -- [ ] **Fallback Behavior**: Default welcome screen when no entry context -- [ ] **Summary Loading**: Proper loading states and error handling -- [ ] **Quick Actions**: Context-appropriate conversation starters -- [ ] **Animations**: Smooth transitions between states -- [ ] **Responsive Design**: Works in both floating and fixed panels -- [ ] **Theme Support**: Proper light/dark mode appearance -- [ ] **Accessibility**: Keyboard navigation and screen reader support - -### Performance Metrics - -- [ ] **First Paint**: Summary content visible within 300ms -- [ ] **Bundle Impact**: <10KB addition to chat module bundle -- [ ] **Memory Usage**: No memory leaks during context switches -- [ ] **Animation Performance**: 60fps during transitions - -## Dependencies and Integration Points - -### Existing Systems Integration - -- **Summary Store**: `packages/internal/store/src/modules/summary/` -- **Entry Store**: Integration with entry context tracking -- **AI Chat Store**: Block system for entry context management -- **Translation System**: New keys for contextual content -- **Theme System**: UIKit colors and material design consistency - -### External Dependencies - -- **React Query**: Already used for summary prefetching -- **Framer Motion**: `m.` prefix components for animations -- **i18next**: Translation key expansion -- **Tailwind CSS**: UIKit color classes and material styles - -### API Integration - -- **Summary Generation**: Existing `/ai/summary` endpoint -- **No new API calls**: Reuses existing summary infrastructure -- **Caching Strategy**: Leverages existing SummaryStore LRU cache - -## Risk Assessment & Mitigation - -### Technical Risks - -1. **Performance Impact** (Low Risk) - - _Risk_: Summary prefetching could slow chat opening - - _Mitigation_: Conditional prefetching only when needed, existing cache strategy - -2. **Animation Jank** (Medium Risk) - - _Risk_: Complex animations could affect performance - - _Mitigation_: Use optimized Framer Motion patterns, test on lower-end devices - -3. **State Synchronization** (Low Risk) - - _Risk_: Entry context out of sync with chat store - - _Mitigation_: Leverage existing mainEntry block system, tested pattern - -### UX Risks - -1. **Feature Discoverability** (Medium Risk) - - _Risk_: Users may not notice the enhanced welcome screen - - _Mitigation_: Subtle onboarding hints, analytics tracking - -2. **Context Mismatch** (Low Risk) - - _Risk_: Summary doesn't match user expectations - - _Mitigation_: Clear visual hierarchy, fallback to default actions - -3. **Information Overload** (Medium Risk) - - _Risk_: Too much information in welcome screen - - _Mitigation_: Progressive disclosure, clean visual design - -## Success Metrics - -### Engagement Metrics - -- **AI Chat Adoption**: 25% increase in chat panel opens from entry context -- **Conversation Initiation**: 40% increase in messages sent from welcome screen -- **Quick Action Usage**: 60% of conversations start with context-aware quick actions -- **Session Duration**: 20% increase in average chat session length - -### Performance Metrics - -- **Load Time**: Summary content visible within 300ms -- **Error Rate**: <1% failure rate for summary loading -- **Cache Hit Rate**: >80% cache hits for recently viewed entries - -### User Satisfaction - -- **User Feedback**: Positive sentiment on enhanced chat experience -- **Feature Retention**: 70% of users who try the feature continue using it -- **Support Tickets**: No increase in AI-related support requests - -## Future Enhancements - -### Short-term (Next Quarter) - -- **Multi-language Summary Support**: Leverage existing i18n infrastructure -- **Summary Quality Feedback**: Allow users to rate summary relevance -- **Conversation Templates**: Pre-built conversation flows for common entry types - -### Medium-term (Next 6 Months) - -- **Cross-entry Conversations**: Enable discussing multiple entries in one chat -- **Smart Follow-ups**: AI-suggested next questions based on conversation context -- **Entry Annotations**: Allow highlighting specific parts of entry for discussion - -### Long-term (Next Year) - -- **Voice Interaction**: Voice commands for summary-based conversations -- **Collaborative Features**: Share AI conversations about entries -- **Advanced Analytics**: ML-driven insights on conversation patterns - -## Confidence Score: 9/10 - -This PRP provides comprehensive implementation guidance with: - -- ✅ **Detailed Architecture**: Clear component structure and data flow -- ✅ **Existing Pattern Reuse**: Leverages proven AISummary and chat patterns -- ✅ **Phased Implementation**: Incremental delivery with clear milestones -- ✅ **Risk Mitigation**: Identified potential issues with solutions -- ✅ **Testing Strategy**: Comprehensive validation approach -- ✅ **Performance Considerations**: Optimized prefetching and caching -- ✅ **UX Best Practices**: Context-aware design with progressive enhancement -- ✅ **Code Examples**: Realistic implementation snippets with proper patterns -- ✅ **Integration Points**: Clear dependencies and system boundaries -- ✅ **Success Metrics**: Measurable outcomes and validation criteria - -The implementation leverages existing, proven infrastructure while introducing meaningful enhancements that align with modern AI chat UX patterns and the application's design system. diff --git a/PRPs/android-shared-webview-image-interception.md b/PRPs/android-shared-webview-image-interception.md deleted file mode 100644 index c141cb3b3..000000000 --- a/PRPs/android-shared-webview-image-interception.md +++ /dev/null @@ -1,617 +0,0 @@ -# Android SharedWebView with Image Interception Implementation - -## Objective - -Implement an Android equivalent of the iOS SharedWebView module with advanced image interception capabilities using WebView hooks instead of custom URL schemes. This will provide feature parity between iOS and Android platforms while leveraging Android's superior request interception capabilities. - -## Context - -The iOS implementation uses a custom URL scheme (`follow-image://`) to intercept image requests due to WKWebView limitations. Android WebView offers more powerful request interception through `WebViewClient.shouldInterceptRequest()`, allowing for a more elegant and performant solution. - -### Current iOS Architecture (Reference) - -- **SharedWebViewModule** (apps/mobile/native/ios/Modules/SharedWebView/SharedWebViewModule.swift) -- **WebViewManager** (apps/mobile/native/ios/Modules/SharedWebView/WebViewManager.swift) - Singleton with lifecycle management -- **FOWebView** (apps/mobile/native/ios/Modules/SharedWebView/FOWebView.swift) - Custom WKWebView -- **FollowImageURLSchemeHandler** (apps/mobile/native/ios/Modules/SharedWebView/FollowImageURLSchemeHandler.swift) - Custom scheme handler -- **WebViewState** (apps/mobile/native/ios/Modules/SharedWebView/WebViewState.swift) - Observable state management - -### Existing Android Infrastructure - -- **Module Path**: `apps/mobile/native/android/src/main/java/expo/modules/follownative/` -- **Build Configuration**: Standard Expo modules with Kotlin support -- **Naming Convention**: `expo.modules.follownative.modulename.ModuleName` -- **TypeScript Interface**: Already defined in `apps/mobile/src/components/native/webview/index.ts` - -## Implementation Requirements - -### Core Components to Implement - -1. **SharedWebViewModule.kt** - Main Expo module exposing API to React Native -2. **WebViewManager.kt** - Singleton manager for WebView lifecycle and state -3. **SharedWebViewView.kt** - Expo view component -4. **FOWebView.kt** - Custom WebView with image interception -5. **ImageInterceptClient.kt** - WebViewClient with request interception -6. **ImageCache.kt** - Memory and disk caching system -7. **WebViewState.kt** - Reactive state management with StateFlow -8. **BridgeData.kt** - Message payloads and type definitions - -### API Compatibility Requirements - -Maintain 100% compatibility with existing TypeScript interface: - -```typescript -interface ISharedWebViewModule - extends NativeModule<{ - onContentHeightChanged: ({ height }: { height: number }) => void - onImagePreview: (event: ImagePreviewEvent) => void - onSeekAudio?: (e: { time: number }) => void - }> { - load(url: string): void - evaluateJavaScript(js: string): void - dispatch?(type: string, payload?: string): void - - // Debug helpers - getDebugState?(): WebViewDebugState - destroyForDebug?(): void - reloadLastURL?(): void - flushQueue?(): void -} -``` - -## Technical Architecture - -### Image Interception Strategy (Android Advantage) - -**iOS Approach**: Custom URL scheme with JavaScript injection - -```swift -// JavaScript injection to rewrite image URLs -url.replace(/^https?:/, 'follow-image:') -// Custom WKURLSchemeHandler for follow-image:// -``` - -**Android Approach**: Native request interception (Superior) - -```kotlin -override fun shouldInterceptRequest(view: WebView?, request: WebResourceRequest?): WebResourceResponse? { - if (isImageRequest(request)) { - return handleImageRequest(request) - } - return super.shouldInterceptRequest(view, request) -} -``` - -### Implementation Blueprint - -#### Phase 1: Core Infrastructure (Priority 1) - -```kotlin -// 1. SharedWebViewModule.kt -@ExpoModule(name = "FOSharedWebView") -class SharedWebViewModule : Module() { - private val coroutineScope = CoroutineScope(Dispatchers.Main + SupervisorJob()) - - override fun definition() = ModuleDefinition { - Name("FOSharedWebView") - - Function("load") { urlString: String -> - WebViewManager.load(urlString) - } - - Function("evaluateJavaScript") { js: String -> - WebViewManager.evaluateJavaScript(js) - } - - Function("dispatch") { type: String, payload: String? -> - WebViewManager.dispatch(type, payload) - } - - View(SharedWebViewView::class) { - Events("onContentHeightChange", "onSeekAudio") - Prop("url") { view: SharedWebViewView, url: String -> - WebViewManager.load(url) - } - } - - Events("onContentHeightChanged", "onImagePreview", "onSeekAudio") - - OnCreate { - WebViewManager.initializeLifecycleObservers(appContext.currentActivity!!) - } - - OnStartObserving { - // StateFlow subscriptions for reactive events - WebViewManager.state.contentHeight.onEach { height -> - sendEvent("onContentHeightChanged", mapOf("height" to height)) - }.launchIn(coroutineScope) - } - - OnStopObserving { - coroutineScope.cancel() - } - - // Debug functions - Function("getDebugState") { WebViewManager.getDebugState() } - Function("destroyForDebug") { WebViewManager.destroyForDebug() } - Function("reloadLastURL") { WebViewManager.reloadLastURL() } - Function("flushQueue") { WebViewManager.flushQueue() } - } -} -``` - -```kotlin -// 2. WebViewManager.kt - Singleton with lifecycle management -object WebViewManager { - val state = WebViewState() - private var sharedWebView: FOWebView? = null - private var currentHost: ViewGroup? = null - private var isReady = false - private val pendingScripts = mutableListOf() - private var lastUrl: String? = null - private val lastState = mutableMapOf() - - fun initializeLifecycleObservers(activity: Activity) { - val observer = object : DefaultLifecycleObserver { - override fun onPause(owner: LifecycleOwner) { onEnterBackground() } - override fun onResume(owner: LifecycleOwner) { onEnterForeground() } - } - (activity as ComponentActivity).lifecycle.addObserver(observer) - } - - fun load(urlString: String) { - MainScope().launch { - lastUrl = urlString - val webView = getOrCreateWebView() - if (webView.url == urlString && !isLoading(webView)) return@launch - isReady = false - webView.loadUrl(urlString) - } - } - - fun attach(host: ViewGroup) { - MainScope().launch { - val webView = getOrCreateWebView() - (webView.parent as? ViewGroup)?.removeView(webView) - host.addView(webView) - currentHost = host - } - } - - private fun getOrCreateWebView(): FOWebView { - return sharedWebView ?: FOWebView(context, state).also { sharedWebView = it } - } - - private fun onEnterBackground() { - if (currentHost == null) { - MainScope().launch { - delay(3000) - if (currentHost == null) destroyWebView() - } - } - } - - private fun onEnterForeground() { - if (sharedWebView == null) { - getOrCreateWebView() - lastUrl?.let { load(it) } - replayState() - } - } -} -``` - -#### Phase 2: Image Interception System (Priority 1) - -```kotlin -// 3. ImageInterceptClient.kt - Advanced request interception -class ImageInterceptClient( - private val imageCache: ImageCache, - private val baseClient: WebViewClient? = null -) : WebViewClient() { - - companion object { - private val IMAGE_MIME_TYPES = setOf( - "image/jpeg", "image/jpg", "image/png", "image/gif", - "image/webp", "image/svg+xml", "image/avif" - ) - } - - override fun shouldInterceptRequest( - view: WebView?, - request: WebResourceRequest? - ): WebResourceResponse? { - val url = request?.url?.toString() ?: return super.shouldInterceptRequest(view, request) - - if (!isImageRequest(request)) { - return baseClient?.shouldInterceptRequest(view, request) - ?: super.shouldInterceptRequest(view, request) - } - - return handleImageRequest(view, request) - } - - private fun isImageRequest(request: WebResourceRequest): Boolean { - val url = request.url.toString() - val acceptHeader = request.requestHeaders["Accept"] ?: "" - - // Multi-strategy detection - return acceptHeader.contains("image/") || - url.substringAfterLast('.', "").lowercase() in IMAGE_EXTENSIONS || - url.contains(Regex("/(images?|img|pics?|photos?|assets)/", RegexOption.IGNORE_CASE)) - } - - private fun handleImageRequest( - view: WebView?, - request: WebResourceRequest - ): WebResourceResponse? { - val url = request.url.toString() - val cacheKey = url - - // Check cache first - imageCache.get(cacheKey)?.let { cachedData -> - return createImageResponse(cachedData, detectMimeType(cachedData)) - } - - // Network request with proper headers - return try { - val modifiedRequest = buildImageRequest(request) - val response = executeImageRequest(modifiedRequest) - - response?.let { (data, mimeType) -> - imageCache.put(cacheKey, data) - createImageResponse(data, mimeType) - } - } catch (e: Exception) { - Log.w("ImageIntercept", "Failed to load image: $url", e) - null // Fallback to default WebView behavior - } - } - - private fun buildImageRequest(original: WebResourceRequest): HttpURLConnection { - val connection = URL(original.url.toString()).openConnection() as HttpURLConnection - - // Copy original headers - original.requestHeaders.forEach { (key, value) -> - if (!isRestrictedHeader(key)) { - connection.setRequestProperty(key, value) - } - } - - // Optimize for images - connection.setRequestProperty("Accept", "image/webp,image/avif,image/*,*/*;q=0.8") - connection.setRequestProperty("User-Agent", - "Mozilla/5.0 (Linux; Android 12) AppleWebKit/537.36 Mobile Safari/537.36") - - // Set referer for CORS compatibility - original.url.let { url -> - val referer = "${url.scheme}://${url.host}" - connection.setRequestProperty("Referer", referer) - } - - connection.connectTimeout = 10000 - connection.readTimeout = 15000 - return connection - } - - private fun createImageResponse(data: ByteArray, mimeType: String): WebResourceResponse { - val headers = mapOf( - "Access-Control-Allow-Origin" to "*", - "Cache-Control" to "public, max-age=3600", - "Content-Length" to data.size.toString() - ) - - return WebResourceResponse(mimeType, "utf-8", 200, "OK", headers, ByteArrayInputStream(data)) - } -} -``` - -```kotlin -// 4. ImageCache.kt - High-performance caching system -class ImageCache(private val context: Context) { - private val memoryCache = LruCache(16 * 1024 * 1024) // 16MB - private val diskCacheDir = File(context.cacheDir, "webview_images") - - init { - if (!diskCacheDir.exists()) diskCacheDir.mkdirs() - } - - fun get(key: String): ByteArray? { - // Memory first, then disk - return memoryCache.get(key) ?: getDiskCache(key)?.also { - memoryCache.put(key, it) - } - } - - fun put(key: String, data: ByteArray) { - memoryCache.put(key, data) - putDiskCache(key, data) - } - - private fun getDiskCache(key: String): ByteArray? = try { - val file = getCacheFile(key) - if (file.exists() && System.currentTimeMillis() - file.lastModified() < 24 * 60 * 60 * 1000) { - file.readBytes() - } else null - } catch (e: Exception) { null } - - private fun putDiskCache(key: String, data: ByteArray) { - try { - getCacheFile(key).writeBytes(data) - } catch (e: Exception) { - Log.w("ImageCache", "Failed to cache image", e) - } - } - - private fun getCacheFile(key: String): File { - val fileName = key.hashCode().toString(16) + ".cache" - return File(diskCacheDir, fileName) - } -} -``` - -#### Phase 3: State Management and Views (Priority 2) - -```kotlin -// 5. WebViewState.kt - Reactive state management -class WebViewState { - private val _contentHeight = MutableStateFlow(Resources.getSystem().displayMetrics.heightPixels.toFloat()) - val contentHeight: StateFlow = _contentHeight.asStateFlow() - - private val _imagePreviewEvent = MutableStateFlow(null) - val imagePreviewEvent: StateFlow = _imagePreviewEvent.asStateFlow() - - private val _audioSeekEvent = MutableStateFlow(null) - val audioSeekEvent: StateFlow = _audioSeekEvent.asStateFlow() - - fun updateContentHeight(height: Float) { _contentHeight.value = height } - fun triggerImagePreview(urls: List, index: Int) { - _imagePreviewEvent.value = ImagePreviewEvent(urls, index) - } - fun triggerAudioSeek(time: Double) { - _audioSeekEvent.value = AudioSeekEvent(time) - } -} - -data class ImagePreviewEvent(val imageUrls: List, val index: Int) -data class AudioSeekEvent(val time: Double) -``` - -```kotlin -// 6. FOWebView.kt - Custom WebView implementation -class FOWebView(context: Context, private val state: WebViewState) : WebView(context) { - - private val imageCache = ImageCache(context) - private val baseWebViewClient = FOWebViewClient() - private val javascriptInterface = JavaScriptInterface(state) - - init { - setupWebView() - webViewClient = ImageInterceptClient(imageCache, baseWebViewClient) - webChromeClient = FOWebChromeClient() - addJavaScriptInterface(javascriptInterface, "Android") - } - - private fun setupWebView() { - settings.apply { - javaScriptEnabled = true - domStorageEnabled = true - allowFileAccess = true - allowContentAccess = true - loadsImagesAutomatically = true - blockNetworkImage = false - cacheMode = WebSettings.LOAD_DEFAULT - setAppCacheEnabled(true) - } - - // Inject JavaScript bridge - injectBridgeScript() - } - - private fun injectBridgeScript() { - val script = """ - ;(() => { - window.__RN__ = true - - function send(data) { - Android.postMessage(JSON.stringify(data)) - } - - window.bridge = { - measure: () => send({ type: "measure" }), - setContentHeight: (height) => send({ type: "setContentHeight", payload: height }), - previewImage: (data) => send({ type: "previewImage", payload: data }), - seekAudio: (time) => send({ type: "audio:seekTo", payload: { time } }) - } - - // Signal readiness - document.addEventListener("DOMContentLoaded", () => { - send({ type: "ready" }) - }) - })() - """ - evaluateJavascript(script, null) - } - - fun clearImageCache() { imageCache.clear() } -} -``` - -#### Phase 4: Integration and Testing (Priority 3) - -### File Structure to Create - -``` -apps/mobile/native/android/src/main/java/expo/modules/follownative/sharedwebview/ -├── SharedWebViewModule.kt # Main Expo module -├── SharedWebViewView.kt # Expo view component -├── WebViewManager.kt # Singleton WebView manager -├── FOWebView.kt # Custom WebView implementation -├── ImageInterceptClient.kt # Request interception logic -├── ImageCache.kt # Caching system -├── WebViewState.kt # State management -├── BridgeData.kt # Message payloads -└── JavaScriptInterface.kt # WebView-to-native bridge -``` - -### Configuration Updates Required - -1. **expo-module.config.json** - Add new Android module: - -```json -{ - "android": { - "modules": [ - "expo.modules.follownative.sharedwebview.SharedWebViewModule" - // ... existing modules - ] - } -} -``` - -2. **Android Gradle Dependencies** - Add to `apps/mobile/native/android/build.gradle`: - -```gradle -dependencies { - implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.8.6" - implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.9.0" -} -``` - -## Implementation Tasks - -### Phase 1: Core Infrastructure (Week 1) - -1. Create module structure and base classes -2. Implement SharedWebViewModule.kt with Expo module definition -3. Implement WebViewManager.kt singleton with lifecycle management -4. Implement WebViewState.kt with StateFlow-based reactive state -5. Create basic SharedWebViewView.kt Expo view component - -### Phase 2: Image Interception System (Week 1-2) - -1. Implement ImageInterceptClient.kt with shouldInterceptRequest logic -2. Build ImageCache.kt with memory and disk caching -3. Create image request detection and classification system -4. Implement network request handling with proper headers -5. Add MIME type detection and response creation - -### Phase 3: WebView Integration (Week 2) - -1. Implement FOWebView.kt custom WebView -2. Create JavaScriptInterface.kt for native-JS communication -3. Add JavaScript bridge injection and message handling -4. Implement content height tracking and layout updates -5. Add debug utilities and state inspection tools - -### Phase 4: Testing and Integration (Week 2-3) - -1. Create comprehensive unit tests for each component -2. Implement integration tests with mock WebView scenarios -3. Add performance tests for image caching and request interception -4. Test cross-platform API compatibility with iOS implementation -5. Validate memory management and lifecycle behavior - -### Phase 5: Documentation and Deployment (Week 3) - -1. Update expo-module.config.json with new Android module -2. Add necessary Gradle dependencies and configurations -3. Create comprehensive documentation for the implementation -4. Update TypeScript definitions if needed -5. Perform final testing and validation - -## Validation Gates - -### Build Validation - -```bash -cd apps/mobile/native/android -./gradlew assembleDebug -``` - -### Code Quality Checks - -```bash -cd apps/mobile -npm run typecheck -``` - -### Functional Testing - -```bash -# Test module loading -adb logcat | grep "FOSharedWebView" - -# Test image interception -# Navigate to image-heavy content and verify cache behavior -``` - -### Performance Testing - -```kotlin -// Measure image cache hit rates and memory usage -WebViewManager.getImageCacheStats().let { stats -> - assertTrue("Cache hit rate should be > 80%", stats.hitRate > 0.8) - assertTrue("Memory usage should be < 50MB", stats.memoryUsage < 50 * 1024 * 1024) -} -``` - -### Cross-Platform Compatibility Testing - -```javascript -// Verify identical API behavior across platforms -const debugState = await SharedWebViewModule.getDebugState() -expect(debugState).toHaveProperty("hasWebView") -expect(debugState).toHaveProperty("contentHeight") -``` - -## Documentation and References - -### Essential References - -- [Expo Modules API Documentation](https://docs.expo.dev/modules/overview/) -- [Native View Tutorial](https://docs.expo.dev/modules/native-view-tutorial/) -- [Android WebView Request Interception](https://medium.com/@pouryarezaee76/intercepting-requests-in-android-webview-8eb628b32f2a) -- [WebView Security Best Practices](https://blog.oversecured.com/Android-Exploring-vulnerabilities-in-WebResourceResponse/) - -### Key Implementation Files to Reference - -- `apps/mobile/native/ios/Modules/SharedWebView/` - Complete iOS implementation -- `apps/mobile/native/android/src/main/java/expo/modules/follownative/FollowNativeModule.kt` - Existing module patterns -- `apps/mobile/src/components/native/webview/index.ts` - TypeScript API interface - -### Critical Security Considerations - -1. **Request Validation**: Always validate URLs before processing to prevent malicious requests -2. **Header Sanitization**: Filter restricted headers to prevent security vulnerabilities -3. **File Access Limiting**: Restrict file:// URL access to app bundle resources only -4. **Content Security**: Implement proper CSP headers for loaded content -5. **Bridge Data Validation**: Validate all JavaScript bridge message payloads - -## Expected Outcomes - -### Performance Improvements over iOS - -- **No URL Rewriting Overhead**: Direct request interception vs. JavaScript URL manipulation -- **Better Error Handling**: Graceful fallback to default WebView behavior on failure -- **Enhanced Debugging**: Standard HTTP request debugging vs. custom scheme debugging -- **Improved Reliability**: No dependency on JavaScript injection success - -### Feature Parity Achievement - -- 100% API compatibility with iOS implementation -- Identical event handling and state management -- Same debugging capabilities and state inspection tools -- Consistent caching behavior and memory management - -### Success Metrics - -- Image cache hit rate > 90% -- Memory usage < 64MB for typical usage -- Zero crashes related to WebView lifecycle management -- API response time < 50ms for cached resources -- Cross-platform test suite passing rate > 99% - -## Confidence Score: 9/10 - -This PRP provides comprehensive context, detailed implementation blueprints, existing code patterns to follow, specific file references, security considerations, and executable validation gates. The Android implementation leverages superior platform capabilities while maintaining complete API compatibility with iOS. All necessary technical context, external documentation, and testing patterns are included to enable successful one-pass implementation. diff --git a/PRPs/enhanced-ai-usage-observability-frontend.md b/PRPs/enhanced-ai-usage-observability-frontend.md deleted file mode 100644 index b90edae01..000000000 --- a/PRPs/enhanced-ai-usage-observability-frontend.md +++ /dev/null @@ -1,938 +0,0 @@ -name: "Enhanced AI Usage Observability Frontend" -description: | -Frontend implementation for enhanced AI token usage observability with real-time -tracking, predictive analytics, and user-centered visualizations. Integrates with -the backend API from enhanced-ai-usage-observability.md to provide comprehensive -usage insights in both AI settings tab and dedicated analytics page. - -## Purpose - -Implement comprehensive React-based frontend for AI usage observability that transforms raw token statistics into actionable user insights through intuitive visualizations, real-time feedback, and predictive warnings. - -## Core Principles - -1. **Component Reusability**: Build modular components following Follow's SettingBuilder patterns -2. **Real-time Updates**: Use React Query for efficient data synchronization and caching -3. **Progressive Disclosure**: Simple overview in AI tab, detailed analysis in separate page -4. **Visual Clarity**: Use charts and progress indicators for immediate comprehension -5. **Mobile Responsive**: Ensure consistent experience across desktop and mobile - ---- - -## Goal - -Create React frontend components that consume enhanced AI usage analytics APIs to provide users with real-time session tracking, usage pattern insights, predictive warnings, and optimization recommendations through intuitive visualizations. - -## Why - -- **User Empowerment**: Transform technical token counts into actionable usage insights -- **Cost Awareness**: Provide real-time feedback on current session costs and efficiency -- **Proactive Management**: Warn users before hitting rate limits through predictive analytics -- **Usage Optimization**: Help users understand and optimize their AI consumption patterns -- **Seamless Integration**: Integrate naturally into existing AI settings workflow - -## What - -Frontend implementation with: - -1. **AI Tab Integration**: Compact usage analysis section in AI settings -2. **Detailed Analytics Page**: Enhanced token-usage page with comprehensive charts -3. **Real-Time Tracking**: Live session usage and progress indicators -4. **Predictive Warnings**: Visual alerts for approaching rate limits -5. **Usage Visualizations**: Charts for trends, feature distribution, and model efficiency - -### Success Criteria - -- [ ] AI Tab shows simplified usage overview with key metrics and warnings -- [ ] Token usage page displays comprehensive analytics with interactive charts -- [ ] Real-time session tracking updates every 10 seconds -- [ ] Predictive warnings appear when rate limit risk is detected -- [ ] Charts render usage patterns, feature distribution, and efficiency comparisons -- [ ] Mobile responsive design maintains usability on smaller screens -- [ ] Components follow existing Follow UI patterns and design system -- [ ] All user-facing text is properly internationalized - -## All Needed Context - -### Documentation & References - -```yaml -# MUST READ - Include these in your context window -- file: apps/desktop/layer/renderer/src/modules/settings/tabs/ai.tsx - why: Current AI settings page structure and SettingBuilder pattern - -- file: apps/desktop/layer/renderer/src/modules/settings/tabs/token-usage.tsx - why: Existing token usage component to enhance and reference - -- file: apps/desktop/layer/renderer/src/modules/ai-task/query.ts - why: React Query patterns and query key hierarchies used in Follow - -- file: apps/desktop/layer/renderer/src/modules/settings/tabs/ai/mcp/MCPServicesSection.tsx - why: Component structure patterns, useQuery usage, error handling - -- file: apps/desktop/layer/renderer/src/modules/settings/tabs/ai/PanelStyleSection.tsx - why: SettingTabbedSegment usage and section component patterns - -- file: packages/internal/components/src/ui/progress/index.tsx - why: Progress component for usage indicators - -- file: apps/desktop/layer/renderer/src/lib/api-client.ts - why: followClient usage and API integration patterns - -- doc: https://recharts.org/en-US/api - section: LineChart, PieChart, BarChart components - critical: Follow doesn't have charting library - need to add recharts - -- doc: https://tanstack.com/query/latest/docs/framework/react/guides/query-keys - section: Query key hierarchies and best practices - critical: Follow uses hierarchical query keys pattern - -- doc: https://react-i18next.com/latest/using-with-hooks - section: useTranslation hook usage - critical: All user-facing text must be internationalized -``` - -### Current Codebase tree - -```bash -apps/desktop/layer/renderer/src/modules/settings/tabs/ -├── ai.tsx # Main AI settings page with SettingBuilder -├── token-usage.tsx # Current token usage implementation -└── ai/ - ├── index.ts # Exports AI-related components - ├── PanelStyleSection.tsx # Example section component pattern - ├── PersonalizePromptSection.tsx # Example section component pattern - └── mcp/MCPServicesSection.tsx # Complex section with React Query - -apps/desktop/layer/renderer/src/modules/ai-task/ -└── query.ts # React Query patterns and conventions - -packages/internal/components/src/ui/ -├── progress/index.tsx # Progress bars and indicators -├── card/index.tsx # Card containers -├── tabs/index.tsx # Tab navigation -└── [other UI components...] -``` - -### Desired Codebase tree with files to be added and responsibility - -```bash -apps/desktop/layer/renderer/src/modules/settings/tabs/ -├── ai.tsx # MODIFIED: Add UsageAnalysisSection -├── token-usage.tsx # MODIFIED: Enhanced with analytics -└── ai/ - ├── index.ts # MODIFIED: Export new usage components - └── usage/ # NEW: Usage observability components - ├── UsageAnalysisSection.tsx # AI tab simplified usage overview - ├── hooks/ - │ ├── useEnhancedTokenUsage.ts # Enhanced config API hook - │ ├── useUsageAnalytics.ts # Analytics API hook - │ ├── useSessionTracking.ts # Session tracking hook - │ └── index.ts # Hook exports - └── components/ - ├── UsageProgressRing.tsx # Circular progress component - ├── SessionStatusCard.tsx # Active session display - ├── UsageWarningBanner.tsx # Predictive warning alerts - ├── QuickInsights.tsx # Key metrics display - └── index.ts # Component exports - -apps/desktop/layer/renderer/src/modules/settings/tabs/token-usage/ -├── index.tsx # MODIFIED: Export enhanced components -├── TokenUsageEnhanced.tsx # NEW: Enhanced analytics page -├── components/ -│ ├── UsageTrendChart.tsx # Line chart for usage trends -│ ├── FeatureDistributionChart.tsx # Pie chart for feature breakdown -│ ├── ModelEfficiencyChart.tsx # Bar chart for model comparison -│ ├── TimePatternAnalysis.tsx # Time-based usage patterns -│ ├── PredictiveInsights.tsx # Insights and recommendations -│ ├── RealTimeMetrics.tsx # Live usage dashboard -│ └── index.ts # Chart component exports -└── styles/ - └── charts.module.css # Chart-specific styles - -apps/desktop/layer/renderer/src/queries/ -└── ai-analytics.ts # NEW: Centralized analytics query logic -``` - -### Known Gotchas of our codebase & Library Quirks - -```typescript -// CRITICAL: Project-specific patterns and constraints -// REACT_QUERY: Use hierarchical query keys following aiTaskKeys pattern -// REACT_QUERY: Extract data with data?.data pattern, handle loading/error states -// REACT_QUERY: Use proper cache invalidation on mutations -// COMPONENTS: Import UI components from @follow/components/ui/* -// COMPONENTS: Use SettingBuilder for settings pages, SettingTabbedSegment for options -// API: Use followClient.api.* pattern, not direct fetch -// I18N: All user-facing strings must use useTranslation hook -// STYLING: Use Tailwind with UIKit colors (text-text, bg-fill, etc.) -// STYLING: Follow mobile-first responsive design patterns -// ICONS: Use i-mgc-* prefix for MingCute icons, i-mingcute-* as fallback -// MOTION: Use m. instead of motion. for Framer Motion components -// CHARTS: No existing chart library - need to add recharts to dependencies -// MODALS: Use useDialog and useModalStack for modal interactions -// TOASTS: Use toast from "sonner" for notifications -// STATE: Settings state managed through Jotai atoms -// ROUTES: Use React Router Link for navigation -// ERROR_HANDLING: Follow existing error boundary patterns -``` - -## Implementation Blueprint - -### Data models and structure - -Enhanced React Query hooks and TypeScript interfaces: - -```typescript -// apps/desktop/layer/renderer/src/modules/settings/tabs/ai/usage/hooks/useEnhancedTokenUsage.ts -export interface EnhancedTokenUsage { - defaultModel: string - availableModels: string[] - modelBillingStrategy: Record - rateLimit: { - maxTokens: number - currentTokens: number - remainingTokens: number - windowDuration: number - windowResetTime: number - // Enhanced fields - usageRate: number - projectedLimitTime: number | null - warningLevel: "safe" | "moderate" | "high" | "critical" - } - usage: { - total: number - used: number - remaining: number - resetAt: string - // Enhanced fields - avgTokensPerSession: number - avgSessionDuration: number - mostUsedFeature: string - efficiencyScore: number - } - currentSession: { - sessionId: string | null - tokensUsed: number - messageCount: number - duration: number - isActive: boolean - } - usageHistory: Array<{ - id: string - createdAt: Date - changes: number - comment: string | null - operationType: string | null - modelUsed: string | null - sessionId: string | null - }> -} - -// apps/desktop/layer/renderer/src/modules/settings/tabs/ai/usage/hooks/useUsageAnalytics.ts -export interface UsageAnalytics { - patterns: { - daily: Array<{ - date: string - totalTokens: number - operationCount: number - peakHour: number | null - }> - byOperation: Array<{ - operationType: string - totalTokens: number - operationCount: number - percentage: number - avgTokensPerOperation: number - }> - byModel: Array<{ - model: string - totalTokens: number - operationCount: number - percentage: number - avgEfficiency: number - }> - } - insights: { - usageTrend: "increasing" | "decreasing" | "stable" - projectedMonthlyUsage: number - recommendations: string[] - efficiencyTips: string[] - } - timePatterns: { - peakHours: number[] - peakDays: number[] - avgSessionDuration: number - avgTokensPerSession: number - } -} - -// Query keys following aiTaskKeys pattern -export const aiAnalyticsKeys = { - all: ["ai-analytics"] as const, - config: () => [...aiAnalyticsKeys.all, "config"] as const, - analytics: () => [...aiAnalyticsKeys.all, "analytics"] as const, - analyticsWithDays: (days: number) => [...aiAnalyticsKeys.analytics(), days] as const, - session: () => [...aiAnalyticsKeys.all, "session"] as const, - sessionCurrent: () => [...aiAnalyticsKeys.session(), "current"] as const, -} -``` - -### List of tasks to be completed to fulfill the PRP in the order they should be completed - -```yaml -Task 1: Add recharts dependency for data visualization -MODIFY package.json (apps/desktop/): - - ADD "recharts": "^2.10.0" to dependencies - - RUN pnpm install to install charting library - -Task 2: Create AI analytics query hooks -CREATE apps/desktop/layer/renderer/src/queries/ai-analytics.ts: - - IMPLEMENT hierarchical query keys following aiTaskKeys pattern - - EXPORT useEnhancedTokenUsage, useUsageAnalytics, useSessionTracking hooks - - USE followClient.api pattern for API calls - - HANDLE loading states and error cases - -Task 3: Create usage progress ring component -CREATE apps/desktop/layer/renderer/src/modules/settings/tabs/ai/usage/components/UsageProgressRing.tsx: - - IMPLEMENT circular SVG progress indicator - - SUPPORT different sizes (sm/md/lg) and color coding - - FOLLOW UIKit color system for status indication - -Task 4: Create session status components -CREATE apps/desktop/layer/renderer/src/modules/settings/tabs/ai/usage/components/SessionStatusCard.tsx: - - DISPLAY active session information when available - - SHOW real-time token usage and message count - - USE card component from @follow/components/ui/card - -CREATE apps/desktop/layer/renderer/src/modules/settings/tabs/ai/usage/components/UsageWarningBanner.tsx: - - IMPLEMENT predictive warning display - - COLOR-CODE warnings by severity level - - SHOW projected limit time and usage rate - -Task 5: Create simplified usage analysis section -CREATE apps/desktop/layer/renderer/src/modules/settings/tabs/ai/usage/UsageAnalysisSection.tsx: - - IMPLEMENT compact overview for AI settings tab - - USE UsageProgressRing and SessionStatusCard components - - INCLUDE "View Details" link to full analytics page - - FOLLOW SettingTabbedSegment patterns from existing sections - -Task 6: Create chart components for detailed analytics -CREATE apps/desktop/layer/renderer/src/modules/settings/tabs/token-usage/components/UsageTrendChart.tsx: - - USE recharts LineChart for 30-day usage trends - - IMPLEMENT responsive container and proper tooltips - - STYLE using Tailwind and UIKit colors - -CREATE apps/desktop/layer/renderer/src/modules/settings/tabs/token-usage/components/FeatureDistributionChart.tsx: - - USE recharts PieChart for feature usage breakdown - - ADD custom legend with percentages - - IMPLEMENT hover interactions - -CREATE apps/desktop/layer/renderer/src/modules/settings/tabs/token-usage/components/ModelEfficiencyChart.tsx: - - USE recharts BarChart for model efficiency comparison - - SHOW tokens per operation efficiency - - COLOR-CODE by efficiency levels - -Task 7: Create comprehensive analytics page -CREATE apps/desktop/layer/renderer/src/modules/settings/tabs/token-usage/TokenUsageEnhanced.tsx: - - IMPLEMENT tabbed interface with overview/patterns/efficiency/history - - USE chart components for visualizations - - INCLUDE real-time metrics dashboard - - ADD predictive insights and recommendations section - -Task 8: Create time pattern analysis components -CREATE apps/desktop/layer/renderer/src/modules/settings/tabs/token-usage/components/TimePatternAnalysis.tsx: - - DISPLAY peak usage hours and days - - SHOW usage patterns and habits - - PROVIDE optimization suggestions based on patterns - -CREATE apps/desktop/layer/renderer/src/modules/settings/tabs/token-usage/components/PredictiveInsights.tsx: - - RENDER usage trend analysis - - DISPLAY projected monthly usage - - SHOW personalized recommendations and tips - -Task 9: Integrate usage section into AI settings -MODIFY apps/desktop/layer/renderer/src/modules/settings/tabs/ai.tsx: - - IMPORT UsageAnalysisSection component - - ADD to SettingBuilder array after existing sections - - INCLUDE proper title and divider - -MODIFY apps/desktop/layer/renderer/src/modules/settings/tabs/ai/index.ts: - - EXPORT UsageAnalysisSection from new usage module - -Task 10: Enhance existing token usage page -MODIFY apps/desktop/layer/renderer/src/modules/settings/tabs/token-usage.tsx: - - REPLACE current implementation with TokenUsageEnhanced - - MAINTAIN backward compatibility with existing route - - ADD proper error boundaries and loading states - -Task 11: Add internationalization support -CREATE locales/en/ai.json (add new keys): - - ADD usage_analysis.* keys for simplified section - - ADD analytics.* keys for detailed page - - ADD insights.* keys for recommendations and warnings - -CREATE similar entries in locales/zh-CN/ai.json and locales/ja.json - -Task 12: Add responsive mobile support -CREATE apps/desktop/layer/renderer/src/modules/settings/tabs/token-usage/styles/charts.module.css: - - ADD mobile-responsive chart styles - - IMPLEMENT collapsible sections for mobile - - ENSURE touch-friendly interactions - -Task 13: Implement real-time updates -MODIFY all query hooks: - - ADD refetchInterval: 30000 for config data - - ADD refetchInterval: 10000 for session data - - IMPLEMENT proper error retry logic - -Task 14: Add loading and error states -ENHANCE all components: - - ADD skeleton loading states using @follow/components/ui/skeleton - - IMPLEMENT error boundaries with retry mechanisms - - SHOW proper fallback UI when data unavailable - -Task 15: Create comprehensive component exports -CREATE apps/desktop/layer/renderer/src/modules/settings/tabs/ai/usage/index.ts: - - EXPORT all usage-related components and hooks - - MAINTAIN clean import structure - -CREATE apps/desktop/layer/renderer/src/modules/settings/tabs/token-usage/components/index.ts: - - EXPORT all chart and analytics components - -Task 16: Add interactive features -ENHANCE chart components: - - ADD click interactions for drill-down views - - IMPLEMENT brush selection for time range filtering - - ADD export functionality for usage reports - -Task 17: Implement accessibility features -ENHANCE all components: - - ADD proper ARIA labels and descriptions - - ENSURE keyboard navigation support - - IMPLEMENT high contrast mode compatibility - - ADD screen reader support for charts -``` - -### Per task pseudocode as needed - -```typescript -// Task 2: AI Analytics Query Hooks -// apps/desktop/layer/renderer/src/queries/ai-analytics.ts - -import { useQuery } from "@tanstack/react-query" -import { followClient } from "~/lib/api-client" - -export const aiAnalyticsKeys = { - all: ["ai-analytics"] as const, - config: () => [...aiAnalyticsKeys.all, "config"] as const, - analytics: () => [...aiAnalyticsKeys.all, "analytics"] as const, - analyticsWithDays: (days: number) => [...aiAnalyticsKeys.analytics(), days] as const, - session: () => [...aiAnalyticsKeys.all, "session"] as const, - sessionCurrent: () => [...aiAnalyticsKeys.session(), "current"] as const, -} - -export const useEnhancedTokenUsage = (options = {}) => { - const { data, isLoading, error } = useQuery({ - queryKey: aiAnalyticsKeys.config(), - queryFn: async () => { - const res = await followClient.api.ai.config() - return res - }, - refetchInterval: 30000, // 30 seconds - refetchOnWindowFocus: true, - retry: 2, - ...options, - }) - return { data: data?.data, isLoading, error } -} - -export const useUsageAnalytics = (days: number = 30) => { - const { data, isLoading, error } = useQuery({ - queryKey: aiAnalyticsKeys.analyticsWithDays(days), - queryFn: async () => { - const res = await followClient.api.ai.analytics({ days }) - return res - }, - refetchInterval: 60000, // 1 minute - ...options, - }) - return { data: data?.data, isLoading, error } -} - -// Task 5: Usage Analysis Section for AI Tab -// apps/desktop/layer/renderer/src/modules/settings/tabs/ai/usage/UsageAnalysisSection.tsx - -import { Card, CardContent } from "@follow/components/ui/card" -import { cn } from "@follow/utils/utils" -import { useTranslation } from "react-i18next" -import { Link } from "react-router-dom" -import { UsageProgressRing } from "./components/UsageProgressRing" -import { SessionStatusCard } from "./components/SessionStatusCard" -import { UsageWarningBanner } from "./components/UsageWarningBanner" -import { useEnhancedTokenUsage } from "~/queries/ai-analytics" - -export const UsageAnalysisSection = ({ compact = false }) => { - const { t } = useTranslation("ai") - const { data: config, isLoading } = useEnhancedTokenUsage() - - if (isLoading) { - return
- } - - if (!config) return null - - const { usage, rateLimit, currentSession } = config - const usagePercentage = usage.total === 0 ? 0 : (usage.used / usage.total) * 100 - - return ( -
- {/* Warning banner for critical states */} - {rateLimit.warningLevel !== "safe" && ( - - )} - - {/* Main usage card */} - - -
-

- {t("usage_analysis.title")} -

- - {t("usage_analysis.view_details")} → - -
- -
- - -
-
- - {formatTokenCount(rateLimit.remainingTokens).value} - {formatTokenCount(rateLimit.remainingTokens).unit} - - - {t("usage_analysis.tokens_remaining")} - -
- -
- {formatTokenCount(usage.used).value}{formatTokenCount(usage.used).unit} / - {formatTokenCount(usage.total).value}{formatTokenCount(usage.total).unit} used -
- -
- {t("usage_analysis.resets_in")} {formatTimeRemaining(rateLimit.windowResetTime - Date.now())} -
-
-
- - {/* Current session if active */} - {currentSession?.isActive && ( - - )} - - {/* Quick insights */} - {!compact && ( -
- -
- )} -
-
-
- ) -} - -// Task 6: Usage Trend Chart -// apps/desktop/layer/renderer/src/modules/settings/tabs/token-usage/components/UsageTrendChart.tsx - -import { ResponsiveContainer, LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip } from "recharts" -import { Card, CardHeader, CardTitle, CardContent } from "@follow/components/ui/card" - -interface UsageTrendChartProps { - data: Array<{ - date: string - totalTokens: number - operationCount: number - }> -} - -export const UsageTrendChart = ({ data }: UsageTrendChartProps) => { - return ( - - - Usage Trends (Last 30 Days) - - - - - - - - [ - name === 'totalTokens' ? `${value.toLocaleString()} tokens` : `${value} operations`, - name === 'totalTokens' ? 'Tokens Used' : 'Operations' - ]} - contentStyle={{ - backgroundColor: 'rgb(var(--color-fill))', - border: '1px solid rgb(var(--color-fill-secondary))', - borderRadius: '6px' - }} - /> - - - - - - ) -} - -// Task 7: Enhanced Token Usage Page -// apps/desktop/layer/renderer/src/modules/settings/tabs/token-usage/TokenUsageEnhanced.tsx - -import { Tabs, TabsContent, TabsList, TabsTrigger } from "@follow/components/ui/tabs" -import { useEnhancedTokenUsage, useUsageAnalytics } from "~/queries/ai-analytics" -import { UsageTrendChart } from "./components/UsageTrendChart" -import { FeatureDistributionChart } from "./components/FeatureDistributionChart" -import { ModelEfficiencyChart } from "./components/ModelEfficiencyChart" -import { RealTimeMetrics } from "./components/RealTimeMetrics" - -export const TokenUsageEnhanced = () => { - const { t } = useTranslation("ai") - const { data: config, isLoading: configLoading } = useEnhancedTokenUsage() - const { data: analytics, isLoading: analyticsLoading } = useUsageAnalytics(30) - - if (configLoading || analyticsLoading) { - return ( -
-
-
- ) - } - - return ( -
- {/* Real-time metrics overview */} - - - {/* Predictive warnings */} - {config.rateLimit.projectedLimitTime && ( - - )} - - {/* Detailed analytics tabs */} - - - {t("analytics.tabs.overview")} - {t("analytics.tabs.patterns")} - {t("analytics.tabs.efficiency")} - {t("analytics.tabs.history")} - - - - - -
- - -
-
- - - - - - - - - - - - -
-
- ) -} -``` - -### Integration Points - -```yaml -DEPENDENCIES: - - add: "recharts": "^2.10.0" to package.json - - verify: @tanstack/react-query version compatibility - - ensure: React 19 compatibility with recharts - -API_INTEGRATION: - - endpoints: /ai/chat/config (enhanced), /ai/analytics, /ai/session/current - - pattern: followClient.api.ai.* for all API calls - - response: Extract data with data?.data pattern - -UI_COMPONENTS: - - use: @follow/components/ui/* for all UI primitives - - follow: UIKit color system (text-text, bg-fill, etc.) - - icons: i-mgc-* prefix for MingCute icons - -SETTINGS_INTEGRATION: - - modify: apps/desktop/layer/renderer/src/modules/settings/tabs/ai.tsx - - pattern: Add to SettingBuilder array following existing structure - - export: Update ai/index.ts with new components - -ROUTES: - - existing: /settings/token-usage enhanced with new components - - navigation: Use React Router Link for internal navigation - -STATE_MANAGEMENT: - - queries: React Query for server state - - settings: Jotai atoms for client settings (if needed) - - cache: Proper invalidation on data mutations -``` - -## Validation Loop - -### Level 1: Syntax & Style - -```bash -# Run these FIRST - fix any errors before proceeding -pnpm lint --fix # ESLint auto-fix -pnpm typecheck # TypeScript checking -pnpm --filter=@follow/desktop typecheck # Desktop specific check - -# Expected: No errors. If errors, READ the error and fix. -``` - -### Level 2: Component Tests - -```typescript -// CREATE apps/desktop/layer/renderer/src/modules/settings/tabs/ai/usage/__tests__/UsageAnalysisSection.test.tsx -import { render, screen } from "@testing-library/react" -import { QueryClient, QueryClientProvider } from "@tanstack/react-query" -import { UsageAnalysisSection } from "../UsageAnalysisSection" - -const mockConfig = { - usage: { used: 500000, total: 1000000, efficiencyScore: 85 }, - rateLimit: { remainingTokens: 500000, warningLevel: "safe" }, - currentSession: { isActive: false }, -} - -jest.mock("~/queries/ai-analytics", () => ({ - useEnhancedTokenUsage: () => ({ data: mockConfig, isLoading: false }), -})) - -describe('UsageAnalysisSection', () => { - it('renders usage overview correctly', () => { - const queryClient = new QueryClient() - - render( - - - - ) - - expect(screen.getByText("Token Usage")).toBeInTheDocument() - expect(screen.getByText("View Details →")).toBeInTheDocument() - expect(screen.getByText("500K remaining")).toBeInTheDocument() - }) - - it('shows warning banner for critical states', () => { - const criticalConfig = { - ...mockConfig, - rateLimit: { ...mockConfig.rateLimit, warningLevel: "critical" } - } - - jest.mocked(useEnhancedTokenUsage).mockReturnValue({ - data: criticalConfig, - isLoading: false - }) - - render( - - - - ) - - expect(screen.getByRole("alert")).toBeInTheDocument() - }) -}) - -// CREATE apps/desktop/layer/renderer/src/modules/settings/tabs/token-usage/__tests__/TokenUsageEnhanced.test.tsx -describe('TokenUsageEnhanced', () => { - it('renders analytics tabs correctly', () => { - render( - - - - ) - - expect(screen.getByRole("tablist")).toBeInTheDocument() - expect(screen.getByRole("tab", { name: "Overview" })).toBeInTheDocument() - expect(screen.getByRole("tab", { name: "Patterns" })).toBeInTheDocument() - }) - - it('displays charts when data is available', async () => { - const mockAnalytics = { - patterns: { - daily: [{ date: "2024-01-01", totalTokens: 1000, operationCount: 5 }], - byOperation: [{ operationType: "chat", totalTokens: 800, percentage: 80 }], - byModel: [{ model: "gpt-4", totalTokens: 600, avgEfficiency: 120 }], - } - } - - jest.mocked(useUsageAnalytics).mockReturnValue({ - data: mockAnalytics, - isLoading: false - }) - - render( - - - - ) - - expect(screen.getByText("Usage Trends")).toBeInTheDocument() - }) -}) -``` - -```bash -# Run component tests: -pnpm test -- apps/desktop/layer/renderer/src/modules/settings/tabs/ai/usage -pnpm test -- apps/desktop/layer/renderer/src/modules/settings/tabs/token-usage -# If failing: Read error, understand root cause, fix code, re-run -``` - -### Level 3: Integration Test - -```bash -# Start the development server -pnpm --filter=@follow/desktop dev:web - -# Test AI settings page integration -# Navigate to http://localhost:5173/settings/ai -# Expected: New "Token Usage" section visible with progress ring and metrics - -# Test detailed analytics page -# Navigate to http://localhost:5173/settings/token-usage -# Expected: Enhanced page with tabs, charts, and real-time metrics - -# Test responsive design -# Resize browser window to mobile dimensions -# Expected: Charts stack vertically, components remain usable - -# Test real-time updates -# Keep page open for 30 seconds, observe data refresh -# Expected: Usage data updates automatically -``` - -### Level 4: Chart Rendering Validation - -```bash -# Test chart libraries -# Open browser developer tools, check for console errors -# Expected: No recharts-related errors, proper chart rendering - -# Test chart interactions -# Hover over chart elements, verify tooltips appear -# Click on legend items, verify chart responds -# Expected: Interactive elements work smoothly -``` - -### Level 5: API Integration Validation - -```bash -# Test API calls in Network tab -# Navigate to enhanced pages, verify API requests -# Expected: -# - GET /ai/chat/config calls successfully -# - GET /ai/analytics calls successfully -# - GET /ai/session/current calls when needed -# - Proper error handling for failed requests -``` - -## Final validation Checklist - -- [ ] All tests pass: `pnpm test` -- [ ] No linting errors: `pnpm lint` -- [ ] No type errors: `pnpm typecheck` -- [ ] Recharts dependency installed: `pnpm list recharts` -- [ ] AI settings page shows usage section: Navigate to /settings/ai -- [ ] Token usage page enhanced with charts: Navigate to /settings/token-usage -- [ ] Real-time updates working: Observe 30-second refresh intervals -- [ ] Charts render correctly: All visualizations display properly -- [ ] Mobile responsive: Components work on smaller screens -- [ ] Predictive warnings appear: Test with high usage scenarios -- [ ] Session tracking displays: Active sessions show correctly -- [ ] Navigation works: Links between simple/detailed views function -- [ ] Internationalization complete: All text uses translation keys -- [ ] Error states handled: Network failures show appropriate UI -- [ ] Loading states smooth: Skeleton screens display during data fetch -- [ ] Accessibility compliant: Screen reader and keyboard navigation work - ---- - -## Anti-Patterns to Avoid - -- ❌ Don't fetch chart data on every render - use React Query caching -- ❌ Don't ignore mobile responsive design - ensure charts work on small screens -- ❌ Don't hardcode colors - use UIKit color system throughout -- ❌ Don't skip loading states - users need visual feedback during data fetch -- ❌ Don't break existing AI settings layout - integrate cleanly with SettingBuilder -- ❌ Don't forget internationalization - all user text must use useTranslation -- ❌ Don't implement custom query logic - follow existing React Query patterns -- ❌ Don't ignore error boundaries - handle API failures gracefully -- ❌ Don't make charts inaccessible - include proper ARIA labels -- ❌ Don't pollute global styles - scope chart styles appropriately - -## Confidence Score: 8/10 - -This PRP provides comprehensive context for implementing enhanced AI usage observability frontend with: - -- ✅ Complete analysis of existing Follow frontend patterns and conventions -- ✅ Detailed component hierarchy with specific file structure and responsibilities -- ✅ React Query integration following project's established patterns -- ✅ Chart library integration with proper responsive design considerations -- ✅ Comprehensive testing strategy covering unit and integration scenarios -- ✅ Step-by-step implementation tasks with proper dependency management -- ✅ Clear validation steps with specific commands and expected outcomes -- ✅ Internationalization requirements and integration patterns - -The implementation should succeed with high confidence given the detailed context, existing codebase analysis, and thorough validation approach. The -2 points account for the complexity of chart library integration and potential responsive design edge cases that may require iteration. diff --git a/PRPs/entry-layouts-comprehensive-fixes.md b/PRPs/entry-layouts-comprehensive-fixes.md deleted file mode 100644 index aa7024d04..000000000 --- a/PRPs/entry-layouts-comprehensive-fixes.md +++ /dev/null @@ -1,577 +0,0 @@ -# PRP: Comprehensive Entry Content Layout Fixes - -## Overview - -Fix critical issues in the adaptive entry content layouts that prevent proper EntryHeader display, cause inconsistent layout patterns, and implement unwanted popup behaviors. This PRP addresses specific problems identified with SocialMedia, Pictures, and Videos layouts while also fixing entry navigation behavior. - -## Current Issues Analysis - -### Issue Context - -Following the implementation of adaptive entry content layouts in `PRPs/adaptive-entry-content-layouts.md` and `PRPs/adaptive-entry-layouts-fixes.md`, several critical issues remain that affect user experience: - -### 1. Social Media Layout - Blank EntryHeader Issue - -**File**: `apps/desktop/layer/renderer/src/modules/entry-content/components/layouts/SocialMediaLayout.tsx` -**Root Cause**: EntryHeader component explicitly hides all functionality for SocialMedia view - -**Current Implementation Problem**: - -```typescript -// EntryHeader.tsx lines 72-77 - Explicitly hides actions for SocialMedia -{view !== FeedViewType.SocialMedia && ( -
- - -
-)} -``` - -**Missing Functionality**: - -- Star/bookmark actions -- Share actions -- More actions menu (read/unread toggle, archive, etc.) -- Entry metadata display when scrolled -- Read status indicators - -**Additional Issue**: SocialMedia entries also use `
` tag instead of `NavLink` in `EntryItemWrapper.tsx:212`, preventing proper navigation. - -### 2. Pictures Layout - Wrong Layout Structure - -**File**: `apps/desktop/layer/renderer/src/modules/entry-content/components/layouts/PicturesLayout.tsx` - -**Current Structure**: Split-screen layout (image left, metadata sidebar right) - -```typescript -
-
{/* Image area */} -
{/* Sidebar */} -``` - -**Requested Structure**: Article-like layout (title/author top, content below) with multimedia separated from text - -### 3. Videos Layout - AI Summary Position - -**File**: `apps/desktop/layer/renderer/src/modules/entry-content/components/layouts/VideosLayout.tsx` - -**Current Order**: - -1. EntryTitle -2. AuthorHeader -3. ContentBody -4. AISummary ← Currently at bottom - -**Requested Order**: AI Summary should be above ContentBody (between AuthorHeader and ContentBody) - -### 4. Video Entry Popup Logic - -**File**: `apps/desktop/layer/renderer/src/modules/entry-column/Items/video-item.tsx` - -**Current Behavior**: Lines 104-126 - Click triggers modal popup instead of navigation - -```typescript -onClick={(e) => { - if (iframeSrc) { - modalStack.present({ - title: "", - content: (props) => (), - }) - } else { - previewMedia(entry.media) // Triggers video preview popup - } -}} -``` - -### 5. Picture Entry Popup Logic - -**File**: `apps/desktop/layer/renderer/src/modules/entry-column/Items/picture-item.tsx` - -**Current Behavior**: Lines 46-48 - SwipeMedia triggers preview popup - -```typescript - { - previewMedia(media, i) // Triggers picture preview popup instead of navigation - }} -/> -``` - -## Proposed Solutions - -### 1. Social Media Layout Fixes - -#### 1.1 Restore EntryHeader Functionality - -**Strategy**: Remove the SocialMedia exclusion from EntryHeader component and ensure proper integration - -**File Changes**: - -- `EntryHeader.tsx`: Remove `view !== FeedViewType.SocialMedia` condition -- `EntryItemWrapper.tsx`: Change SocialMedia from `
` to `NavLink` like other entry types -- `SocialMediaLayout.tsx`: Ensure layout works with EntryHeader display - -#### 1.2 Layout Integration Pattern - -Follow the same pattern as ArticleLayout: - -```typescript -// EntryContent.tsx integration (lines 120-127) -{!isInPeekModal && ( - -)} -// Then SocialMediaLayout renders below EntryHeader -``` - -### 2. Pictures Layout Restructuring - -#### 2.1 Change to Article-Style Layout - -**New Structure**: Title/Author on top, content below, with multimedia content separated - -```typescript -// New PicturesLayout structure -
- {/* Title and Author Section */} -
- - -
- - {/* Multimedia Content Section */} -
- -
- - {/* Text Content Section */} -
- - -
-
-``` - -#### 2.2 Multimedia Content Separation - -- Use `noMedia={true}` on ContentBody to remove multimedia elements from text -- Display multimedia content separately in dedicated MediaGallery section -- Maintain click-to-preview functionality via `usePreviewMedia` hook - -### 3. Videos Layout - AI Summary Position Fix - -**Simple Reordering**: Move AISummary component above ContentBody - -```typescript -// Current order in VideosLayout.tsx (lines 49-72) - - - {/* Move here - above content */} - -``` - -### 4. Remove Video Entry Popup Logic - -#### 4.1 Update Video Item Click Handler - -**File**: `video-item.tsx` -**Change**: Remove modal popup logic, allow normal entry navigation - -```typescript -// Replace current onClick handler (lines 104-126) with normal entry behavior -// Remove the modal presentation logic entirely -// Let EntryItemWrapper handle navigation naturally -``` - -#### 4.2 Preserve Video Preview in Layout - -**Note**: Users can still preview videos via the VideosLayout which shows the video player properly - -### 5. Remove Picture Entry Popup Logic - -#### 5.1 Update Picture Item OnPreview Handler - -**File**: `picture-item.tsx` -**Change**: Remove `onPreview` prop from SwipeMedia to disable popup behavior - -```typescript -// Remove onPreview prop from SwipeMedia (lines 46-48) - previewMedia(media, i)} // Remove this line - className="aspect-square w-full overflow-hidden rounded-md" -/> -``` - -#### 5.2 Maintain Preview in PicturesLayout - -**Note**: Users can still preview images via the restructured PicturesLayout with proper MediaGallery integration - -## Implementation Plan - -### Phase 1: Fix Social Media Layout EntryHeader (Priority: Critical) - -#### Task 1.1: Remove SocialMedia Exclusion from EntryHeader - -**File**: `apps/desktop/layer/renderer/src/modules/entry-content/components/entry-header/EntryHeader.tsx` - -```typescript -// Remove condition: view !== FeedViewType.SocialMedia -// Lines 72-77: Always show EntryHeaderActions regardless of view type -
- - -
-``` - -#### Task 1.2: Fix SocialMedia Entry Navigation - -**File**: `apps/desktop/layer/renderer/src/modules/entry-column/layouts/EntryItemWrapper.tsx` - -```typescript -// Change line 212 from: -const Link = view === FeedViewType.SocialMedia ? "article" : NavLink -// To: -const Link = NavLink // Use NavLink for all entry types including SocialMedia -``` - -#### Task 1.3: Verify SocialMediaLayout Integration - -**File**: `apps/desktop/layer/renderer/src/modules/entry-content/components/layouts/SocialMediaLayout.tsx` - -- Ensure layout works properly when EntryHeader is displayed above it -- Test star collection overlay positioning doesn't conflict with EntryHeader -- Verify responsive behavior with EntryHeader present - -### Phase 2: Restructure Pictures Layout (Priority: High) - -#### Task 2.1: Implement Article-Style Layout Structure - -**File**: `apps/desktop/layer/renderer/src/modules/entry-content/components/layouts/PicturesLayout.tsx` - -- Replace split-screen layout with vertical article-style layout -- Move title and author to top section -- Create dedicated multimedia content section with MediaGallery -- Separate text content section with `noMedia={true}` - -#### Task 2.2: Update MediaGallery Integration - -- Ensure MediaGallery component supports click-to-preview functionality -- Maintain existing `usePreviewMedia` hook integration for modal previews -- Test multiple image handling and carousel behavior - -### Phase 3: Fix Videos Layout AI Summary Position (Priority: Medium) - -#### Task 3.1: Reorder Components - -**File**: `apps/desktop/layer/renderer/src/modules/entry-content/components/layouts/VideosLayout.tsx` - -- Move `` from line 72 to after AuthorHeader (around line 53) -- Maintain proper spacing and styling -- Test responsive behavior and content flow - -### Phase 4: Remove Popup Logic (Priority: Medium) - -#### Task 4.1: Remove Video Entry Popup Logic - -**File**: `apps/desktop/layer/renderer/src/modules/entry-column/Items/video-item.tsx` - -- Remove or simplify onClick handler (lines 104-126) -- Remove `PreviewVideoModalContent` modal presentation logic -- Allow normal entry navigation through EntryItemWrapper - -#### Task 4.2: Remove Picture Entry Popup Logic - -**File**: `apps/desktop/layer/renderer/src/modules/entry-column/Items/picture-item.tsx` - -- Remove `onPreview` prop from SwipeMedia component (lines 46-48) -- Test that normal entry navigation still works -- Verify pictures can still be previewed through PicturesLayout - -### Phase 5: Testing and Validation (Priority: High) - -#### Task 5.1: Layout Functionality Testing - -- Test EntryHeader actions work in SocialMedia layout (star, share, more actions) -- Test Pictures layout displays properly with article-style structure -- Test Videos layout shows AI summary in correct position -- Test normal entry navigation works for all layouts - -#### Task 5.2: Integration Testing - -- Test entry factory routing still works correctly -- Test mobile responsive behavior across all layout changes -- Test no regressions in ArticleLayout functionality -- Test modal preview functionality where preserved - -## Context & References - -### Codebase Architecture Understanding - -#### Entry Content System Files - -- **Factory**: `layouts/factory.ts` - Layout component selection -- **Integration**: `EntryContent.tsx` - Main integration point with EntryHeader -- **Layouts**: Individual layout components in `layouts/` directory -- **Entry Wrapper**: `EntryItemWrapper.tsx` - Controls navigation vs article tag usage - -#### Key Components to Modify - -- **EntryHeader.tsx**: Remove SocialMedia exclusion (lines 72-77) -- **EntryItemWrapper.tsx**: Fix SocialMedia navigation (line 212) -- **SocialMediaLayout.tsx**: Verify EntryHeader integration -- **PicturesLayout.tsx**: Complete restructure to article-style layout -- **VideosLayout.tsx**: Simple component reordering -- **video-item.tsx**: Remove popup click logic -- **picture-item.tsx**: Remove SwipeMedia onPreview prop - -#### Existing Patterns to Follow - -- **ArticleLayout**: Reference for proper EntryHeader integration -- **MediaGallery**: Pattern for multimedia content display -- **EntryHeaderActions**: Component providing star, share, more actions -- **usePreviewMedia**: Hook for maintaining preview functionality where appropriate - -### External Best Practices & Documentation - -#### React Component Restructuring - -- **Conditional Rendering Removal**: https://react.dev/learn/conditional-rendering -- **Component Composition**: https://react.dev/learn/passing-props-to-a-component -- **Layout Patterns**: Clean separation of content sections for better UX - -#### Entry Navigation Patterns - -- **React Router NavLink**: https://reactrouter.com/en/main/components/nav-link -- **Navigation vs Modal UX**: Best practices for content discovery vs preview - -#### Layout Design Principles - -- **Article Layout Standards**: Title → Author → Content flow for readability -- **Multimedia Content Separation**: Clear distinction between media and text content -- **Progressive Enhancement**: Maintain preview functionality while improving base navigation - -### UI/UX Reference Standards - -#### Social Media Layout - -- **Standard Pattern**: Avatar + Author info + Actions in header area -- **Content Flow**: Header actions → AI summary → Content → Media gallery -- **Action Accessibility**: All entry actions should be consistently available - -#### Pictures Layout - -- **Article-Style Pattern**: Linear flow from title → author → multimedia → text -- **Content Separation**: Clear visual separation between media and text sections -- **Preview Enhancement**: Click-to-preview maintains advanced image viewing - -#### Videos Layout - -- **Information Hierarchy**: AI summary provides context before content consumption -- **Content Flow**: Video → Title → Author → AI Summary → Description/Content - -## Validation Gates - -### Code Quality Checks - -```bash -# TypeScript validation -pnpm run typecheck - -# Linting validation -pnpm run lint:tsl -pnpm run lint - -# Code formatting -pnpm run format - -# Build validation -pnpm run build:web -``` - -### Functional Testing Checklist - -#### Social Media Layout Validation - -- [ ] EntryHeader displays with all action buttons (star, share, more actions menu) -- [ ] Social media entries navigate properly when clicked (no article tag behavior) -- [ ] Star/bookmark functionality works correctly -- [ ] Share actions function properly -- [ ] More actions menu provides read/unread, archive, etc. options -- [ ] Entry metadata displays when scrolled -- [ ] Layout integrates properly with EntryHeader space allocation - -#### Pictures Layout Validation - -- [ ] Title displays at top of layout -- [ ] Author information displays below title -- [ ] Multimedia content displays in dedicated section -- [ ] Text content displays separately without embedded media -- [ ] AI summary appears in text content section -- [ ] MediaGallery maintains click-to-preview functionality -- [ ] Layout is responsive and readable on all screen sizes - -#### Videos Layout Validation - -- [ ] AI summary displays above main content (after AuthorHeader) -- [ ] Video player displays properly at top -- [ ] Title and author information display correctly -- [ ] Text content excludes multimedia elements (`noMedia={true}`) -- [ ] Component spacing and flow is visually appealing - -#### Entry Navigation Validation - -- [ ] Video entries navigate to content view instead of showing popup -- [ ] Picture entries navigate to content view instead of showing popup -- [ ] Normal entry clicking behavior consistent across all entry types -- [ ] Preview functionality still available within content layouts -- [ ] No regressions in other entry types (Article, Audio, Notifications) - -### Performance & Integration Testing - -- [ ] No layout shift when EntryHeader is displayed with SocialMedia layout -- [ ] Entry factory routing continues to work seamlessly -- [ ] Modal preview functionality works when explicitly triggered -- [ ] Bundle size impact minimal (layout restructuring only) -- [ ] Mobile responsive design maintained across all layouts - -## Success Criteria - -### Primary Objectives - -- [ ] **Social Media**: EntryHeader displays with all actions like other entry types -- [ ] **Pictures**: Article-style layout (title/author top, content below) with multimedia separation -- [ ] **Videos**: AI summary positioned above main content -- [ ] **Navigation**: Entry clicks navigate to content view instead of triggering popups -- [ ] **Functionality**: All existing features preserved (preview, actions, etc.) - -### Quality Standards - -- [ ] Zero regressions in existing ArticleLayout functionality -- [ ] Entry factory system continues routing correctly -- [ ] All EntryHeader actions work consistently across layouts -- [ ] Mobile responsive design maintained -- [ ] Performance impact negligible - -### User Experience Improvements - -- [ ] Consistent action availability across all entry types (star, share, etc.) -- [ ] Better content organization with proper information hierarchy -- [ ] Improved navigation flow (click to view, preview when desired) -- [ ] Cleaner content separation between multimedia and text -- [ ] Maintained advanced preview functionality where appropriate - -## Risk Assessment & Mitigation - -### High-Risk Areas - -#### 1. EntryHeader Integration Changes - -**Risk**: Removing SocialMedia exclusion might break existing layout assumptions -**Mitigation**: - -- Test thoroughly with various social media entry types -- Verify responsive behavior with EntryHeader space allocation -- Implement feature flag for gradual rollout if needed - -#### 2. Navigation Behavior Changes - -**Risk**: Changing from `
` to `NavLink` might affect performance or behavior -**Mitigation**: - -- Profile performance impact of additional NavLink components -- Test keyboard navigation and accessibility compliance -- Verify no conflicts with existing event handlers - -#### 3. Pictures Layout Restructuring - -**Risk**: Complete layout change might break existing user expectations -**Mitigation**: - -- Maintain click-to-preview functionality for advanced image viewing -- Test with various image entry types and content lengths -- Consider user feedback mechanism for layout preference - -### Medium-Risk Areas - -#### Component Dependencies - -**Risk**: Changes to shared components might affect other areas -**Mitigation**: - -- Audit all usage of modified components before changes -- Use backward-compatible prop additions where possible -- Test all entry list item displays remain functional - -#### Preview Functionality - -**Risk**: Removing popup logic might eliminate desired preview features -**Mitigation**: - -- Preserve advanced preview functionality within content layouts -- Ensure MediaGallery and video players provide equivalent experience -- Test that preview modals still work when explicitly triggered - -### Rollback Strategy - -- Implement changes incrementally by layout type -- Use git feature branches for each major change -- Maintain existing layout components as backup during transition -- Feature flag capability for reverting individual layout changes - -## Additional Considerations - -### Accessibility Standards - -- Ensure EntryHeader actions maintain proper ARIA labels and keyboard navigation -- Test screen reader compatibility with restructured layouts -- Maintain proper heading hierarchy across all layout changes -- Verify video player controls remain keyboard accessible - -### Internationalization Support - -- Test all layout changes with RTL languages -- Ensure proper text flow in restructured Pictures layout -- Verify action button layouts work with translated text -- Maintain consistent translation key usage across layouts - -### Future Extensibility - -- Pattern established for other layout types (Audio, Notifications) -- EntryHeader integration approach reusable for new layout types -- Component separation supports future customization options -- Layout factory system ready for additional media types - -## Confidence Score: 8.5/10 - -This PRP provides a comprehensive solution with high implementation confidence: - -### Strengths - -- ✅ **Root Cause Analysis**: Identified exact source of each issue with file and line references -- ✅ **Proven Patterns**: Leverages existing successful EntryHeader and ArticleLayout patterns -- ✅ **Focused Scope**: Addresses specific reported issues without unnecessary complexity -- ✅ **Detailed Implementation**: Clear file-by-file changes with code examples -- ✅ **Risk Management**: Identified potential issues with concrete mitigation strategies -- ✅ **Comprehensive Testing**: Detailed validation steps for each change -- ✅ **External Research**: Incorporates best practices for navigation and layout design - -### Implementation Confidence Factors - -1. **Clear Problem Definition**: Each issue has been thoroughly analyzed with exact file locations -2. **Existing Foundation**: EntryHeader, ArticleLayout, and navigation patterns are already proven -3. **Minimal Breaking Changes**: Most changes involve removing exclusions or simple reordering -4. **Component Reuse**: Leveraging existing tested components rather than creating new ones -5. **Incremental Approach**: Changes can be implemented and tested independently -6. **Fallback Options**: Clear rollback strategy for each component change - -### Minor Risk Factors - -- **Layout Integration**: Need to verify EntryHeader spacing works with each layout type -- **Navigation Changes**: SocialMedia routing change requires thorough cross-platform testing -- **User Experience**: Pictures layout restructuring is most significant UX change - -The high confidence score reflects that this PRP addresses well-defined issues using existing proven components and patterns, with clear implementation steps and comprehensive validation approaches. diff --git a/PRPs/entry-layouts-refinement-fixes.md b/PRPs/entry-layouts-refinement-fixes.md deleted file mode 100644 index 1a9bdef28..000000000 --- a/PRPs/entry-layouts-refinement-fixes.md +++ /dev/null @@ -1,660 +0,0 @@ -# PRP: Entry Content Layout Refinement Fixes - -## Overview - -Address critical styling and functionality issues in the recently implemented adaptive entry content layouts. This PRP fixes three main areas: Social Media layout typography and avatar styling, Pictures layout carousel integration, and scroll container height calculation issues that cause content truncation. - -## Current Issues Analysis - -### Issue Context - -Following the implementation of adaptive entry content layouts in previous PRPs, user feedback has identified several refinement issues that affect the visual quality and functionality of the layouts. - -### 1. Social Media Layout - Typography and Avatar Issues - -**File**: `apps/desktop/layer/renderer/src/modules/entry-content/components/layouts/SocialMediaLayout.tsx` - -**Issue 1.1: Author Name Text Size** - -```typescript -// Current AuthorHeader.tsx implementation (lines 64-67) -
- - - -``` - -**Problem**: Author name uses `text-sm` (14px) which is too small for social media context where author prominence is important. - -**Issue 1.2: Avatar Border Radius Scale Mismatch** - -```typescript -// Current SocialMediaLayout.tsx (line 59) - - -// Current FeedIcon.tsx avatar styling (line 283) - -``` - -**Problem**: Avatar size increased to 48px but border radius remains `rounded-sm` (2px), creating visual inconsistency. - -### 2. Pictures Layout - Carousel Implementation Issues - -**File**: `apps/desktop/layer/renderer/src/modules/entry-content/components/layouts/PicturesLayout.tsx` - -**Issue 2.1: Multiple Image Display** - -```typescript -// Current MediaGallery.tsx implementation (lines 47-113) -if (isAllMediaSameRatio) { - return ( -
- {media.map((mediaItem, i, mediaList) => { - // Multiple images displayed side by side -``` - -**Problem**: MediaGallery shows multiple images side by side instead of single image with carousel navigation. - -**Issue 2.2: Missing PreviewMediaContent Integration** -**Expected**: Single image display that opens PreviewMediaContent modal on click with carousel functionality. -**Current**: Direct MediaGallery display without modal integration. - -**Issue 2.3: Entry List Click Behavior Not Restored** -**Problem**: Previous fixes removed SwipeMedia `onPreview` prop but didn't properly restore the expected clicking behavior for entry list images. - -### 3. Scroll Container Height Calculation Issues - -**Files**: - -- `packages/internal/components/src/ui/scroll-area/ScrollArea.tsx` -- `apps/desktop/layer/renderer/src/modules/entry-content/components/entry-content/EntryContent.tsx` - -**Issue 3.1: Height Mismatch** - -```typescript -// EntryContent.tsx ScrollArea usage (lines 242-244) -rootClassName={cn( - "h-0 min-w-0 grow overflow-y-auto print:h-auto print:overflow-visible", - className, -)} -``` - -**Problem**: `h-0 grow` pattern causes height calculation issues where content extends beyond scroll container. - -**Issue 3.2: Flex Container Min-Height Problem** -**Root Cause**: Flexbox containers with implicit minimum size cause scroll containers to not properly calculate their available height. - -## Proposed Solutions - -### 1. Social Media Layout Typography and Avatar Fixes - -#### 1.1 Increase Author Name Font Size - -**Strategy**: Update AuthorHeader to use larger text size for social media context - -**Implementation**: - -```typescript -// AuthorHeader.tsx - Enhanced author name styling -
- {/* Changed from text-sm */} - - -``` - -#### 1.2 Scale Avatar Border Radius Appropriately - -**Strategy**: Use size-responsive border radius that scales with avatar dimensions - -**Implementation**: - -```typescript -// FeedIcon.tsx - Size-based border radius -const getBorderRadius = (size: number) => { - if (size <= 24) return "rounded-sm" // 2px for small avatars - if (size <= 32) return "rounded-md" // 6px for medium avatars - if (size <= 48) return "rounded-lg" // 8px for large avatars - return "rounded-xl" // 12px for extra large avatars -} - - -``` - -### 2. Pictures Layout Single Image Carousel Implementation - -#### 2.1 Replace MediaGallery with Single Image Display - -**Strategy**: Show only the first image with click-to-carousel functionality using PreviewMediaContent - -**Implementation Pattern**: - -```typescript -// PicturesLayout.tsx - Single image with carousel -const PicturesLayout: React.FC = ({ entryId, ... }) => { - const entry = useEntry(entryId, (state) => ({ media: state.media })) - const previewMedia = usePreviewMedia() - - const handleImageClick = () => { - if (entry?.media && entry.media.length > 0) { - previewMedia( - entry.media.map(m => ({ - url: m.url, - type: m.type, - blurhash: m.blurhash, - fallbackUrl: m.preview_image_url - })), - 0 // Start at first image - ) - } - } - - return ( -
- {/* Single image display */} - {entry?.media && entry.media.length > 0 && ( -
-
- - {entry.media.length > 1 && ( -
- +{entry.media.length - 1} more -
- )} -
-
- )} - {/* Rest of content */} -
- ) -} -``` - -#### 2.2 Restore Entry List Image Click Behavior - -**Strategy**: Re-add proper onPreview functionality to SwipeMedia in entry list contexts - -**Implementation**: - -```typescript -// picture-item.tsx - Restore preview functionality - { - previewMedia( - media.map(m => ({ - url: m.url, - type: m.type, - blurhash: m.blurhash, - fallbackUrl: m.preview_image_url - })), - i - ) - }} -/> -``` - -### 3. Scroll Container Height Fixes - -#### 3.1 Apply Min-Height Fix to Flex Containers - -**Strategy**: Add explicit `min-height: 0` to flex containers to fix height calculation - -**Implementation**: - -```typescript -// EntryContent.tsx - Fix flex container height calculation -
{/* Add min-h-0 */} - - {children} - -
-``` - -#### 3.2 Update ScrollArea Component for Better Height Handling - -**Strategy**: Enhance ScrollArea component to handle flex container height issues - -**Implementation**: - -```typescript -// ScrollArea.tsx - Enhanced flex support -export const ScrollArea = ({ - flex, - rootClassName, - viewportClassName, - ...props -}) => { - return ( - - - div]:!flex [&>div]:!flex-col [&>div]:!min-h-0", // Add min-h-0 to flex children - viewportClassName - )} - // ... rest of props - > - {children} - - - - ) -} -``` - -## Implementation Tasks - -### Phase 1: Social Media Layout Typography and Avatar Fixes (Priority: High) - -#### Task 1.1: Update AuthorHeader Font Sizes - -**File**: `apps/desktop/layer/renderer/src/modules/entry-content/components/layouts/shared/AuthorHeader.tsx` - -- Change author name from `text-sm` to `text-base` for better prominence -- Ensure consistency across all AuthorHeader usages -- Test with different author name lengths - -#### Task 1.2: Implement Size-Responsive Avatar Border Radius - -**File**: `apps/desktop/layer/renderer/src/modules/feed/feed-icon/FeedIcon.tsx` - -- Create `getBorderRadius()` utility function based on avatar size -- Update AvatarImage className to use responsive border radius -- Test with different avatar sizes (20px, 32px, 48px, 64px) - -#### Task 1.3: Update SocialMediaLayout Avatar Integration - -**File**: `apps/desktop/layer/renderer/src/modules/entry-content/components/layouts/SocialMediaLayout.tsx` - -- Verify 48px avatar size works with new border radius -- Test visual consistency with typography changes -- Ensure proper spacing and alignment - -### Phase 2: Pictures Layout Single Image Carousel (Priority: High) - -#### Task 2.1: Replace MediaGallery with Single Image Display - -**File**: `apps/desktop/layer/renderer/src/modules/entry-content/components/layouts/PicturesLayout.tsx` - -- Remove MediaGallery usage -- Implement single image display with first image from media array -- Add click handler for PreviewMediaContent modal -- Add visual indicator for multiple images ("+X more" overlay) - -#### Task 2.2: Create Picture-Specific Media Component - -**File**: `apps/desktop/layer/renderer/src/modules/entry-content/components/layouts/shared/SingleImageDisplay.tsx` - -- Create reusable single image display component -- Include click-to-carousel functionality -- Handle edge cases (no images, single image, multiple images) -- Support different aspect ratios and sizing - -#### Task 2.3: Restore Entry List Image Click Behavior - -**File**: `apps/desktop/layer/renderer/src/modules/entry-column/Items/picture-item.tsx` - -- Re-add `onPreview` prop to SwipeMedia component -- Ensure proper media format for PreviewMediaContent -- Test clicking behavior in entry list vs content view -- Verify no conflicts with entry navigation - -### Phase 3: Scroll Container Height Fixes (Priority: Critical) - -#### Task 3.1: Apply Min-Height Fixes to Entry Content - -**File**: `apps/desktop/layer/renderer/src/modules/entry-content/components/entry-content/EntryContent.tsx` - -- Add `min-h-0` classes to flex containers -- Update ScrollArea usage to include flex height fixes -- Test with different content lengths and screen sizes -- Verify fix applies to all layout types (Social Media, Videos, Articles, Pictures) - -#### Task 3.2: Enhance ScrollArea Component - -**File**: `packages/internal/components/src/ui/scroll-area/ScrollArea.tsx` - -- Add explicit `min-h-0` support for flex contexts -- Update flex child selectors to include min-height fixes -- Ensure backward compatibility with existing usage -- Test across different scroll scenarios - -#### Task 3.3: Test Scroll Fixes Across All Layouts - -**Files**: All layout components - -- Test Social Media layout with long content -- Test Video layout with long descriptions -- Test Articles layout for regressions -- Test Pictures layout after restructuring -- Verify mobile responsive behavior - -### Phase 4: Integration Testing and Polish (Priority: Medium) - -#### Task 4.1: Cross-Layout Consistency Testing - -- Verify typography consistency across all layouts -- Test avatar sizing consistency in different contexts -- Ensure PreviewMediaContent modal works consistently -- Test scroll behavior across all entry types - -#### Task 4.2: Performance and Accessibility Testing - -- Verify no performance regressions with scroll fixes -- Test keyboard navigation with new carousel behavior -- Ensure proper focus management in PreviewMediaContent -- Test screen reader compatibility - -## Context & References - -### Codebase Architecture Understanding - -#### Core Components to Modify - -- **AuthorHeader.tsx**: Typography improvements for author names -- **FeedIcon.tsx**: Size-responsive border radius implementation -- **PicturesLayout.tsx**: Single image display with carousel integration -- **ScrollArea.tsx**: Flex container height calculation fixes -- **EntryContent.tsx**: Scroll container height fixes -- **picture-item.tsx**: Restore image click preview behavior - -#### Existing Patterns to Follow - -- **PreviewMediaContent.tsx**: Modal-based media carousel (Embla carousel integration) -- **usePreviewMedia**: Hook for triggering media preview modals -- **Media component**: Image display with loading states and proxy support -- **UIKit color system**: Consistent with `text-base`, `rounded-lg` etc. - -### External Best Practices & Documentation - -#### CSS Flexbox Height Calculation Fixes - -- **Min-Height Zero Pattern**: https://stackoverflow.com/questions/21515042/scrolling-a-flexbox-with-overflowing-content -- **Flex Container Overflow**: https://moduscreate.com/blog/how-to-fix-overflow-issues-in-css-flex-layouts/ -- **Critical Insight**: "There's a special case where the min-height of flex items defaults to the content size. We have to explicitly set the min height to zero." - -#### React Image Carousel Best Practices - -- **Single Image Display**: https://cloudinary.com/blog/add-a-responsive-image-carousel-to-your-react-app -- **Click Navigation**: Custom controls with callback functions and hasNext/hasPrev state -- **Responsive Design**: Automatic layout adjustment based on screen size -- **Performance**: Lazy loading and video play control on selected slides - -#### Typography and Avatar Scaling - -- **Responsive Avatar Sizing**: Border radius should scale proportionally with avatar size -- **Social Media Typography**: Author names should be prominent (16px+ / text-base) for readability -- **Apple UIKit Standards**: Consistent size relationships (border radius = size/6 to size/4) - -### Implementation Guidelines - -#### Typography Scaling Pattern - -```typescript -// Size-based styling patterns -const getFontSize = (context: "social" | "article" | "compact") => { - switch (context) { - case "social": - return "text-base font-semibold" // 16px for prominence - case "article": - return "text-sm font-semibold" // 14px for articles - case "compact": - return "text-xs font-medium" // 12px for compact - } -} -``` - -#### Border Radius Scaling Pattern - -```typescript -// Size-responsive border radius -const getBorderRadius = (size: number) => { - const ratio = size / 8 // Scale factor - if (ratio <= 3) return "rounded-sm" // 2px - if (ratio <= 4) return "rounded-md" // 6px - if (ratio <= 6) return "rounded-lg" // 8px - return "rounded-xl" // 12px -} -``` - -#### Single Image Carousel Pattern - -```typescript -// Media display with carousel integration -const handleImageClick = useCallback(() => { - if (mediaArray.length > 0) { - previewMedia( - mediaArray.map(transformMediaForPreview), - 0, // Initial index - ) - } -}, [mediaArray, previewMedia]) -``` - -## Validation Gates - -### Code Quality Checks - -```bash -# TypeScript validation -pnpm run typecheck - -# Linting validation -pnpm run lint:tsl -pnpm run lint - -# Code formatting -pnpm run format - -# Build validation -pnpm run build:web -``` - -### Functional Testing Checklist - -#### Social Media Layout Validation - -- [ ] Author name displays with `text-base` font size (16px) -- [ ] Author name maintains proper font weight and color -- [ ] Avatar border radius scales appropriately with 48px size -- [ ] Avatar maintains circular appearance with proper radius -- [ ] Typography is consistent across different author name lengths -- [ ] Layout maintains proper spacing and alignment - -#### Pictures Layout Validation - -- [ ] Single image displays instead of multiple images side by side -- [ ] First image from media array is shown as primary display -- [ ] "+X more" indicator appears when multiple images exist -- [ ] Clicking single image opens PreviewMediaContent modal -- [ ] Carousel navigation works properly in modal (left/right arrows) -- [ ] Image maintains proper aspect ratio and scaling -- [ ] No layout shift when transitioning to/from modal - -#### Entry List Image Click Validation - -- [ ] Clicking images in picture-item triggers PreviewMediaContent modal -- [ ] Modal opens with correct initial image and media array -- [ ] Entry navigation still works when clicking non-image areas -- [ ] No conflicts between image click and entry click behaviors -- [ ] Touch gestures work properly on mobile devices - -#### Scroll Container Height Validation - -- [ ] Long content no longer gets cut off at bottom in Social Media layout -- [ ] Long content no longer gets cut off at bottom in Video layout -- [ ] ScrollArea height matches available container height -- [ ] Content scrolls smoothly to very bottom without truncation -- [ ] No visual glitches or layout shifts during scrolling -- [ ] Fix applies to all entry types (Articles, Social Media, Videos, Pictures) -- [ ] Mobile responsive behavior maintains proper scrolling - -### Performance & Integration Testing - -- [ ] No layout shift when switching between entry types -- [ ] PreviewMediaContent modal opens smoothly without lag -- [ ] Image loading states display properly -- [ ] Scroll performance maintained with height fixes -- [ ] No memory leaks with modal-based image preview -- [ ] Typography rendering remains crisp at all zoom levels - -## Success Criteria - -### Primary Objectives - -- [ ] **Social Media**: Author names display prominently with proper typography -- [ ] **Social Media**: Avatar border radius scales appropriately with size -- [ ] **Pictures**: Single image display with click-to-carousel functionality -- [ ] **Pictures**: Entry list image clicking restored and working properly -- [ ] **Scroll Heights**: Long content fully visible without truncation in all layouts -- [ ] **Performance**: All fixes implemented without performance regression - -### Quality Standards - -- [ ] Zero regressions in existing ArticleLayout functionality -- [ ] All layout types maintain consistent typography patterns -- [ ] PreviewMediaContent modal integration works seamlessly -- [ ] ScrollArea height calculation robust across different content types -- [ ] Mobile responsive design maintained across all fixes - -### User Experience Improvements - -- [ ] Social media author information more readable and prominent -- [ ] Picture viewing experience enhanced with proper carousel integration -- [ ] Content consumption improved with full scroll accessibility -- [ ] Visual consistency improved with proper avatar styling -- [ ] Interaction patterns consistent between entry list and content view - -## Risk Assessment & Mitigation - -### High-Risk Areas - -#### 1. ScrollArea Height Calculation Changes - -**Risk**: Height fixes might break existing scroll behavior in other components -**Mitigation**: - -- Apply changes incrementally with thorough testing -- Maintain backward compatibility with existing ScrollArea usage -- Test all scroll containers across the application -- Use feature flags for gradual rollout if needed - -#### 2. PreviewMediaContent Modal Integration - -**Risk**: Modal stacking conflicts or keyboard navigation issues -**Mitigation**: - -- Test modal behavior with existing modal stack system -- Verify proper cleanup of modal state -- Ensure keyboard navigation (ESC, arrows) works correctly -- Test touch gestures on mobile devices - -#### 3. Typography Changes Impact - -**Risk**: Font size changes might break layout in compact or mobile views -**Mitigation**: - -- Test with various author name lengths and special characters -- Verify responsive behavior across all screen sizes -- Check international character rendering (CJK, Arabic, etc.) -- Maintain proper line height and spacing - -### Medium-Risk Areas - -#### Component Dependencies - -**Risk**: Changes to shared components might affect other features -**Mitigation**: - -- Audit all usage of modified components before changes -- Use backward-compatible prop additions -- Test AuthorHeader and FeedIcon usage across application -- Verify no breaking changes to component APIs - -#### Image Loading Performance - -**Risk**: Single image display might affect loading performance -**Mitigation**: - -- Maintain existing lazy loading behavior -- Test image proxy functionality with new implementation -- Profile image loading performance before and after changes -- Ensure proper loading states and error handling - -### Rollback Strategy - -- Implement changes as separate commits for easy reversal -- Use CSS custom properties for easy typography adjustments -- Maintain original component implementations as backup -- Feature flag capability for critical scroll height fixes - -## Additional Considerations - -### Accessibility Standards - -- Ensure author names maintain proper contrast ratios with larger text -- Test screen reader compatibility with typography changes -- Verify keyboard navigation works properly with image carousel -- Maintain proper heading hierarchy across layout changes - -### Internationalization Support - -- Test typography changes with different language character sets -- Ensure proper text flow with RTL languages -- Verify avatar and text spacing works with longer translated text -- Maintain consistent translation key usage across components - -### Future Extensibility - -- Typography pattern scalable to other layout types -- Border radius pattern reusable for other avatar contexts -- ScrollArea fixes applicable to other scroll containers -- Image carousel pattern extensible to video content - -## Confidence Score: 9/10 - -This PRP provides a comprehensive solution with high implementation confidence: - -### Strengths - -- ✅ **Root Cause Analysis**: Identified exact source of each issue with specific file/line references -- ✅ **Proven External Research**: Incorporates CSS flexbox best practices and React carousel patterns -- ✅ **Focused Scope**: Addresses specific reported issues without unnecessary complexity -- ✅ **Detailed Implementation**: Clear component-by-component changes with code examples -- ✅ **Risk Management**: Identified potential issues with concrete mitigation strategies -- ✅ **Comprehensive Testing**: Detailed validation steps for each change -- ✅ **Existing Pattern Leverage**: Uses established components (PreviewMediaContent, usePreviewMedia) - -### Implementation Confidence Factors - -1. **Clear Problem Definition**: Each issue traced to specific code locations with examples -2. **External Best Practices**: CSS flexbox fixes and React carousel patterns are well-established -3. **Component Reuse**: Leveraging existing PreviewMediaContent rather than building new carousel -4. **Incremental Approach**: Changes can be implemented and tested independently -5. **Minimal Breaking Changes**: Most fixes involve styling adjustments and proper component usage -6. **Established Patterns**: Typography and avatar sizing follow existing codebase conventions - -### Minor Risk Areas - -- **ScrollArea Changes**: Need thorough testing across different content types -- **Typography Impact**: Must verify responsive behavior with larger text -- **Modal Integration**: Ensure smooth interaction with existing modal stack - -The high confidence score reflects that this PRP addresses well-defined styling and functional issues using established patterns and external best practices, with clear implementation steps and comprehensive validation approaches. diff --git a/PRPs/entry-modal-to-routing.md b/PRPs/entry-modal-to-routing.md deleted file mode 100644 index ea9528628..000000000 --- a/PRPs/entry-modal-to-routing.md +++ /dev/null @@ -1,278 +0,0 @@ -# PRP: Entry Modal to Routing Refactoring - -## Overview - -Refactor the entry content display system from a full-page modal approach to a scoped, column-based routing approach. This will improve user experience by keeping the modal scoped to the entry list column and make the entry content display reflect in the URL for better navigation. - -## Current Implementation Analysis - -### Current Architecture - -- **Three-column layout**: Feed list (left) → Entry list (center) → AI chat (right) -- **Main layout**: `MainDestopLayout` → `SubscriptionColumnContainer` + `` (`CenterColumnLayout`) -- **Entry list**: Located in `CenterColumnLayout` which contains `EntryColumn` and `AIChatLayout` -- **Modal system**: Uses `usePeekModal` hook to display entry content in a full-page modal - -### Key Files & Components - -#### Current Modal Implementation - -- **`usePeekModal.tsx`**: Hook that creates full-page modal using `PeekModal` component -- **`EntryItemWrapper.tsx:126`**: Click handler calls `peekModal(entry.id, "modal")` instead of navigation -- **`PeekModal` component**: Renders modal with `modalClassName` covering entire viewport - -#### Routing Infrastructure - -- **`useNavigateEntry.ts`**: Contains `getNavigateEntryPath()` function that generates proper entry URLs -- **Route structure**: `/timeline/:timelineId/:feedId/:entryId` -- **Current entry routing**: - - Layout: `/timeline/[timelineId]/[feedId]/layout.tsx` → `CenterColumnLayout` - - Index: `/timeline/[timelineId]/[feedId]/index.tsx` → redirects to `ROUTE_ENTRY_PENDING` - - Entry: `/timeline/[timelineId]/[feedId]/[entryId]/index.tsx` → `EntryLayoutContent` - -#### Layout Components - -- **`CenterColumnLayout.tsx`**: Two-column layout with entry list and AI chat -- **`EntryLayoutContent.tsx`**: Renders entry content when entryId is present in URL -- **`MainDestopLayout.tsx`**: Root layout with feed column and main content outlet - -### Current Flow - -1. User clicks entry in `EntryItemWrapper` -2. `handleClick` calls `peekModal(entry.id, "modal")` -3. Modal covers entire viewport, disrupting three-column layout -4. Entry content not reflected in URL routing - -## Proposed Solution - -### Architecture Changes - -Transform the entry content display from a modal overlay to a proper routing-based approach that renders within the entry column scope. - -### New Flow - -1. User clicks entry in `EntryItemWrapper` -2. `handleClick` calls `navigateEntry()` with `getNavigateEntryPath()` -3. URL changes to `/timeline/:timelineId/:feedId/:entryId` -4. Entry content renders within entry column boundaries via `` - -### Key Modifications - -#### 1. Modify Entry Click Handler - -**File**: `apps/desktop/layer/renderer/src/modules/entry-column/layouts/EntryItemWrapper.tsx:115-129` - -Replace modal invocation with navigation: - -```typescript -// BEFORE -const handleClick = useCallback( - (e) => { - // ... existing logic - peekModal(entry.id, "modal") - }, - [peekModal], -) - -// AFTER -const handleClick = useCallback( - (e) => { - // ... existing logic - navigateEntry({ entryId: entry.id }) - }, - [navigateEntry], -) -``` - -#### 2. Update CenterColumnLayout for Entry Display - -**File**: `apps/desktop/layer/renderer/src/modules/app-layout/timeline-column/CenterColumnLayout.tsx` - -Add conditional rendering for entry content within the entry column: - -```typescript -// Current: Simple two-column layout -
- -
- -// Proposed: Conditional entry content display -
- - {/* Renders entry content when entryId is present */} -
-``` - -#### 3. Create Column-Scoped Entry Layout - -**New file**: `apps/desktop/layer/renderer/src/modules/entry-column/EntryColumnLayout.tsx` - -```typescript -export const EntryColumnLayout = () => { - const { entryId } = useParams() - const navigate = useNavigateEntry() - - if (!entryId || entryId === ROUTE_ENTRY_PENDING) { - return - } - - return ( -
- {/* Entry list background */} - - - {/* Entry content overlay within column */} -
- navigate({ entryId: null })} - /> -
-
- ) -} -``` - -#### 4. Update Route Configuration - -**File**: `apps/desktop/layer/renderer/src/pages/(main)/(layer)/timeline/[timelineId]/[feedId]/layout.tsx` - -Change from `CenterColumnLayout` to new scoped layout: - -```typescript -// BEFORE -export { CenterColumnLayout as Component } from "~/modules/app-layout/timeline-column/index" - -// AFTER -export { EntryColumnLayout as Component } from "~/modules/entry-column/EntryColumnLayout" -``` - -## Implementation Tasks - -### Phase 1: Core Routing Setup - -1. **Create EntryColumnLayout component** - - Implement conditional rendering for entry content - - Add proper scoping within entry column boundaries - - Handle entry close navigation - -2. **Update CenterColumnLayout route** - - Replace component export in layout.tsx - - Ensure proper outlet rendering - -3. **Modify EntryItemWrapper click handler** - - Replace `peekModal` call with `navigateEntry` - - Import and use existing navigation utilities - -### Phase 2: Layout & Styling - -4. **Style scoped entry content** - - Ensure entry content stays within column boundaries - - Add proper animations for entry open/close - - Handle responsive behavior - -5. **Update entry content close behavior** - - Remove modal-specific close logic - - Implement navigation-based close - -### Phase 3: Testing & Validation - -6. **Test navigation flow** - - Verify URL changes correctly - - Test browser back/forward navigation - - Validate entry content display - -7. **Test edge cases** - - Direct URL access to entry routes - - Entry not found scenarios - - Mobile responsiveness - -## Context & References - -### Existing Patterns - -- **Navigation utilities**: `useNavigateEntry`, `getNavigateEntryPath` already exist -- **Route structure**: Entry routing infrastructure already present -- **Entry content component**: `EntryContent` component already handles rendering - -### External Documentation - -- **React Router Modal Patterns**: https://blog.logrocket.com/building-react-modal-module-with-react-router/ -- **Background Location Pattern**: https://dev.to/unorthodev/how-to-make-routable-modals-in-react-with-react-router-3hgp -- **Scoped Navigation**: https://github.com/remix-run/react-router/discussions/9601 - -### Best Practices Applied - -1. **Nested Route Approach**: Use existing route structure with outlets -2. **Scoped Rendering**: Keep entry content within column boundaries -3. **Persistent Navigation**: URL reflects entry selection state -4. **Smooth Transitions**: Maintain existing animation patterns - -## Validation Gates - -### Code Quality - -```bash -# TypeScript validation -pnpm run typecheck - -# Linting -pnpm run lint - -# Build validation -pnpm run build:web -``` - -### Functional Testing - -```bash -# Manual testing checklist -1. Click entry in list → URL updates → Entry content displays in column -2. Browser back/forward → Entry content appears/disappears correctly -3. Direct URL access → Entry content renders properly -4. Entry close action → Returns to entry list with correct URL -5. Mobile responsiveness → Entry content adapts to smaller screens -``` - -### Performance Validation - -- No increase in bundle size -- Smooth animations maintained -- No layout shifts during entry display - -## Success Criteria - -- [ ] Entry clicks trigger navigation instead of modal -- [ ] Entry content displays within column boundaries -- [ ] URL reflects entry selection state -- [ ] Browser navigation works correctly -- [ ] Entry close returns to proper state -- [ ] All existing functionality preserved -- [ ] No visual regression in three-column layout - -## Risk Mitigation - -### Potential Issues - -1. **Layout disruption**: Entry content might overflow column boundaries -2. **Animation conflicts**: Existing animations might conflict with new approach -3. **Mobile compatibility**: Column-scoped display might not work on mobile - -### Mitigation Strategies - -1. **Absolute positioning**: Use absolute positioning to contain entry content -2. **CSS containment**: Use CSS containment properties for layout isolation -3. **Responsive design**: Implement mobile-specific fallbacks if needed - -## Confidence Score: 8/10 - -This PRP provides a comprehensive approach with: - -- ✅ Detailed analysis of current implementation -- ✅ Clear technical specifications -- ✅ Existing code patterns leveraged -- ✅ External best practices incorporated -- ✅ Executable validation steps -- ✅ Risk mitigation strategies - -The high confidence comes from the existing navigation infrastructure and clear understanding of the current modal system. The main implementation involves connecting existing pieces rather than building from scratch. diff --git a/PRPs/ratio-based-mixing.md b/PRPs/ratio-based-mixing.md deleted file mode 100644 index b2e701017..000000000 --- a/PRPs/ratio-based-mixing.md +++ /dev/null @@ -1,397 +0,0 @@ -# PRP: Ratio-Based Color Mixing Tailwind Plugin - -## Project Overview - -This PRP defines the implementation of a Tailwind CSS plugin that provides ratio-based syntax for color mixing as an alternative to lengthy `color-mix()` CSS declarations. The plugin will transform shortened syntax like `bg-mix-accent/background-7/3` into native CSS `color-mix()` functions. - -## Current Problem Analysis - -### Existing Usage Patterns - -The codebase currently uses verbose arbitrary value syntax for color mixing: - -```css -/* Current verbose syntax - 137 characters */ -bg-[color-mix(in_srgb,hsl(var(--fo-a)),hsl(var(--background))_70%)] - -/* Complex nested mixing - 172 characters */ -bg-[color-mix(in_srgb,_color-mix(in_srgb,rgb(var(--color-red)),hsl(var(--background))_80%),transparent_30%)] -``` - -**Found Usage Locations:** - -- `/apps/desktop/layer/renderer/src/modules/ai-chat/components/message/UserChatMessage.tsx:137` -- `/apps/desktop/layer/renderer/src/modules/ai-chat/components/layouts/CollapsibleError.tsx:172` - -### Problems Identified - -1. **Readability**: Extremely long class names reduce code clarity -2. **Maintainability**: Hard to modify mixing ratios -3. **Performance**: Arbitrary values prevent CSS optimization -4. **Developer Experience**: No IntelliSense support -5. **Consistency**: No standardized mixing ratios across project - -## Solution Design - -### Ratio-Based Syntax Options - -**Primary Syntax (Recommended):** - -```css -/* Ratio format: color1/color2-ratio1/ratio2 */ -bg-mix-accent/background-7/3 /* 70% accent, 30% background */ -bg-mix-red/background-4/1 /* 80% red, 20% background */ -border-mix-blue/white-3/2 /* 60% blue, 40% white */ -text-mix-accent/background-9/1 /* 90% accent, 10% background */ -``` - -**Alternative Syntax (Percentage-based):** - -```css -bg-mix-accent-70 /* 70% accent, 30% background (implicit) */ -bg-mix-red-80 /* 80% red, 20% background (implicit) */ -``` - -**Generated CSS Output:** - -```css -.bg-mix-accent\/background-7\/3 { - background-color: color-mix(in srgb, hsl(var(--fo-a)) 70%, hsl(var(--background)) 30%); -} -``` - -## Technical Implementation - -### Plugin Architecture - -Based on existing codebase patterns in `/packages/configs/tailwindcss/web.ts`: - -```typescript -// New file: /packages/configs/tailwindcss/ratio-mixing-plugin.js -const plugin = require("tailwindcss/plugin") - -const ratioMixingPlugin = plugin.withOptions( - (options = {}) => { - return ({ addUtilities, theme, e }) => { - const config = { ...defaultConfig, ...options } - const utilities = {} - - // Generate ratio-based utilities - generateRatioBasedUtilities(utilities, config) - - // Generate percentage-based utilities (fallback) - generatePercentageBasedUtilities(utilities, config) - - addUtilities(utilities) - } - }, - (options) => { - return { - theme: { - // Theme extensions if needed - }, - } - }, -) - -module.exports = ratioMixingPlugin -``` - -### Configuration Schema - -```typescript -interface RatioMixingConfig { - colorSpace?: "srgb" | "hsl" | "oklab" | "oklch" - baseColors: { - [key: string]: string // CSS variable or color value - } - ratios?: { - [key: string]: [number, number] // [numerator, denominator] pairs - } - variants?: ("bg" | "border" | "text")[] - prefix?: string - implicitBackground?: string -} - -const defaultConfig: RatioMixingConfig = { - colorSpace: "srgb", - baseColors: { - background: "hsl(var(--background))", - accent: "hsl(var(--fo-a))", - red: "rgb(var(--color-red))", - // Map to existing theme colors from UIKit - }, - ratios: { - "1/1": [1, 1], // 50%/50% - "2/1": [2, 1], // 66.7%/33.3% - "3/1": [3, 1], // 75%/25% - "4/1": [4, 1], // 80%/20% - "7/3": [7, 3], // 70%/30% - "9/1": [9, 1], // 90%/10% - }, - variants: ["bg", "border", "text"], - prefix: "mix", - implicitBackground: "background", -} -``` - -### Core Implementation Functions - -```javascript -function generateRatioBasedUtilities(utilities, config) { - const { baseColors, ratios, variants, colorSpace } = config - - // Generate: bg-mix-accent/background-7/3 - Object.entries(baseColors).forEach(([color1Name, color1Value]) => { - Object.entries(baseColors).forEach(([color2Name, color2Value]) => { - if (color1Name === color2Name) return // Skip same color mixing - - Object.entries(ratios).forEach(([ratioKey, [num, denom]]) => { - const percentage1 = Math.round((num / (num + denom)) * 100) - const percentage2 = 100 - percentage1 - - variants.forEach((variant) => { - const className = `.${variant}-${config.prefix}-${color1Name}\\/${color2Name}-${ratioKey.replace("/", "\\/")}` - const property = getPropertyName(variant) - const mixedColor = `color-mix(in ${colorSpace}, ${color1Value} ${percentage1}%, ${color2Value} ${percentage2}%)` - - utilities[className] = { [property]: mixedColor } - }) - }) - }) - }) -} - -function generatePercentageBasedUtilities(utilities, config) { - // Generate: bg-mix-accent-70 (implicit background mixing) - const { baseColors, variants, colorSpace, implicitBackground } = config - const backgroundValue = baseColors[implicitBackground] - - const percentages = [5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 95] - - Object.entries(baseColors).forEach(([colorName, colorValue]) => { - if (colorName === implicitBackground) return - - percentages.forEach((percentage) => { - variants.forEach((variant) => { - const className = `.${variant}-${config.prefix}-${colorName}-${percentage}` - const property = getPropertyName(variant) - const mixedColor = `color-mix(in ${colorSpace}, ${colorValue} ${percentage}%, ${backgroundValue} ${100 - percentage}%)` - - utilities[className] = { [property]: mixedColor } - }) - }) - }) -} - -function getPropertyName(variant) { - switch (variant) { - case "bg": - return "background-color" - case "border": - return "border-color" - case "text": - return "color" - default: - return "background-color" - } -} -``` - -### Integration with Existing Config - -Update `/packages/configs/tailwindcss/web.ts`: - -```typescript -import ratioMixingPlugin from "./ratio-mixing-plugin" - -const twConfig = { - // ... existing config - plugins: [ - // ... existing plugins - ratioMixingPlugin({ - baseColors: { - background: "hsl(var(--background))", - accent: "hsl(var(--fo-a))", - red: "rgb(var(--color-red))", - // Map to UIKit colors already in theme - }, - }), - ], -} satisfies Config -``` - -## Migration Strategy - -### Before/After Examples - -```jsx -// BEFORE: Verbose arbitrary values -
- User message -
- -// AFTER: Clean ratio syntax -
- User message -
- -// ALTERNATIVE: Percentage syntax (when mixing with background) -
- User message -
-``` - -### Migration Steps - -1. Install plugin in Tailwind config -2. Generate new utility classes via build -3. Replace existing arbitrary values with new classes -4. Test visual consistency -5. Run lint/typecheck validation - -## Implementation Tasks - -### Phase 1: Core Plugin Development - -1. **Create plugin file structure** - - Create `/packages/configs/tailwindcss/ratio-mixing-plugin.js` - - Implement core `plugin.withOptions` structure - - Add default configuration schema - -2. **Implement ratio parsing logic** - - Create `generateRatioBasedUtilities()` function - - Handle ratio-to-percentage conversion - - Support escape characters for CSS class names (`/` → `\/`) - -3. **Add percentage fallback** - - Implement `generatePercentageBasedUtilities()` function - - Provide implicit background mixing - - Support common percentage values - -### Phase 2: Integration & Configuration - -4. **Integrate with existing Tailwind config** - - Update `/packages/configs/tailwindcss/web.ts` - - Map to existing UIKit color variables - - Test build process compatibility - -5. **Color mapping to existing theme** - - Extract colors from current theme configuration - - Map `--fo-a`, `--background`, `--color-red` variables - - Ensure compatibility with existing color system - -### Phase 3: Migration & Testing - -6. **Migrate existing usage** - - Update `UserChatMessage.tsx:137` - - Update `CollapsibleError.tsx:172` - - Search for other arbitrary color-mix usages - -7. **Validation & testing** - - Visual regression testing - - CSS output verification - - Build process validation - -## Validation Gates - -### Build Validation - -```bash -# TypeScript compilation -pnpm run typecheck - -# Linting validation -pnpm run lint -pnpm run lint:tsl - -# Tailwind build test -cd packages/configs/tailwindcss && npx tailwindcss build -``` - -### Plugin-Specific Validation - -```bash -# Test plugin registration -node -e "console.log(require('./packages/configs/tailwindcss/ratio-mixing-plugin.js'))" - -# Test CSS generation -echo "@tailwind utilities;" | npx tailwindcss --config packages/configs/tailwindcss/web.ts -``` - -### Visual Validation - -```bash -# Development server test -cd apps/desktop && pnpm run dev:web - -# Build verification -pnpm run build:web -``` - -## Expected Deliverables - -1. **Plugin Implementation** - - `/packages/configs/tailwindcss/ratio-mixing-plugin.js` - Complete plugin - - Updated `/packages/configs/tailwindcss/web.ts` - Integration - -2. **Migration Changes** - - Updated component files with new class syntax - - Removed verbose arbitrary value usage - -3. **Documentation** - - Generated CSS class reference - - Migration guide for future usage - -## Risk Assessment & Mitigation - -### Potential Issues - -1. **CSS Specificity**: New utilities should have same specificity as existing ones -2. **Build Performance**: Plugin should not significantly slow build times -3. **Browser Compatibility**: `color-mix()` requires modern browser support -4. **Class Name Conflicts**: Need to avoid conflicts with existing utilities - -### Mitigation Strategies - -1. Follow Tailwind's utility layer conventions -2. Implement efficient utility generation (avoid nested loops where possible) -3. Document browser support requirements (IE not supported) -4. Use unique prefixes and test for conflicts - -## Success Criteria - -1. **Functionality**: All current color mixing usage successfully migrated -2. **Performance**: No measurable build time increase (< 5% overhead) -3. **Maintainability**: New syntax reduces class name length by >60% -4. **Developer Experience**: IntelliSense support for new utilities -5. **Visual Consistency**: Pixel-perfect visual match with existing styling - -## External References - -### Documentation - -- **Tailwind Plugin API**: https://v3.tailwindcss.com/docs/plugins -- **CSS color-mix() Specification**: https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/color-mix -- **Existing Plugin Examples**: https://github.com/JavierM42/tailwindcss-color-mix - -### Codebase Files to Reference - -- `/packages/configs/tailwindcss/web.ts` - Main Tailwind configuration -- `/packages/configs/tailwindcss/tw-css-plugin.js` - Existing plugin pattern -- `/packages/configs/tailwindcss/tailwind-extend.css` - Utility examples - -## Confidence Score: 8/10 - -**Rationale**: High confidence due to: - -- ✅ Clear existing patterns in codebase -- ✅ Well-documented Tailwind plugin API -- ✅ Specific usage examples identified -- ✅ CSS color-mix() is well-supported specification -- ✅ Comprehensive implementation plan - -**Potential challenges**: - -- ⚠️ CSS class name escaping complexity -- ⚠️ Color variable mapping accuracy diff --git a/apps/desktop/AGENTS.md b/apps/desktop/AGENTS.md index ed35a501c..ff07e5e2a 100644 --- a/apps/desktop/AGENTS.md +++ b/apps/desktop/AGENTS.md @@ -107,6 +107,114 @@ import { Button, Modal } from "@follow/components" import { FeedList } from "~/modules/name/components" ``` +## Glassmorphic Depth Design System + +Follow uses a sophisticated glassmorphic depth design system for elevated UI components (modals, toasts, floating panels, etc.). This design provides visual hierarchy through layered transparency and subtle color accents. + +### Design Principles + +- **Multi-layer Depth**: Create visual depth through stacked transparent layers +- **Subtle Color Accents**: Use brand colors at very low opacity (5-20%) for borders, glows, and backgrounds +- **Refined Blur**: Heavy backdrop blur (backdrop-blur-2xl) for frosted glass effect +- **Minimal Shadows**: Combine multiple soft shadows with accent colors for depth perception +- **Smooth Animations**: Use Spring presets for all transitions + +### Color Usage + +- **Primary Accent**: `#FF5C00` (orange) at 5-20% opacity for borders, glows, and highlights +- **Border**: `rgba(255, 92, 0, 0.2)` for main borders +- **Inner Glow**: `rgba(255, 92, 0, 0.05)` for subtle radial/linear gradients inside containers +- **Shadows**: Layered shadows with accent tint: + - `0 8px 32px rgba(255, 92, 0, 0.08)` - large soft glow + - `0 4px 16px rgba(255, 92, 0, 0.06)` - medium shadow + - `0 2px 8px rgba(0, 0, 0, 0.1)` - close depth + +### Component Structure + +```tsx +
+ {/* Inner glow layer */} +
+ + {/* Content */} +
{/* Your content here */}
+
+``` + +### Interactive Elements + +For hover states on buttons or interactive areas within glass containers: + +```tsx + +``` + +### Dividers + +Use gradient dividers within glass containers: + +```tsx +
+``` + +### Animation Guidelines + +- Entry animations: `initial={{ y: 8, opacity: 0 }}` → `animate={{ y: 0, opacity: 1 }}` +- Use `Spring.presets.snappy` for quick interactions +- Use `Spring.presets.smooth` for larger movements +- Keep scale animations subtle (1.0 ↔ 1.02) + +### When to Use + +Apply this design system to: + +- Toast notifications +- Modal dialogs +- Floating panels and popovers +- Ambient UI prompts +- Contextual menus +- Elevated cards with actions + +### Accessibility + +- Ensure sufficient contrast for text over glass backgrounds +- Maintain border visibility in both light and dark modes +- Preserve keyboard focus indicators +- Keep animations respectful of `prefers-reduced-motion` + ## Build Outputs - Desktop: `apps/desktop/out/` for packaged applications diff --git a/apps/desktop/changelog/0.8.0.md b/apps/desktop/changelog/0.8.0.md new file mode 100644 index 000000000..78302a378 --- /dev/null +++ b/apps/desktop/changelog/0.8.0.md @@ -0,0 +1,13 @@ +# What's new in v0.8.0 + +## Shiny new things + +- Smart onboarding that gets you started in seconds. +- One place for all your feeds. +- A cleaner, consistent reading experience for social media posts, picture galleries, videos, and articles. +- Support subtitles for podcasts and videos. +- Redesigned Actions for easier setup. + +## Improvements + +- We have made countless improvements and bug fixes in this version. diff --git a/apps/desktop/changelog/next.md b/apps/desktop/changelog/next.md index fc6b11e21..8f5eac449 100644 --- a/apps/desktop/changelog/next.md +++ b/apps/desktop/changelog/next.md @@ -2,18 +2,8 @@ ## Shiny new things -- AI Chat for RSS (Folo AI): A personalized, context-aware chat built for readers. The assistant understands your feeds, lists, and reading patterns to help you get more from what you follow. - - Summarize articles, translate content, and get quick explanations—without leaving your timeline. - - Ask feed-aware questions (e.g., “What did I miss this week?”) and receive suggestions tailored to your habits. - - Discover what to read next based on your subscriptions and saved items. - - Streamlined chat UI with context pickers for feeds, entries, and more. - - Early preview: we’ll iterate quickly with your feedback. - - Limited rollout: currently available to a subset of users; we’ll expand testing over time. - ## Improvements -- Foundations for richer, feed-aware AI features and future extensibility around the chat module. - ## No longer broken ## Thanks diff --git a/apps/desktop/configs/vite.render.config.ts b/apps/desktop/configs/vite.render.config.ts index cad832af9..286dac8cd 100644 --- a/apps/desktop/configs/vite.render.config.ts +++ b/apps/desktop/configs/vite.render.config.ts @@ -39,7 +39,7 @@ export const viteRenderBaseConfig = { format: "es", }, optimizeDeps: { - exclude: ["sqlocal", "wa-sqlite"], + exclude: ["sqlocal", "wa-sqlite", "@follow-app/client-sdk"], }, resolve: { alias: { diff --git a/apps/desktop/layer/main/package.json b/apps/desktop/layer/main/package.json index 20e5142f4..2e71bd710 100644 --- a/apps/desktop/layer/main/package.json +++ b/apps/desktop/layer/main/package.json @@ -28,35 +28,35 @@ "@follow/shared": "workspace:*", "@follow/utils": "workspace:*", "@openpanel/web": "1.0.1", - "@sentry/electron": "7.1.0", + "@sentry/electron": "7.2.0", "builder-util-runtime": "9.3.1", "electron-context-menu": "4.1.1", "electron-ipc-decorator": "0.2.0", "electron-log": "5.4.3", "electron-squirrel-startup": "1.0.1", - "electron-store": "10.1.0", + "electron-store": "11.0.2", "electron-updater": "6.6.2", - "es-toolkit": "1.39.10", + "es-toolkit": "1.40.0", "font-list": "2.0.1", - "i18next": "25.5.2", + "i18next": "25.6.0", "js-yaml": "4.1.0", - "ky": "1.10.0", + "ky": "1.12.0", "linkedom": "0.18.11", "lowdb": "7.0.1", "msedge-tts": "2.0.2", "node-machine-id": "1.1.12", "ofetch": "1.4.1", "pathe": "2.0.3", - "semver": "7.7.2", - "tar": "7.4.3", + "semver": "7.7.3", + "tar": "7.5.1", "vscode-languagedetection": "npm:@vscode/vscode-languagedetection@1.0.22" }, "devDependencies": { "@follow/models": "workspace:*", "@follow/types": "workspace:*", "@types/js-yaml": "4.0.9", - "@types/node": "24.5.2", - "electron": "37.2.0", + "@types/node": "24.8.1", + "electron": "38.3.0", "electron-devtools-installer": "4.0.0", "typescript": "catalog:" } diff --git a/apps/desktop/layer/main/src/manager/bootstrap.ts b/apps/desktop/layer/main/src/manager/bootstrap.ts index 45b6d3bbe..4e8f19d30 100644 --- a/apps/desktop/layer/main/src/manager/bootstrap.ts +++ b/apps/desktop/layer/main/src/manager/bootstrap.ts @@ -88,16 +88,16 @@ export class BootstrapManager { if (url.hostname === "us.i.posthog.com") { const responseHeaders = details.responseHeaders || {} - responseHeaders["Access-Control-Allow-Origin"] = ["*"] - responseHeaders["Access-Control-Allow-Methods"] = [ + responseHeaders["access-control-allow-origin"] = ["*"] + responseHeaders["access-control-allow-methods"] = [ "GET", "POST", "PUT", "DELETE", "OPTIONS", ] - responseHeaders["Access-Control-Allow-Headers"] = ["*"] - responseHeaders["Access-Control-Allow-Credentials"] = ["true"] + responseHeaders["access-control-allow-headers"] = ["*"] + responseHeaders["access-control-allow-credentials"] = ["true"] callback({ cancel: false, diff --git a/apps/desktop/layer/main/src/updater/custom-github-provider.ts b/apps/desktop/layer/main/src/updater/custom-github-provider.ts index 04d5eca6b..ef90be635 100644 --- a/apps/desktop/layer/main/src/updater/custom-github-provider.ts +++ b/apps/desktop/layer/main/src/updater/custom-github-provider.ts @@ -17,6 +17,7 @@ import { parseUpdateInfo, resolveFiles } from "electron-updater/out/providers/Pr import * as semver from "semver" import { isWindows } from "../env" +import { githubProviderLogger as logger, logObject } from "./logger" import { isSquirrelBuild } from "./utils" interface GithubUpdateInfo extends UpdateInfo { @@ -46,10 +47,21 @@ export class CustomGitHubProvider extends BaseGitHubProvider { } async getLatestVersion(): Promise { + logger.info("Starting getLatestVersion") + logObject(logger, "Provider Configuration", { + "Base URL": this.baseUrl.href, + "Base Path": this.basePath, + "Current Version": this.updater.currentVersion, + "Allow Prerelease": this.updater.allowPrerelease, + }) + const cancellationToken = new CancellationToken() + const feedUrl = newUrlFromBase(`${this.basePath}.atom`, this.baseUrl) + logger.info(`Fetching feed from: ${feedUrl.href}`) + const feedXml = await this.httpRequest( - newUrlFromBase(`${this.basePath}.atom`, this.baseUrl), + feedUrl, { accept: "application/xml, application/atom+xml, text/xml, */*", }, @@ -60,6 +72,8 @@ export class CustomGitHubProvider extends BaseGitHubProvider { throw new Error(`Cannot find feed in the remote server (${this.baseUrl.href})`) } + logger.info(`Feed fetched successfully, length: ${feedXml.length} bytes`) + const feed = parseXml(feedXml) // noinspection TypeScriptValidateJSTypes let latestRelease = feed.element("entry", false, `No published versions on GitHub`) @@ -71,6 +85,8 @@ export class CustomGitHubProvider extends BaseGitHubProvider { (semver.prerelease(this.updater.currentVersion)?.[0] as string) || null + logger.info(`Current Channel: ${currentChannel}`) + if (currentChannel === null) { throw newError( `Cannot parse channel from version: ${this.updater.currentVersion}`, @@ -78,16 +94,27 @@ export class CustomGitHubProvider extends BaseGitHubProvider { ) } + logger.info(`Fetching latest tag by release API for channel: ${currentChannel}`) const releaseTag = await this.getLatestTagByRelease(currentChannel, cancellationToken) + logger.info(`Release tag from API: ${releaseTag || "null (will use feed matching)"}`) + logger.info("Iterating through feed entries to find matching release") + let entryCount = 0 for (const element of feed.getElements("entry")) { + entryCount++ // noinspection TypeScriptValidateJSTypes - const hrefElement = hrefRegExp.exec(element.element("link").attribute("href")) + const href = element.element("link").attribute("href") + const hrefElement = hrefRegExp.exec(href) // If this is null then something is wrong and skip this release - if (hrefElement === null) continue + if (hrefElement === null) { + logger.warn(`Entry #${entryCount}: Invalid href format: ${href}`) + continue + } // This Release's Tag const hrefTag = hrefElement[1]! + logger.debug(`Entry #${entryCount}: Processing tag: ${hrefTag}`) + // Get Channel from this release's tag // Handle new format: desktop/v1.2.3 or mobile/v1.2.3 let hrefChannel = "stable" @@ -95,28 +122,40 @@ export class CustomGitHubProvider extends BaseGitHubProvider { // For desktop tags, extract the version and check if it's a prerelease const version = hrefTag.replace("desktop/", "") hrefChannel = (semver.prerelease(version)?.[0] as string) || "stable" + logger.debug( + `Entry #${entryCount}: Desktop tag detected, version: ${version}, channel: ${hrefChannel}`, + ) } else if (hrefTag.startsWith("mobile/")) { // Skip mobile releases for desktop updater + logger.debug(`Entry #${entryCount}: Skipping mobile tag`) continue } else { // Legacy format: check for prerelease directly hrefChannel = (semver.prerelease(hrefTag)?.[0] as string) || "stable" + logger.debug(`Entry #${entryCount}: Legacy tag format, channel: ${hrefChannel}`) } let isNextPreRelease = false if (releaseTag) { isNextPreRelease = releaseTag === hrefTag + logger.debug(`Entry #${entryCount}: Matching by release tag: ${releaseTag === hrefTag}`) } else { isNextPreRelease = hrefChannel === currentChannel + logger.debug( + `Entry #${entryCount}: Matching by channel: ${hrefChannel} === ${currentChannel} = ${isNextPreRelease}`, + ) } if (isNextPreRelease) { tag = hrefTag latestRelease = element + logger.info(`✓ Found matching release at entry #${entryCount}: ${hrefTag}`) break } } + logger.info(`Processed ${entryCount} feed entries total`) } catch (e: any) { + logger.error(`Failed to parse releases feed: ${e.stack || e.message}`) throw newError( `Cannot parse releases feed: ${e.stack || e.message},\nXML:\n${feedXml}`, "ERR_UPDATER_INVALID_RELEASE_FEED", @@ -124,6 +163,7 @@ export class CustomGitHubProvider extends BaseGitHubProvider { } if (tag === null || tag === undefined) { + logger.error("No matching published versions found on GitHub") throw newError(`No published versions on GitHub`, "ERR_UPDATER_NO_PUBLISHED_VERSIONS") } @@ -136,11 +176,15 @@ export class CustomGitHubProvider extends BaseGitHubProvider { this.getBaseDownloadPath(String(tag), channelFile), this.baseUrl, ) + logger.info(`Fetching channel file: ${channelFile} from ${channelFileUrl}`) const requestOptions = this.createRequestOptions(channelFileUrl) try { - return await this.executor.request(requestOptions, cancellationToken) + const data = await this.executor.request(requestOptions, cancellationToken) + logger.info(`Successfully fetched ${channelFile}`) + return data } catch (e: any) { if (e instanceof HttpError && e.statusCode === 404) { + logger.warn(`Channel file not found: ${channelFile} (404)`) throw newError( `Cannot find ${channelFile} in the latest release artifacts (${channelFileUrl}): ${ e.stack || e.message @@ -148,6 +192,7 @@ export class CustomGitHubProvider extends BaseGitHubProvider { "ERR_UPDATER_CHANNEL_FILE_NOT_FOUND", ) } + logger.error(`Failed to fetch channel file: ${e.message}`) throw e } } @@ -156,10 +201,12 @@ export class CustomGitHubProvider extends BaseGitHubProvider { const channel = this.updater.allowPrerelease ? this.getCustomChannelName(String(semver.prerelease(tag)?.[0] || "latest")) : this.getDefaultChannelName() + logger.info(`Attempting to fetch channel: ${channel}`) rawData = await fetchData(channel) } catch (e: any) { if (this.updater.allowPrerelease) { // Allow fallback to `latest.yml` + logger.info("Falling back to default channel (latest.yml)") rawData = await fetchData(this.getDefaultChannelName()) } else { throw e @@ -179,6 +226,16 @@ export class CustomGitHubProvider extends BaseGitHubProvider { latestRelease, ) } + + logger.info(`Update info parsed successfully`) + logObject(logger, "Update Result", { + Tag: tag, + Version: result.version, + "Release Name": result.releaseName || "N/A", + "Release Date": result.releaseDate || "N/A", + Files: result.files?.length || 0, + }) + return { tag, ...result, @@ -201,8 +258,11 @@ export class CustomGitHubProvider extends BaseGitHubProvider { cancellationToken: CancellationToken, ) { try { + const apiUrl = newUrlFromBase(`/repos${this.basePath}`, this.baseApiUrl) + logger.debug(`Fetching releases from GitHub API: ${apiUrl}`) + const releasesStr = await this.httpRequest( - newUrlFromBase(`/repos${this.basePath}`, this.baseApiUrl), + apiUrl, { accept: "Accept: application/vnd.github+json", "X-GitHub-Api-Version": "2022-11-28", @@ -211,15 +271,21 @@ export class CustomGitHubProvider extends BaseGitHubProvider { ) if (!releasesStr) { + logger.warn("GitHub API returned empty response") return null } const releases: GithubRelease[] = JSON.parse(releasesStr) + logger.debug(`Received ${releases.length} releases from GitHub API`) + + let checkedCount = 0 for (const release of releases) { if (release.draft) { + logger.debug(`Skipping draft release: ${release.tag_name}`) continue } + checkedCount++ const releaseTag = release.tag_name // Handle new format: desktop/v1.2.3 or mobile/v1.2.3 @@ -228,40 +294,62 @@ export class CustomGitHubProvider extends BaseGitHubProvider { // For desktop tags, extract the version and check if it's a prerelease const version = releaseTag.replace("desktop/", "") releaseChannel = (semver.prerelease(version)?.[0] as string) || "stable" + logger.debug(`API Release: ${releaseTag} (desktop, channel: ${releaseChannel})`) } else if (releaseTag.startsWith("mobile/")) { // Skip mobile releases for desktop updater + logger.debug(`Skipping mobile release: ${releaseTag}`) continue } else { // Legacy format: check for prerelease directly releaseChannel = (semver.prerelease(releaseTag)?.[0] as string) || "stable" + logger.debug(`API Release: ${releaseTag} (legacy, channel: ${releaseChannel})`) } if (releaseChannel === currentChannel) { + logger.info(`✓ Found matching release via API: ${release.tag_name}`) return release.tag_name } } + + logger.info(`No matching release found via API (checked ${checkedCount} non-draft releases)`) } catch (e: any) { - console.info(`Cannot parse release: ${e.stack || e.message}`) + logger.warn(`Cannot parse release from API: ${e.message}`) } return null } resolveFiles(updateInfo: GithubUpdateInfo): Array { + logger.info(`Resolving files for tag: ${updateInfo.tag}`) + logger.debug(`Total files in update info: ${updateInfo.files.length}`) + const filteredUpdateInfo = structuredClone(updateInfo) // for windows, we need to determine its installer type (nsis or squirrel) if (isWindows && updateInfo.files.length > 1) { const isSquirrel = isSquirrelBuild() + logger.info(`Windows build detected, installer type: ${isSquirrel ? "Squirrel" : "NSIS"}`) + // @ts-expect-error we should be able to modify the object filteredUpdateInfo.files = updateInfo.files.filter((file) => isSquirrel ? !file.url.includes("nsis.exe") : file.url.includes("nsis.exe"), ) + + logger.debug( + `Filtered to ${filteredUpdateInfo.files.length} files after Windows installer type filtering`, + ) } // still replace space to - due to backward compatibility - return resolveFiles(filteredUpdateInfo, this.baseUrl, (p) => + const resolved = resolveFiles(filteredUpdateInfo, this.baseUrl, (p) => this.getBaseDownloadPath(filteredUpdateInfo.tag, p.replaceAll(" ", "-")), ) + + logger.info(`Resolved ${resolved.length} file(s) for download`) + resolved.forEach((file, index) => { + logger.debug(` File ${index + 1}: ${file.url}`) + }) + + return resolved } private getBaseDownloadPath(tag: string, fileName: string): string { diff --git a/apps/desktop/layer/main/src/updater/logger.ts b/apps/desktop/layer/main/src/updater/logger.ts new file mode 100644 index 000000000..53102c073 --- /dev/null +++ b/apps/desktop/layer/main/src/updater/logger.ts @@ -0,0 +1,22 @@ +import log from "electron-log" + +/** + * Logger for updater module with scoped prefix + * All logs are prefixed with [Updater] for easy identification + */ +export const updaterLogger = log.scope("updater") + +/** + * Logger specifically for GitHub provider operations + */ +export const githubProviderLogger = log.scope("updater:github") + +/** + * Helper to log object properties in a formatted way + */ +export function logObject(logger: typeof updaterLogger, prefix: string, obj: Record) { + logger.info(`${prefix}:`) + for (const [key, value] of Object.entries(obj)) { + logger.info(` ${key}: ${value}`) + } +} diff --git a/apps/desktop/layer/renderer/index.html b/apps/desktop/layer/renderer/index.html index 5ccea93b4..5b4b89509 100644 --- a/apps/desktop/layer/renderer/index.html +++ b/apps/desktop/layer/renderer/index.html @@ -17,14 +17,14 @@ - Folo - AI-Driven RSS Reader | Follow Everything + Folo - AI Reader | Follow Everything @@ -40,10 +40,10 @@ - + - + - + - +