Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,7 @@
## 2025-02-15 - Replace Array.from(map.values()).map with a for...of loop
**Learning:** Using `Array.from(map.values()).map(...)` creates an unnecessary intermediate array which wastes memory allocation and garbage collection time, particularly for frequently re-rendered components handling large collections.
**Action:** Use a `for...of` loop over `map.values()` to iterate and push mapped elements directly into the final array for O(1) memory and avoiding intermediate array allocations.

## 2023-07-05 - Avoid intermediate array iteration and apply early breaks for minimum search
**Learning:** `song?.sections.reduce(...)` causes unconditional O(N) operations and `Array.from({ length: X }).map(...)` creates intermediate arrays of `undefined` values, reducing React rendering performance.
**Action:** Replace `reduce` with a `for...of` loop with an early `break` when searching for absolute bounds (like a low confidence level), and use the built-in mapping argument `Array.from({ length: X }, ...)` to avoid allocating intermediate arrays.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## [0.1.4] - 2023-07-05
### 성능 (Performance)
- React 컴포넌트(`App.tsx`, `Workspace.tsx`)에서 `Array.from` 매핑 시 중간 배열이 생성되지 않도록 built-in 인자를 활용하여 최적화
- `ConfidenceMetric` 컴포넌트 내 `reduce`를 이용한 최솟값 탐색 시 `low` confidence를 찾으면 바로 조기 종료(early break)하도록 개선하여 O(N) 연산을 최적화

# Changelog

## [Unreleased]
Expand Down
27 changes: 27 additions & 0 deletions apps/desktop/src/App.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,33 @@ describe("App", () => {
expect(screen.getAllByText(/2 sections/i).length).toBeGreaterThan(0);
});

it("short-circuits confidence summarization when encountering a low-confidence section", async () => {
const loadedProject = succeededResult().result;
loadedProject.sections.push(
{
...loadedProject.sections[0],
id: "verse-2",
label: "verse",
confidence: { level: "low", source: "model", notes: "Hard to hear." }
},
{
...loadedProject.sections[0],
id: "chorus-2",
label: "chorus",
confidence: { level: "high", source: "model", notes: "Very clear." }
}
);
mockLoadProject.mockResolvedValueOnce(loadedProject);
render(<App />);

fireEvent.click(screen.getByRole("button", { name: /open project/i }));

await waitFor(() => {
expect(screen.getByText(/^Low$/i)).toBeTruthy();
});
expect(screen.getAllByText(/3 sections/i).length).toBeGreaterThan(0);
});

it("selects a local audio source and starts a local-audio analysis job", async () => {
tauriInvoke
.mockResolvedValueOnce(bootstrapResponse())
Expand Down
21 changes: 12 additions & 9 deletions apps/desktop/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -180,15 +180,18 @@ function MetricCard({
function ConfidenceMetric({ song }: { song: RehearsalSong | null }) {
const sectionCount = song?.sections.length ?? 0;
const confidenceOrder = { high: 3, medium: 2, low: 1 } as const;
const lowestConfidence = song?.sections.reduce<RehearsalSong["sections"][number]["confidence"]["level"] | null>(
(current, section) => {
if (!current || confidenceOrder[section.confidence.level] < confidenceOrder[current]) {
return section.confidence.level;
let lowestConfidence: RehearsalSong["sections"][number]["confidence"]["level"] | null = null;
if (song && song.sections.length > 0) {
lowestConfidence = "high";
for (const section of song.sections) {
if (confidenceOrder[section.confidence.level] < confidenceOrder[lowestConfidence]) {
lowestConfidence = section.confidence.level;
if (lowestConfidence === "low") {
break; // O(1) early exit when finding lowest possible confidence
}
}
return current;
},
null
);
}
}
const confidence = lowestConfidence ? `${lowestConfidence[0].toUpperCase()}${lowestConfidence.slice(1)}` : "Ready";
const detail = sectionCount > 0 ? `${sectionCount} section${sectionCount === 1 ? "" : "s"}` : "Local analysis";

Expand Down Expand Up @@ -523,7 +526,7 @@ export function App() {
</p>
<div className="mt-3 h-14 overflow-hidden rounded-xl bg-[linear-gradient(90deg,rgba(34,211,238,.12),rgba(124,58,237,.12))]">
<div className="flex h-full items-end gap-0.5 px-2 pb-1" aria-hidden="true">
{Array.from({ length: 34 }).map((_, index) => (
{Array.from({ length: 34 }, (_, index) => (
<span
key={index}
className="w-1 rounded-t bg-gradient-to-t from-cyan-400 to-violet-400"
Expand Down
2 changes: 1 addition & 1 deletion apps/desktop/src/features/workspace/Workspace.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ const SongStructure = memo(function SongStructure({ sections, t }: { sections: R

<div className="relative min-w-[720px] border-t border-white/10 px-3 py-6" aria-hidden="true">
<div className="flex h-24 items-center gap-1 overflow-hidden">
{Array.from({ length: 84 }).map((_, index) => (
{Array.from({ length: 84 }, (_, index) => (
<span
key={index}
className="w-1 flex-none rounded-full bg-gradient-to-t from-cyan-500 via-sky-400 to-violet-400 opacity-85"
Expand Down
Loading