fixed: [BUG] crypto.randomUUID is not a function Runtime Error #205. Added fallback function with manual UUID generation if crypto.randomUUID() was not available.
This commit is contained in:
@ -5,4 +5,34 @@ import { twMerge } from "tailwind-merge";
|
|||||||
|
|
||||||
export function cn(...inputs: ClassValue[]) {
|
export function cn(...inputs: ClassValue[]) {
|
||||||
return twMerge(clsx(inputs));
|
return twMerge(clsx(inputs));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a UUID v4 string
|
||||||
|
* Uses crypto.randomUUID() if available, otherwise falls back to a custom implementation
|
||||||
|
*/
|
||||||
|
export function generateUUID(): string {
|
||||||
|
// Use the native crypto.randomUUID if available
|
||||||
|
if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {
|
||||||
|
return crypto.randomUUID();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Secure fallback using crypto.getRandomValues
|
||||||
|
const bytes = new Uint8Array(16);
|
||||||
|
crypto.getRandomValues(bytes);
|
||||||
|
|
||||||
|
// Set version 4 (UUIDv4)
|
||||||
|
bytes[6] = (bytes[6] & 0x0f) | 0x40;
|
||||||
|
// Set variant 10xxxxxx
|
||||||
|
bytes[8] = (bytes[8] & 0x3f) | 0x80;
|
||||||
|
|
||||||
|
const hex = [...bytes].map(b => b.toString(16).padStart(2, '0'));
|
||||||
|
|
||||||
|
return (
|
||||||
|
hex.slice(0, 4).join('') + '-' +
|
||||||
|
hex.slice(4, 6).join('') + '-' +
|
||||||
|
hex.slice(6, 8).join('') + '-' +
|
||||||
|
hex.slice(8, 10).join('') + '-' +
|
||||||
|
hex.slice(10, 16).join('')
|
||||||
|
);
|
||||||
}
|
}
|
@ -1,6 +1,7 @@
|
|||||||
import { create } from "zustand";
|
import { create } from "zustand";
|
||||||
import { storageService } from "@/lib/storage/storage-service";
|
import { storageService } from "@/lib/storage/storage-service";
|
||||||
import { useTimelineStore } from "./timeline-store";
|
import { useTimelineStore } from "./timeline-store";
|
||||||
|
import { generateUUID } from "@/lib/utils";
|
||||||
|
|
||||||
export type MediaType = "image" | "video" | "audio";
|
export type MediaType = "image" | "video" | "audio";
|
||||||
|
|
||||||
@ -161,7 +162,7 @@ export const useMediaStore = create<MediaStore>((set, get) => ({
|
|||||||
addMediaItem: async (projectId, item) => {
|
addMediaItem: async (projectId, item) => {
|
||||||
const newItem: MediaItem = {
|
const newItem: MediaItem = {
|
||||||
...item,
|
...item,
|
||||||
id: crypto.randomUUID(),
|
id: generateUUID(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add to local state immediately for UI responsiveness
|
// Add to local state immediately for UI responsiveness
|
||||||
|
@ -4,6 +4,7 @@ import { storageService } from "@/lib/storage/storage-service";
|
|||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { useMediaStore } from "./media-store";
|
import { useMediaStore } from "./media-store";
|
||||||
import { useTimelineStore } from "./timeline-store";
|
import { useTimelineStore } from "./timeline-store";
|
||||||
|
import { generateUUID } from "@/lib/utils";
|
||||||
|
|
||||||
interface ProjectStore {
|
interface ProjectStore {
|
||||||
activeProject: TProject | null;
|
activeProject: TProject | null;
|
||||||
@ -35,7 +36,7 @@ export const useProjectStore = create<ProjectStore>((set, get) => ({
|
|||||||
|
|
||||||
createNewProject: async (name: string) => {
|
createNewProject: async (name: string) => {
|
||||||
const newProject: TProject = {
|
const newProject: TProject = {
|
||||||
id: crypto.randomUUID(),
|
id: generateUUID(),
|
||||||
name,
|
name,
|
||||||
thumbnail: "",
|
thumbnail: "",
|
||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
@ -223,7 +224,7 @@ export const useProjectStore = create<ProjectStore>((set, get) => ({
|
|||||||
existingNumbers.length > 0 ? Math.max(...existingNumbers) + 1 : 1;
|
existingNumbers.length > 0 ? Math.max(...existingNumbers) + 1 : 1;
|
||||||
|
|
||||||
const newProject: TProject = {
|
const newProject: TProject = {
|
||||||
id: crypto.randomUUID(),
|
id: generateUUID(),
|
||||||
name: `(${nextNumber}) ${baseName}`,
|
name: `(${nextNumber}) ${baseName}`,
|
||||||
thumbnail: project.thumbnail,
|
thumbnail: project.thumbnail,
|
||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
|
@ -13,6 +13,7 @@ import { useEditorStore } from "./editor-store";
|
|||||||
import { useMediaStore, getMediaAspectRatio } from "./media-store";
|
import { useMediaStore, getMediaAspectRatio } from "./media-store";
|
||||||
import { storageService } from "@/lib/storage/storage-service";
|
import { storageService } from "@/lib/storage/storage-service";
|
||||||
import { useProjectStore } from "./project-store";
|
import { useProjectStore } from "./project-store";
|
||||||
|
import { generateUUID } from "@/lib/utils";
|
||||||
|
|
||||||
// Helper function to manage element naming with suffixes
|
// Helper function to manage element naming with suffixes
|
||||||
const getElementNameWithSuffix = (
|
const getElementNameWithSuffix = (
|
||||||
@ -279,7 +280,7 @@ export const useTimelineStore = create<TimelineStore>((set, get) => {
|
|||||||
: "Track";
|
: "Track";
|
||||||
|
|
||||||
const newTrack: TimelineTrack = {
|
const newTrack: TimelineTrack = {
|
||||||
id: crypto.randomUUID(),
|
id: generateUUID(),
|
||||||
name: trackName,
|
name: trackName,
|
||||||
type,
|
type,
|
||||||
elements: [],
|
elements: [],
|
||||||
@ -304,7 +305,7 @@ export const useTimelineStore = create<TimelineStore>((set, get) => {
|
|||||||
: "Track";
|
: "Track";
|
||||||
|
|
||||||
const newTrack: TimelineTrack = {
|
const newTrack: TimelineTrack = {
|
||||||
id: crypto.randomUUID(),
|
id: generateUUID(),
|
||||||
name: trackName,
|
name: trackName,
|
||||||
type,
|
type,
|
||||||
elements: [],
|
elements: [],
|
||||||
@ -363,7 +364,7 @@ export const useTimelineStore = create<TimelineStore>((set, get) => {
|
|||||||
|
|
||||||
const newElement: TimelineElement = {
|
const newElement: TimelineElement = {
|
||||||
...elementData,
|
...elementData,
|
||||||
id: crypto.randomUUID(),
|
id: generateUUID(),
|
||||||
startTime: elementData.startTime || 0,
|
startTime: elementData.startTime || 0,
|
||||||
trimStart: 0,
|
trimStart: 0,
|
||||||
trimEnd: 0,
|
trimEnd: 0,
|
||||||
@ -556,7 +557,7 @@ export const useTimelineStore = create<TimelineStore>((set, get) => {
|
|||||||
const secondDuration =
|
const secondDuration =
|
||||||
element.duration - element.trimStart - element.trimEnd - relativeTime;
|
element.duration - element.trimStart - element.trimEnd - relativeTime;
|
||||||
|
|
||||||
const secondElementId = crypto.randomUUID();
|
const secondElementId = generateUUID();
|
||||||
|
|
||||||
updateTracksAndSave(
|
updateTracksAndSave(
|
||||||
get()._tracks.map((track) =>
|
get()._tracks.map((track) =>
|
||||||
@ -682,7 +683,7 @@ export const useTimelineStore = create<TimelineStore>((set, get) => {
|
|||||||
|
|
||||||
// Find existing audio track or prepare to create one
|
// Find existing audio track or prepare to create one
|
||||||
const existingAudioTrack = _tracks.find((t) => t.type === "audio");
|
const existingAudioTrack = _tracks.find((t) => t.type === "audio");
|
||||||
const audioElementId = crypto.randomUUID();
|
const audioElementId = generateUUID();
|
||||||
|
|
||||||
if (existingAudioTrack) {
|
if (existingAudioTrack) {
|
||||||
// Add audio element to existing audio track
|
// Add audio element to existing audio track
|
||||||
@ -706,7 +707,7 @@ export const useTimelineStore = create<TimelineStore>((set, get) => {
|
|||||||
} else {
|
} else {
|
||||||
// Create new audio track with the audio element in a single atomic update
|
// Create new audio track with the audio element in a single atomic update
|
||||||
const newAudioTrack: TimelineTrack = {
|
const newAudioTrack: TimelineTrack = {
|
||||||
id: crypto.randomUUID(),
|
id: generateUUID(),
|
||||||
name: "Audio Track",
|
name: "Audio Track",
|
||||||
type: "audio",
|
type: "audio",
|
||||||
elements: [
|
elements: [
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { MediaType } from "@/stores/media-store";
|
import { MediaType } from "@/stores/media-store";
|
||||||
|
import { generateUUID } from "@/lib/utils";
|
||||||
|
|
||||||
export type TrackType = "media" | "text" | "audio";
|
export type TrackType = "media" | "text" | "audio";
|
||||||
|
|
||||||
@ -111,7 +112,7 @@ export function ensureMainTrack(tracks: TimelineTrack[]): TimelineTrack[] {
|
|||||||
if (!hasMainTrack) {
|
if (!hasMainTrack) {
|
||||||
// Create main track if it doesn't exist
|
// Create main track if it doesn't exist
|
||||||
const mainTrack: TimelineTrack = {
|
const mainTrack: TimelineTrack = {
|
||||||
id: crypto.randomUUID(),
|
id: generateUUID(),
|
||||||
name: "Main Track",
|
name: "Main Track",
|
||||||
type: "media",
|
type: "media",
|
||||||
elements: [],
|
elements: [],
|
||||||
|
Reference in New Issue
Block a user