Skip to content

Populate ChooseFile modal via file-meta entries in index#3949

Open
lukemelia wants to merge 5 commits intomainfrom
cs-10109-populate-choosefile-modal-via-file-meta-entries-in-index
Open

Populate ChooseFile modal via file-meta entries in index#3949
lukemelia wants to merge 5 commits intomainfrom
cs-10109-populate-choosefile-modal-via-file-meta-entries-in-index

Conversation

@lukemelia
Copy link
Contributor

@lukemelia lukemelia commented Feb 4, 2026

Summary

  • Replace HTTP directory listing requests with indexed file-meta queries for the ChooseFile modal
  • Instead of making separate HTTP requests for each directory level, query all files at once via the search API and build the tree client-side
  • Leverages SearchResource for Store integration, caching, reference counting, and live event subscription

Changes

File Description
packages/host/app/resources/file-tree-from-index.ts New resource that wraps SearchResource with FileDef type filter and builds tree structure
packages/host/app/components/editor/indexed-file-tree.gts New component that renders file tree using the resource
packages/host/app/components/operator-mode/choose-file-modal.gts Updated to use IndexedFileTree instead of FileTree

Test plan

  • Use the staging or production preview link
  • Open the AI Assistant.
  • Click the + button and choose Attach File
  • Verify files display in tree structure
  • Verify directory expand/collapse works
  • Verify file selection works
  • Verify switching realms refreshes the tree
  • Verify adding/removing files updates the tree (via event subscription)

Closes CS-10109

🤖 Generated with Claude Code

@github-actions
Copy link

github-actions bot commented Feb 4, 2026

Preview deployments

@github-actions
Copy link

github-actions bot commented Feb 4, 2026

Host Test Results

    1 files  ±0      1 suites  ±0   1h 48m 56s ⏱️ + 2m 16s
1 944 tests ±0  1 927 ✅ ±0  17 💤 ±0  0 ❌ ±0 
1 959 runs  ±0  1 942 ✅ ±0  17 💤 ±0  0 ❌ ±0 

Results for commit 0458b9c. ± Comparison against base commit 4830d89.

♻️ This comment has been updated with latest results.

@lukemelia lukemelia force-pushed the cs-10109-populate-choosefile-modal-via-file-meta-entries-in-index branch from c8da34e to 061876a Compare February 5, 2026 19:35
lukemelia and others added 4 commits February 5, 2026 17:41
Replace HTTP directory listing requests with indexed file-meta queries
for the ChooseFile modal. Instead of making separate HTTP requests for
each directory level as users expand the tree, we now query all files
at once via the search API and build the tree client-side.

Changes:
- Add FileTreeFromIndexResource that wraps SearchResource with FileDef
  type filter and builds a tree structure from flat file instances
- Add IndexedFileTree component that renders the tree using the resource
- Update ChooseFileModal to use IndexedFileTree instead of FileTree

The new implementation leverages SearchResource for Store integration,
caching, reference counting, and live event subscription.

Closes CS-10109

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Use private class field (#realmURL) instead of tracked property to avoid
"attempted to update but it had already been used" error during render.
Private fields are not automatically tracked by Glimmer, which prevents
the conflict when modify() updates the URL after entries getter has read it.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The comparison to skip re-creating the search was causing issues when
switching realms - the search resource's realms function was evaluated
with stale data. The SearchResource handles deduplication internally,
so we can safely remove this optimization.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Use {{#each}} with single-element array to force component recreation
when the realm URL changes. This ensures the FileTreeFromIndexResource
is properly re-initialized with the new realm URL.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@lukemelia lukemelia force-pushed the cs-10109-populate-choosefile-modal-via-file-meta-entries-in-index branch from 061876a to 07c14a0 Compare February 5, 2026 22:41
Show a centered LoadingIndicator spinner in the file tree mask overlay
while the search query is still loading, so users see feedback instead
of a blank white area in the ChooseFileModal.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@lukemelia lukemelia marked this pull request as ready for review February 6, 2026 01:25
@lukemelia lukemelia requested review from a team, backspace and Copilot February 6, 2026 01:25
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR replaces HTTP directory listing requests with indexed file-meta queries for the ChooseFile modal. Instead of making separate HTTP requests for each directory level as users expand directories, the new implementation queries all files at once via the search API and builds the tree structure client-side. This approach leverages SearchResource for Store integration, caching, reference counting, and live event subscription, which should improve performance and provide real-time updates when files are added or removed.

Changes:

  • Introduces FileTreeFromIndexResource that queries all FileDef instances in a realm and builds a tree structure from them
  • Creates IndexedFileTree component that renders the file tree using the new resource
  • Updates choose-file-modal to use the new IndexedFileTree component with a component recreation pattern when realms switch

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

File Description
packages/host/app/resources/file-tree-from-index.ts New resource that wraps SearchResource with FileDef type filter, queries all files at once, and builds hierarchical tree structure from flat file list
packages/host/app/components/editor/indexed-file-tree.gts New component that renders file tree using FileTreeFromIndexResource, manages directory expansion state, and provides loading indicators
packages/host/app/components/operator-mode/choose-file-modal.gts Updates to use IndexedFileTree instead of FileTree, with #each pattern to force component recreation when realm changes

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

onFileSelected: (entryPath: LocalPath) => void;
onDirectorySelected: (entryPath: LocalPath) => void;
scrollPositionKey?: LocalPath;
relativePath: string;
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The relativePath parameter is declared in the TreeLevelSignature but is never used in the TreeLevel component. It's passed down recursively but has no effect on the component's behavior. Consider removing it if it's not needed, or document why it's being tracked for future use.

Copilot uses AI. Check for mistakes.

class TreeLevel extends Component<TreeLevelSignature> {
<template>
{{#each @entries as |entry|}}
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The #each loop should include a key parameter for better rendering performance and correctness. The old Directory component uses key='path' which should be added here as well. Without a key, Ember may not correctly track which items have changed when the list updates, potentially causing rendering issues or unnecessary re-renders.

Suggested change
{{#each @entries as |entry|}}
{{#each @entries key="path" as |entry|}}

Copilot uses AI. Check for mistakes.
Comment on lines +76 to +81
// We query with FileDef type filter, so instances are FileDef
let files = this.search.instances as FileDef[];
let tree = this.buildTreeFromFiles(files);
return this.sortEntries(tree);
}

Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The type assertion as FileDef[] assumes all instances are FileDef without runtime validation. If the search returns unexpected types (e.g., due to index inconsistencies or race conditions), this could cause issues downstream. Consider adding runtime validation or a filter to ensure only FileDef instances are processed.

Suggested change
// We query with FileDef type filter, so instances are FileDef
let files = this.search.instances as FileDef[];
let tree = this.buildTreeFromFiles(files);
return this.sortEntries(tree);
}
let instances = this.search.instances ?? [];
let files = instances.filter((instance): instance is FileDef =>
this.isFileDef(instance),
);
let tree = this.buildTreeFromFiles(files);
return this.sortEntries(tree);
}
private isFileDef(instance: CardDef | FileDef): instance is FileDef {
let anyInstance = instance as any;
return (
anyInstance?.type?.module === 'https://cardstack.com/base/file-api' &&
anyInstance?.type?.name === 'FileDef'
);
}

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant