From 93d1e3383ce47e45157d70f9ba6251239dae6bc2 Mon Sep 17 00:00:00 2001 From: Maze <167211895+mazeincoding@users.noreply.github.com> Date: Mon, 23 Feb 2026 03:24:02 +0100 Subject: feat: major editor overhaul (assets, properties, timeline, fonts) (#709) * feat: major editor overhaul (assets, properties, timeline, fonts) Refactor editor core systems to standardize UI architecture and improve performance. Assets & Properties: - Replace monolithic property items with composable `Section` architecture. - Add specialized sections for Transform, Blending, and Text. - Implement `NumberField` with scrubbing and math evaluation. - Add new ColorPicker with EyeDropper and multiple format support. - Standardize asset panels using new `PanelView` layout. Fonts & Stickers: - Implement custom font atlas/sprite system for high-performance previews. - Add virtualized FontPicker with search and favorites. - Refactor stickers to use a provider-based architecture (icons, emoji, flags, shapes). - Standardize sticker IDs to `provider:value` format. Timeline & Interaction: - Convert bookmarks to rich objects with notes, colors, and duration. - Refactor drag-and-drop to use Command pattern (enabling proper undo/redo). - Add Shift modifier to disable snapping during moves/resizes. - Add new overlays for layout guides and text editing. Renderer: - Add support for multi-line text, custom line-height, and letter-spacing. - Implement global composite operation (blend modes). - Update sticker node to resolve dynamic provider IDs. Infrastructure: - Add storage migrations (v3->v6) for text weights, sticker IDs, and bookmarks. - Update global styles and core UI components (Button, Input, Popover). * add ts-nocheck directive to settings-legacy.tsx to suppress TypeScript errors * fix: correct global composite operation assignment in TextNode to ensure proper blend mode handling * deleted shadcn components with errors * formatting * fix linter issues * migrate from next middleware to proxy * add missing component back * add breadcrumb back * chore: add @radix-ui/react-primitive deps * chore: more deps * chore: add missing env vars to bun-ci * next env --- apps/web/src/hooks/use-sound-search.ts | 308 ++++++++++++++++----------------- 1 file changed, 154 insertions(+), 154 deletions(-) (limited to 'apps/web/src/hooks/use-sound-search.ts') diff --git a/apps/web/src/hooks/use-sound-search.ts b/apps/web/src/hooks/use-sound-search.ts index 263a01e2..f3c0aab4 100644 --- a/apps/web/src/hooks/use-sound-search.ts +++ b/apps/web/src/hooks/use-sound-search.ts @@ -1,154 +1,154 @@ -import { useEffect } from "react"; -import { useSoundsStore } from "@/stores/sounds-store"; - -export function useSoundSearch({ - query, - commercialOnly, -}: { - query: string; - commercialOnly: boolean; -}) { - const { - searchResults, - isSearching, - searchError, - lastSearchQuery, - currentPage, - hasNextPage, - isLoadingMore, - totalCount, - setSearchResults, - setSearching, - setSearchError, - setLastSearchQuery, - setCurrentPage, - setHasNextPage, - setTotalCount, - setLoadingMore, - appendSearchResults, - appendTopSounds, - resetPagination, - } = useSoundsStore(); - - const loadMore = async () => { - if (isLoadingMore || !hasNextPage) return; - - try { - setLoadingMore({ loading: true }); - const nextPage = currentPage + 1; - - const searchParams = new URLSearchParams({ - page: nextPage.toString(), - type: "effects", - }); - - if (query.trim()) { - searchParams.set("q", query); - } - - searchParams.set("commercial_only", commercialOnly.toString()); - const response = await fetch( - `/api/sounds/search?${searchParams.toString()}`, - ); - - if (response.ok) { - const data = await response.json(); - - if (query.trim()) { - appendSearchResults(data.results); - } else { - appendTopSounds(data.results); - } - - setCurrentPage({ page: nextPage }); - setHasNextPage({ hasNext: !!data.next }); - setTotalCount(data.count); - } else { - setSearchError({ error: `Load more failed: ${response.status}` }); - } - } catch (err) { - setSearchError({ - error: err instanceof Error ? err.message : "Load more failed", - }); - } finally { - setLoadingMore({ loading: false }); - } - }; - - useEffect(() => { - if (!query.trim()) { - setSearchResults({ results: [] }); - setSearchError({ error: null }); - setLastSearchQuery({ query: "" }); - return; - } - - if (query === lastSearchQuery && searchResults.length > 0) { - return; - } - - let ignore = false; - - const timeoutId = setTimeout(async () => { - try { - setSearching({ searching: true }); - setSearchError({ error: null }); - resetPagination(); - - const response = await fetch( - `/api/sounds/search?q=${encodeURIComponent(query)}&type=effects&page=1`, - ); - - if (!ignore) { - if (response.ok) { - const data = await response.json(); - setSearchResults({ results: data.results }); - setLastSearchQuery({ query: query }); - setHasNextPage({ hasNext: !!data.next }); - setTotalCount({ count: data.count }); - setCurrentPage({ page: 1 }); - } else { - setSearchError({ error: `Search failed: ${response.status}` }); - } - } - } catch (err) { - if (!ignore) { - setSearchError({ - error: err instanceof Error ? err.message : "Search failed", - }); - } - } finally { - if (!ignore) { - setSearching({ searching: false }); - } - } - }, 300); - - return () => { - clearTimeout(timeoutId); - ignore = true; - }; - }, [ - query, - lastSearchQuery, - searchResults.length, - setSearchResults, - setSearching, - setSearchError, - setLastSearchQuery, - setCurrentPage, - setHasNextPage, - setTotalCount, - resetPagination, - ]); - - return { - results: searchResults, - isLoading: isSearching, - error: searchError, - loadMore, - hasNextPage, - isLoadingMore, - totalCount, - }; -} +import { useEffect } from "react"; +import { useSoundsStore } from "@/stores/sounds-store"; + +export function useSoundSearch({ + query, + commercialOnly, +}: { + query: string; + commercialOnly: boolean; +}) { + const { + searchResults, + isSearching, + searchError, + lastSearchQuery, + currentPage, + hasNextPage, + isLoadingMore, + totalCount, + setSearchResults, + setSearching, + setSearchError, + setLastSearchQuery, + setCurrentPage, + setHasNextPage, + setTotalCount, + setLoadingMore, + appendSearchResults, + appendTopSounds, + resetPagination, + } = useSoundsStore(); + + const loadMore = async () => { + if (isLoadingMore || !hasNextPage) return; + + try { + setLoadingMore({ loading: true }); + const nextPage = currentPage + 1; + + const searchParams = new URLSearchParams({ + page: nextPage.toString(), + type: "effects", + }); + + if (query.trim()) { + searchParams.set("q", query); + } + + searchParams.set("commercial_only", commercialOnly.toString()); + const response = await fetch( + `/api/sounds/search?${searchParams.toString()}`, + ); + + if (response.ok) { + const data = await response.json(); + + if (query.trim()) { + appendSearchResults(data.results); + } else { + appendTopSounds(data.results); + } + + setCurrentPage({ page: nextPage }); + setHasNextPage({ hasNext: !!data.next }); + setTotalCount(data.count); + } else { + setSearchError({ error: `Load more failed: ${response.status}` }); + } + } catch (err) { + setSearchError({ + error: err instanceof Error ? err.message : "Load more failed", + }); + } finally { + setLoadingMore({ loading: false }); + } + }; + + useEffect(() => { + if (!query.trim()) { + setSearchResults({ results: [] }); + setSearchError({ error: null }); + setLastSearchQuery({ query: "" }); + return; + } + + if (query === lastSearchQuery && searchResults.length > 0) { + return; + } + + let ignore = false; + + const timeoutId = setTimeout(async () => { + try { + setSearching({ searching: true }); + setSearchError({ error: null }); + resetPagination(); + + const response = await fetch( + `/api/sounds/search?q=${encodeURIComponent(query)}&type=effects&page=1`, + ); + + if (!ignore) { + if (response.ok) { + const data = await response.json(); + setSearchResults({ results: data.results }); + setLastSearchQuery({ query: query }); + setHasNextPage({ hasNext: !!data.next }); + setTotalCount({ count: data.count }); + setCurrentPage({ page: 1 }); + } else { + setSearchError({ error: `Search failed: ${response.status}` }); + } + } + } catch (err) { + if (!ignore) { + setSearchError({ + error: err instanceof Error ? err.message : "Search failed", + }); + } + } finally { + if (!ignore) { + setSearching({ searching: false }); + } + } + }, 300); + + return () => { + clearTimeout(timeoutId); + ignore = true; + }; + }, [ + query, + lastSearchQuery, + searchResults.length, + setSearchResults, + setSearching, + setSearchError, + setLastSearchQuery, + setCurrentPage, + setHasNextPage, + setTotalCount, + resetPagination, + ]); + + return { + results: searchResults, + isLoading: isSearching, + error: searchError, + loadMore, + hasNextPage, + isLoadingMore, + totalCount, + }; +} -- cgit v1.3.1