Skip to content

[Bug]: Folder breadcrumb sorting repeats locale setup #10191

Description

@AksharP5

Before submitting

  • I searched existing issues and did not find a duplicate.
  • I included enough detail to reproduce or investigate the problem.

Area

apps/web

Steps to reproduce

  1. In T3 Code, open a repository file and click a folder breadcrumb above the preview.
  2. fileBreadcrumbChildren filters the workspace index and sorts the folder's children. Each filename comparison calls localeCompare with options, repeating collation setup.
  3. For a repeatable source-level measurement, check out PR perf(web): speed up folder menu sorting #10190 and run the benchmark below from the repository root. It compares unchanged upstream code with the proposed patch using tracked repository paths. No app server is needed.

Expected behavior

Reuse comparison setup within a folder sort while preserving the current file order.

Actual behavior

The same locale and numeric-ordering options are processed for every filename comparison. In a warmed benchmark with this repository's actual 169-entry apps/web/src folder, the full helper took 1.39 ms before the fix and 0.20 ms after. Output was identical. This measures helper execution, not visible menu-opening latency.

Impact

Minor bug or occasional failure

Version or commit

main @ f8b4c46

Environment

Arch Linux / Omarchy, x64, Node 26.8.1. Source-level benchmark; no browser measurement.

Logs or stack traces

Folder                          Entries  Before   After
apps/web/src/components/files        33  0.408ms  0.154ms
apps/web/src/components             107  1.086ms  0.192ms
apps/web/src                        169  1.394ms  0.202ms
Reproducible benchmark

Save as /tmp/breadcrumb-sort-benchmark.mjs, then run node /tmp/breadcrumb-sort-benchmark.mjs from the PR checkout. Requires Node with TypeScript stripping, such as Node 24. Timings vary by machine.

import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
import { execFileSync } from 'node:child_process';
import { stripTypeScriptTypes } from 'node:module';
const root = process.cwd();
const path = 'apps/web/src/components/files/filePath.ts';
const sourceBefore = execFileSync('git', ['show', `f8b4c464b4760d73e0ece7e68011c738803d8b69:${path}`], { cwd: root, encoding: 'utf8' });
const sourceAfter = readFileSync(`${root}/${path}`, 'utf8');
// Imports support other exports; the measured export has no runtime dependencies.
const load = (source) => import('data:text/javascript;base64,' + Buffer.from(stripTypeScriptTypes(source.replace(/^import .*;\n/gm, ''))).toString('base64'));
const [before, after] = await Promise.all([load(sourceBefore), load(sourceAfter)]);
const paths = execFileSync('git', ['ls-files', 'apps', 'packages'], { cwd: root, encoding: 'utf8' }).trim().split('\n');
const map = new Map();
for (const path of paths) {
  map.set(path, { path, kind: 'file' });
  const segments = path.split('/');
  for (let i = 1; i < segments.length; i++) {
    const directory = segments.slice(0, i).join('/');
    map.set(directory, { path: directory, kind: 'directory' });
  }
}
const entries = [...map.values()];
const inputBefore = JSON.stringify(entries);
for (const directory of ['apps/web/src/components/files', 'apps/web/src/components', 'apps/web/src']) {
  const expected = before.fileBreadcrumbChildren(entries, directory);
  assert.deepEqual(after.fileBreadcrumbChildren(entries, directory), expected);
  const samples = [[], []];
  for (let round = 0; round < 19; round++) {
    for (const side of round % 2 ? [1, 0] : [0, 1]) {
      const start = performance.now();
      for (let repeat = 0; repeat < 40; repeat++) [before, after][side].fileBreadcrumbChildren(entries, directory);
      if (round >= 4) samples[side].push((performance.now() - start) / 40);
    }
  }
  const median = (values) => Number(values.toSorted((a, b) => a - b)[7].toFixed(3));
  console.log(JSON.stringify({ directory, indexedEntries: entries.length, children: expected.length, beforeMs: median(samples[0]), afterMs: median(samples[1]), identical: true }));
}
assert.equal(JSON.stringify(entries), inputBefore);
console.log(JSON.stringify({node:process.version,platform:process.platform,arch:process.arch,measurement:'full exported function, warmed, alternating order; not UI latency'}));

Workaround

None needed; menu behavior is correct. Fix prepared in #10190.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions