diff --git a/web/src/components/MasonryView/MasonryView.tsx b/web/src/components/MasonryView/MasonryView.tsx index dd87c68a4..f20067b16 100644 --- a/web/src/components/MasonryView/MasonryView.tsx +++ b/web/src/components/MasonryView/MasonryView.tsx @@ -10,10 +10,10 @@ const MasonryView = ({ memoList, renderer, prefixElement, listMode = false }: Ma const { columns, distribution, handleHeightChange } = useMasonryLayout(memoList, listMode, containerRef, prefixElementRef); - // Create render context: automatically enable compact mode when multiple columns + // Create render context: always enable compact mode for list views const renderContext: MemoRenderContext = useMemo( () => ({ - compact: columns > 1, + compact: true, columns, }), [columns], diff --git a/web/src/components/MemoContent/constants.ts b/web/src/components/MemoContent/constants.ts index 2a7c8293d..c7a95fc7c 100644 --- a/web/src/components/MemoContent/constants.ts +++ b/web/src/components/MemoContent/constants.ts @@ -1,6 +1,12 @@ import { defaultSchema } from "rehype-sanitize"; -export const MAX_DISPLAY_HEIGHT = 256; +// Compact mode display settings +export const COMPACT_MODE_CONFIG = { + maxHeightVh: 60, // 60% of viewport height + gradientHeight: "h-24", // Tailwind class for gradient overlay +} as const; + +export const getMaxDisplayHeight = () => window.innerHeight * (COMPACT_MODE_CONFIG.maxHeightVh / 100); export const COMPACT_STATES: Record<"ALL" | "SNIPPET", { textKey: string; next: "ALL" | "SNIPPET" }> = { ALL: { textKey: "memo.show-more", next: "SNIPPET" }, diff --git a/web/src/components/MemoContent/hooks.ts b/web/src/components/MemoContent/hooks.ts index 2f7c82ca1..bc8187472 100644 --- a/web/src/components/MemoContent/hooks.ts +++ b/web/src/components/MemoContent/hooks.ts @@ -1,5 +1,5 @@ import { useCallback, useEffect, useRef, useState } from "react"; -import { COMPACT_STATES, MAX_DISPLAY_HEIGHT } from "./constants"; +import { COMPACT_STATES, getMaxDisplayHeight } from "./constants"; import type { ContentCompactView } from "./types"; export const useCompactMode = (enabled: boolean) => { @@ -8,7 +8,8 @@ export const useCompactMode = (enabled: boolean) => { useEffect(() => { if (!enabled || !containerRef.current) return; - if (containerRef.current.getBoundingClientRect().height > MAX_DISPLAY_HEIGHT) { + const maxHeight = getMaxDisplayHeight(); + if (containerRef.current.getBoundingClientRect().height > maxHeight) { setMode("ALL"); } }, [enabled]); diff --git a/web/src/components/MemoContent/index.tsx b/web/src/components/MemoContent/index.tsx index f0114909b..8342f6582 100644 --- a/web/src/components/MemoContent/index.tsx +++ b/web/src/components/MemoContent/index.tsx @@ -1,4 +1,5 @@ import type { Element } from "hast"; +import { ChevronDown, ChevronUp } from "lucide-react"; import { memo } from "react"; import ReactMarkdown from "react-markdown"; import rehypeKatex from "rehype-katex"; @@ -14,7 +15,7 @@ import { remarkPreserveType } from "@/utils/remark-plugins/remark-preserve-type" import { remarkTag } from "@/utils/remark-plugins/remark-tag"; import { CodeBlock } from "./CodeBlock"; import { isTagNode, isTaskListItemNode } from "./ConditionalComponent"; -import { SANITIZE_SCHEMA } from "./constants"; +import { COMPACT_MODE_CONFIG, SANITIZE_SCHEMA } from "./constants"; import { useCompactLabel, useCompactMode } from "./hooks"; import { Tag } from "./Tag"; import { TaskListItem } from "./TaskListItem"; @@ -37,7 +38,7 @@ const MemoContent = (props: MemoContentProps) => { ref={memoContentContainerRef} className={cn( "markdown-content relative w-full max-w-full wrap-break-word text-base leading-6", - showCompactMode === "ALL" && "line-clamp-6 max-h-60", + showCompactMode === "ALL" && `max-h-[${COMPACT_MODE_CONFIG.maxHeightVh}vh] overflow-hidden`, contentClassName, )} onMouseUp={onClick} @@ -71,18 +72,25 @@ const MemoContent = (props: MemoContentProps) => { > {content} + {showCompactMode === "ALL" && ( +
+ )} - {showCompactMode === "ALL" && ( - - )} {showCompactMode !== undefined && ( -