This commit is contained in:
ct
2025-06-16 19:08:45 +08:00
parent ff981ea9f0
commit 4220709b57
13 changed files with 159 additions and 174 deletions

View File

@@ -5,12 +5,7 @@ import { Sheet, SheetContent, SheetHeader, SheetTitle } from '@/components/ui/sh
import useLocalSettingsStore from '@/stores/localSettingsStore';
import { SettingsIcon } from 'lucide-react';
interface EditNavSidebarProps {
isOpen: boolean;
onClose: () => void;
}
export default function EditNavSidebar({ isOpen, onClose }: EditNavSidebarProps) {
export default function EditNavSidebar({ isOpen, onClose }) {
const { getSetting, setSetting } = useLocalSettingsStore();
return (

View File

@@ -7,12 +7,7 @@ import useMediaStore from '@/stores/MediaStore';
import { Edit3 } from 'lucide-react';
import { useEffect, useState } from 'react';
interface EditSidebarProps {
isOpen: boolean;
onClose: () => void;
}
export default function EditSidebar({ isOpen, onClose }: EditSidebarProps) {
export default function EditSidebar({ isOpen, onClose }) {
const {
memes,
backgrounds,
@@ -42,7 +37,7 @@ export default function EditSidebar({ isOpen, onClose }: EditSidebarProps) {
}
}, [isOpen, activeTab, memes.length, backgrounds.length, isFetchingMemes, isFetchingBackgrounds]);
const handleTabChange = (value: string) => {
const handleTabChange = (value) => {
setActiveTab(value);
if (value === 'memes' && memes.length === 0 && !isFetchingMemes) {
fetchMemes();

View File

@@ -1,8 +1,8 @@
import React, { useEffect, useLayoutEffect, useState } from 'react';
import { useEffect, useLayoutEffect, useState } from 'react';
import { LAYOUT_CONSTANTS, calculateResponsiveScale } from '../utils/layout-constants';
import VideoEditor from './canvas/video-editor';
const useResponsiveCanvas = (maxWidth: number = 350) => {
const useResponsiveCanvas = (maxWidth = 350) => {
const [scale, setScale] = useState(() => calculateResponsiveScale(maxWidth));
useLayoutEffect(() => {
@@ -22,14 +22,14 @@ const useResponsiveCanvas = (maxWidth: number = 350) => {
window.addEventListener('orientationchange', handleResize);
// ResizeObserver for more reliable detection
let resizeObserver: ResizeObserver | undefined;
let resizeObserver;
if (window.ResizeObserver) {
resizeObserver = new ResizeObserver(handleResize);
resizeObserver.observe(document.body);
}
// MutationObserver for dev tools detection
let mutationObserver: MutationObserver | undefined;
let mutationObserver;
if (window.MutationObserver) {
mutationObserver = new MutationObserver(() => {
setTimeout(handleResize, 50);
@@ -51,11 +51,7 @@ const useResponsiveCanvas = (maxWidth: number = 350) => {
return scale;
};
interface EditorCanvasProps {
maxWidth?: number;
}
const EditorCanvas: React.FC<EditorCanvasProps> = ({ maxWidth = 350 }) => {
const EditorCanvas = ({ maxWidth = 350 }) => {
const scale = useResponsiveCanvas(maxWidth);
const displayWidth = LAYOUT_CONSTANTS.CANVAS_WIDTH * scale;
const displayHeight = LAYOUT_CONSTANTS.CANVAS_HEIGHT * scale;

View File

@@ -0,0 +1,67 @@
// Shared layout constants for the editor
export const LAYOUT_CONSTANTS = {
// Fixed element heights
HEADER_HEIGHT: 64, // Header with padding
CONTROLS_HEIGHT: 48, // Button controls height
// Canvas dimensions
CANVAS_WIDTH: 720,
CANVAS_HEIGHT: 1280,
CANVAS_ASPECT_RATIO: 720 / 1280, // 9:16 aspect ratio
// Spacing and padding
MIN_PADDING: 16, // Minimum padding around elements
CONTAINER_SPACING: 16, // Space between header, canvas, and controls (space-y-4 = 16px)
CONTAINER_VERTICAL_PADDING: 24, // py-6 = 24px top + 24px bottom
};
// Calculate optimal maxWidth based on viewport dimensions
export const calculateOptimalMaxWidth = () => {
const viewportWidth = window.innerWidth;
const viewportHeight = window.innerHeight;
// Calculate total used height for fixed elements and spacing
const usedHeight =
LAYOUT_CONSTANTS.HEADER_HEIGHT +
LAYOUT_CONSTANTS.CONTROLS_HEIGHT +
(LAYOUT_CONSTANTS.CONTAINER_SPACING * 2) + // Space between elements
LAYOUT_CONSTANTS.CONTAINER_VERTICAL_PADDING + // py-6 padding
(LAYOUT_CONSTANTS.MIN_PADDING * 2); // Additional safety padding
const availableHeight = viewportHeight - usedHeight;
const availableWidth = viewportWidth - (LAYOUT_CONSTANTS.MIN_PADDING * 2);
// Calculate maxWidth based on both width and height constraints
const maxWidthFromHeight = availableHeight * LAYOUT_CONSTANTS.CANVAS_ASPECT_RATIO;
const maxWidthFromWidth = availableWidth;
// Use the smaller constraint to ensure everything fits
const optimalMaxWidth = Math.min(maxWidthFromHeight, maxWidthFromWidth);
// Ensure minimum viable size (but allow smaller if viewport is tiny)
const minViableWidth = 280;
return Math.max(optimalMaxWidth, Math.min(minViableWidth, availableWidth));
};
// Calculate responsive width based on optimal maxWidth
export const calculateResponsiveWidth = () => {
const optimalMaxWidth = calculateOptimalMaxWidth();
const viewportWidth = window.innerWidth;
const padding = LAYOUT_CONSTANTS.MIN_PADDING * 2;
const availableWidth = viewportWidth - padding;
return Math.min(availableWidth, optimalMaxWidth);
};
// Calculate responsive scale for canvas
export const calculateResponsiveScale = (maxWidth) => {
const viewportWidth = window.innerWidth;
const padding = LAYOUT_CONSTANTS.MIN_PADDING * 2;
const availableWidth = viewportWidth - padding;
const constrainedWidth = Math.min(availableWidth, maxWidth);
const scale = constrainedWidth / LAYOUT_CONSTANTS.CANVAS_WIDTH;
// Cap at 100% to avoid upscaling
return Math.min(scale, 1);
};

View File

@@ -1,67 +0,0 @@
// Shared layout constants for the editor
export const LAYOUT_CONSTANTS = {
// Fixed element heights
HEADER_HEIGHT: 64, // Header with padding
CONTROLS_HEIGHT: 48, // Button controls height
// Canvas dimensions
CANVAS_WIDTH: 720,
CANVAS_HEIGHT: 1280,
CANVAS_ASPECT_RATIO: 720 / 1280, // 9:16 aspect ratio
// Spacing and padding
MIN_PADDING: 16, // Minimum padding around elements
CONTAINER_SPACING: 16, // Space between header, canvas, and controls (space-y-4 = 16px)
CONTAINER_VERTICAL_PADDING: 24, // py-6 = 24px top + 24px bottom
};
// Calculate optimal maxWidth based on viewport dimensions
export const calculateOptimalMaxWidth = (): number => {
const viewportWidth = window.innerWidth;
const viewportHeight = window.innerHeight;
// Calculate total used height for fixed elements and spacing
const usedHeight =
LAYOUT_CONSTANTS.HEADER_HEIGHT +
LAYOUT_CONSTANTS.CONTROLS_HEIGHT +
(LAYOUT_CONSTANTS.CONTAINER_SPACING * 2) + // Space between elements
LAYOUT_CONSTANTS.CONTAINER_VERTICAL_PADDING + // py-6 padding
(LAYOUT_CONSTANTS.MIN_PADDING * 2); // Additional safety padding
const availableHeight = viewportHeight - usedHeight;
const availableWidth = viewportWidth - (LAYOUT_CONSTANTS.MIN_PADDING * 2);
// Calculate maxWidth based on both width and height constraints
const maxWidthFromHeight = availableHeight * LAYOUT_CONSTANTS.CANVAS_ASPECT_RATIO;
const maxWidthFromWidth = availableWidth;
// Use the smaller constraint to ensure everything fits
const optimalMaxWidth = Math.min(maxWidthFromHeight, maxWidthFromWidth);
// Ensure minimum viable size (but allow smaller if viewport is tiny)
const minViableWidth = 280;
return Math.max(optimalMaxWidth, Math.min(minViableWidth, availableWidth));
};
// Calculate responsive width based on optimal maxWidth
export const calculateResponsiveWidth = (): number => {
const optimalMaxWidth = calculateOptimalMaxWidth();
const viewportWidth = window.innerWidth;
const padding = LAYOUT_CONSTANTS.MIN_PADDING * 2;
const availableWidth = viewportWidth - padding;
return Math.min(availableWidth, optimalMaxWidth);
};
// Calculate responsive scale for canvas
export const calculateResponsiveScale = (maxWidth: number): number => {
const viewportWidth = window.innerWidth;
const padding = LAYOUT_CONSTANTS.MIN_PADDING * 2;
const availableWidth = viewportWidth - padding;
const constrainedWidth = Math.min(availableWidth, maxWidth);
const scale = constrainedWidth / LAYOUT_CONSTANTS.CANVAS_WIDTH;
// Cap at 100% to avoid upscaling
return Math.min(scale, 1);
};

View File

@@ -1,4 +1,4 @@
import Editor from '@/modules/editor/editor';
import Editor from '@/modules/editor/editor.jsx';
const Home = () => {
return (

View File

@@ -0,0 +1,83 @@
import { mountStoreDevtool } from 'simple-zustand-devtools';
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
// Immutable default settings
const defaultSettings = {
genAlphaSlang: false,
// Add more settings here
};
const useLocalSettingsStore = create(
persist(
(set, get) => ({
settings: { ...defaultSettings }, // clone to avoid shared reference
// Get a setting by key
getSetting: (key) => {
const currentSettings = get().settings;
return key in currentSettings ? currentSettings[key] : (defaultSettings[key] ?? null);
},
// Set or update a specific setting
setSetting: (key, value) => {
console.log(`Updating setting ${key} to`, value); // <-- Debug log
set((state) => ({
settings: {
...state.settings,
[key]: value,
},
}));
},
// Reset all settings to default
resetSettings: () => {
set({ settings: { ...defaultSettings } }); // create new object reference
},
// Reset a specific setting to its default
resetSetting: (key) => {
set((state) => ({
settings: {
...state.settings,
[key]: defaultSettings[key],
},
}));
},
// Merge default settings with current ones (useful on load)
initializeSettings: () => {
set((state) => ({
settings: {
...defaultSettings,
...state.settings,
},
}));
},
}),
{
name: 'local-settings-storage',
// Ensure only the 'settings' key is stored
partialize: (state) => ({ settings: state.settings }),
// Explicit localStorage usage (for compatibility)
storage: {
getItem: (name) => {
const stored = localStorage.getItem(name);
return stored ? JSON.parse(stored) : null;
},
setItem: (name, value) => {
localStorage.setItem(name, JSON.stringify(value));
},
removeItem: (name) => {
localStorage.removeItem(name);
},
},
},
),
);
if (import.meta.env.APP_ENV === 'local') {
mountStoreDevtool('LocalSettingsStore', useLocalSettingsStore);
}
export default useLocalSettingsStore;

View File

@@ -1,84 +0,0 @@
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
// Immutable default settings
const defaultSettings = {
genAlphaSlang: false,
// Add more settings here
};
const useLocalSettingsStore = create(
persist(
(set, get) => ({
settings: { ...defaultSettings }, // clone to avoid shared reference
// Get a setting by key
getSetting: (key) => {
const currentSettings = get().settings;
return key in currentSettings ? currentSettings[key] : defaultSettings[key] ?? null;
},
// Set or update a specific setting
setSetting: (key, value) => {
console.log(`Updating setting ${key} to`, value); // <-- Debug log
set((state) => ({
settings: {
...state.settings,
[key]: value,
},
}));
},
// Reset all settings to default
resetSettings: () => {
set({ settings: { ...defaultSettings } }); // create new object reference
},
// Reset a specific setting to its default
resetSetting: (key) => {
set((state) => ({
settings: {
...state.settings,
[key]: defaultSettings[key],
},
}));
},
// Merge default settings with current ones (useful on load)
initializeSettings: () => {
set((state) => ({
settings: {
...defaultSettings,
...state.settings,
},
}));
},
}),
{
name: 'local-settings-storage',
// Ensure only the 'settings' key is stored
partialize: (state) => ({ settings: state.settings }),
// Explicit localStorage usage (for compatibility)
storage: {
getItem: (name) => {
const stored = localStorage.getItem(name);
return stored ? JSON.parse(stored) : null;
},
setItem: (name, value) => {
localStorage.setItem(name, JSON.stringify(value));
},
removeItem: (name) => {
localStorage.removeItem(name);
},
}
}
)
);
if (import.meta.env.APP_ENV === "local") {
mountStoreDevtool("LocalSettingsStore", useLocalSettingsStore);
}
export default useLocalSettingsStore;