返回 presentation-ai
useCommonValues.ts
1 "use client";
2
3 import { useCallback, useMemo } from "react";
4
5 import { type LayoutType } from "@/components/notebook/presentation/utils/parser";
6 import { resolvePresentationThemeData } from "@/lib/presentation/theme-resolution";
7 import { usePresentationState } from "@/states/presentation-state";
8 import { type ContentAlignment } from "../types";
9
10 export function useCommonValues() {
11 const { slides, theme, customThemeData } = usePresentationState();
12
13 // Get the current theme's font family as default
14 const getThemeFontFamily = useCallback(() => {
15 const themeData = resolvePresentationThemeData({
16 customThemeData,
17 theme,
18 });
19
20 if (themeData?.fonts) {
21 return {
22 heading: themeData.fonts.heading,
23 body: themeData.fonts.body,
24 };
25 }
26 // Fallback
27 return { heading: "Inter", body: "Inter" };
28 }, [theme, customThemeData]);
29
30 const getMostCommonValue = useCallback(
31 <T>(key: keyof (typeof slides)[0], defaultValue: T): T => {
32 if (slides.length === 0) {
33 // Special handling for fontFamily: use theme fonts as default
34 if (key === "fontFamily") {
35 return getThemeFontFamily() as T;
36 }
37 return defaultValue;
38 }
39
40 const map = new Map<string, { count: number; sample: unknown }>();
41
42 for (const slide of slides) {
43 const raw = slide[key] as unknown;
44 if (raw === undefined || raw === null || (raw as unknown) === "")
45 continue;
46 const label =
47 typeof raw === "object" ? JSON.stringify(raw) : String(raw as string);
48 const entry = map.get(label);
49 if (entry) {
50 entry.count += 1;
51 } else {
52 map.set(label, { count: 1, sample: raw });
53 }
54 }
55
56 if (map.size === 0) {
57 // Special handling for fontFamily: use theme fonts as default
58 if (key === "fontFamily") {
59 return getThemeFontFamily() as T;
60 }
61 return defaultValue;
62 }
63 let best: { count: number; sample: unknown } | null = null;
64 for (const entry of map.values()) {
65 if (!best || entry.count > best.count) best = entry;
66 }
67 return (best?.sample as T) ?? defaultValue;
68 },
69 [slides, getThemeFontFamily],
70 );
71
72 const derived = useMemo(() => {
73 const currentLayout = getMostCommonValue(
74 "layoutType",
75 "left" as LayoutType,
76 );
77
78 // Get the theme background color as a default
79 let defaultBgColor = "#ffffff";
80 if (typeof window !== "undefined") {
81 const root = window.getComputedStyle(document.documentElement);
82 const themeBg = root.getPropertyValue("--presentation-background").trim();
83 if (themeBg) defaultBgColor = themeBg;
84 }
85
86 const currentBgColor = getMostCommonValue("bgColor", defaultBgColor);
87
88 const currentWidth = getMostCommonValue("width", "M" as "S" | "M" | "L");
89 const currentAlignment = getMostCommonValue(
90 "alignment",
91 "start" as ContentAlignment,
92 );
93 const currentFontSize = getMostCommonValue(
94 "fontSize",
95 "M" as "S" | "M" | "L",
96 );
97
98 const currentFormatCategory = getMostCommonValue(
99 "formatCategory",
100 "presentation" as "presentation" | "social" | "document" | "webpage",
101 );
102 const currentAspectRatio = getMostCommonValue("aspectRatio", {
103 type: "fluid",
104 value: "",
105 } as unknown) as unknown as {
106 type: string;
107 value?: string;
108 width?: number;
109 height?: number;
110 };
111
112 return {
113 currentLayout,
114 currentBgColor,
115 currentWidth,
116 currentAlignment,
117 currentFontSize,
118 currentFormatCategory,
119 currentAspectRatio,
120 };
121 }, [getMostCommonValue]);
122
123 return { slides, getMostCommonValue, ...derived };
124 }
125
125 lines TYPESCRIPT