Skip to content

Conversation

HarshMN2345
Copy link
Member

@HarshMN2345 HarshMN2345 commented Oct 6, 2025

What does this PR do?

Auto-opens root and navigates to typed path, move to svlete5 , disable save when unchanged

update in pink for selection appwrite/pink#368

Test Plan

in discord

Related PRs and Issues

(If this PR is related to any other PR or resolves any issue or related to any issue link all related PR and issues here.)

Have you read the Contributing Guidelines on issues?

yes

Summary by CodeRabbit

  • New Features

    • Enhanced directory picker with incremental loading and path-based expansion for faster navigation in large repos.
    • Improved selection workflow with clearer state and Save enabled only when changes are made.
  • Bug Fixes

    • More robust loading and error handling to prevent stalls and ensure consistent behavior during directory exploration.
  • Chores

    • Updated UI dependencies to the latest prebuilt versions for icons and components.

@HarshMN2345 HarshMN2345 requested a review from ItzNotABug October 6, 2025 19:37
Copy link
Contributor

coderabbitai bot commented Oct 6, 2025

Walkthrough

  • package.json: Updated pins for @appwrite.io/pink-icons-svelte and @appwrite.io/pink-svelte from 8f82877 to aa2ed6e.
  • src/lib/components/git/selectRootModal.svelte: Removed exported props (show, rootDir, product, branch) and switched to a destructured $props() object. Reworked initialization into reactive, incremental directory loading with new helpers (detectRuntimeOrFramework, getDirByPath, loadPath, expandToPath, normalizePath). Added path-based expansion, caching, loading flags (isLoading, isFetching), and error handling. Updated selection handling via handleSelect/currentPath. Save button now considers hasChanges.

Possibly related PRs

Suggested reviewers

  • ItzNotABug
  • lohanidamodar

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title clearly captures the primary focus of the changeset—enhancing the root directory modal’s behavior and UX—and aligns with the detailed component refactor described in the PR. It is concise, specific, and omits unrelated details such as dependency updates. As a result, it provides a clear summary for anyone scanning the commit history.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix-SER-428-Quality-Check-root-drectory-modal-issue

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (4)
src/lib/components/git/selectRootModal.svelte (4)

181-185: Harden path normalization (leading and duplicate slashes)

Current logic can produce .//foo for inputs like /foo. Collapse duplicates and strip leading slashes.

-    function normalizePath(path: string): string {
-        if (!path || path === './') return './';
-        const trimmed = path.replace(/\/$/, '');
-        return trimmed.startsWith('./') ? trimmed : `./${trimmed}`;
-    }
+    function normalizePath(path: string): string {
+        if (!path || path === './') return './';
+        let p = path.trim();
+        p = p.replace(/^\/+/, '');     // remove leading slashes
+        p = p.replace(/\/+$/, '');     // remove trailing slashes
+        p = p.replace(/\/{2,}/g, '/'); // collapse duplicate slashes
+        if (p === '' || p === '.') return './';
+        return p.startsWith('./') ? p : `./${p}`;
+    }

193-211: Avoid shadowing currentPath local vs state

Local let currentPath = './' shadows the state variable, which is confusing and risks bugs. Use a different local name.

-        let currentDir = directories[0];
-        let currentPath = './';
+        let currentDir = directories[0];
+        let accPath = './';
 
         for (const segment of segments) {
-            currentPath = currentPath === './' ? `./${segment}` : `${currentPath}/${segment}`;
+            accPath = accPath === './' ? `./${segment}` : `${accPath}/${segment}`;
 
             // Load the parent directory if not already loaded
             await loadPath(currentDir.fullPath);
 
             // Find the next directory
             const nextDir = currentDir.children?.find((d) => d.title === segment);
             if (!nextDir) return; // Path doesn't exist
 
             currentDir = nextDir;
-            expandedStore.update((exp) => [...new Set([...exp, currentPath])]);
+            expandedStore.update((exp) => [...new Set([...exp, accPath])]);
         }
 
-        currentPath = normalized;
+        // set selection to final normalized path
+        currentPath = normalized;

129-131: Nit: fix comment typo

"donee" → "done". Improves readability.

-        // skip loading if this directory was donee
+        // skip loading if this directory was done

223-229: Optional: reset expanded state on close

Consider clearing expandedStore when closing so reopening starts from a clean tree (unless intentional to persist expansions).

     $effect(() => {
         if (!show && initialized) {
             initialized = false;
+            expandedStore.set([]);
         }
     });
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a0227bd and 45e9a40.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (2)
  • package.json (1 hunks)
  • src/lib/components/git/selectRootModal.svelte (3 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: e2e
🔇 Additional comments (4)
package.json (1)

27-29: Pins for pink packages updated — verify compatibility and lockfile integrity

Upgrading to aa2ed6e looks fine. Please:

  • Ensure these builds are compatible with Svelte 5 and DirectoryPicker API used here.
  • Regenerate pnpm-lock and run e2e/visual checks for icon/component regressions.
src/lib/components/git/selectRootModal.svelte (3)

2-2: Confirm $app/state import

SvelteKit typically exposes page from $app/stores. Ensure $app/state is valid for your Kit version (2.42.1). If not, switch to $app/stores.


21-31: branch is required — ensure it’s always provided

No default is set for branch, yet it’s used in API calls. Verify parent always passes it to avoid runtime errors.


248-259: Save UX check looks good

Disabling Save when isLoading or no changes (!hasChanges) matches the PR goal.

Please verify DirectoryPicker’s expanded expects a writable store (not an array) and that openTo accepts normalized paths as implemented.

Comment on lines +88 to +101
directories[0] = {
...directories[0],
fileCount: content.contents?.length ?? 0,
children: content.contents
.filter((e) => e.isDirectory)
.map((dir) => ({
title: dir.name,
fullPath: `./${dir.name}`,
fileCount: undefined,
// set logo for root directories
thumbnailUrl: dir.name,
loading: false
}))
};
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Prevent crash when contents is undefined

You use ?.length but then call .filter/.map on content.contents directly. If contents is absent, this will throw at runtime.

Apply:

-                directories[0] = {
+                const contents = content.contents ?? [];
+                directories[0] = {
                     ...directories[0],
-                    fileCount: content.contents?.length ?? 0,
-                    children: content.contents
+                    fileCount: contents.length,
+                    children: contents
                         .filter((e) => e.isDirectory)
                         .map((dir) => ({
                             title: dir.name,
                             fullPath: `./${dir.name}`,
                             fileCount: undefined,
                             // set logo for root directories
                             thumbnailUrl: dir.name,
                             loading: false
                         }))
                 };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
directories[0] = {
...directories[0],
fileCount: content.contents?.length ?? 0,
children: content.contents
.filter((e) => e.isDirectory)
.map((dir) => ({
title: dir.name,
fullPath: `./${dir.name}`,
fileCount: undefined,
// set logo for root directories
thumbnailUrl: dir.name,
loading: false
}))
};
const contents = content.contents ?? [];
directories[0] = {
...directories[0],
fileCount: contents.length,
children: contents
.filter((e) => e.isDirectory)
.map((dir) => ({
title: dir.name,
fullPath: `./${dir.name}`,
fileCount: undefined,
// set logo for root directories
thumbnailUrl: dir.name,
loading: false
}))
};
🤖 Prompt for AI Agents
In src/lib/components/git/selectRootModal.svelte around lines 88 to 101, the
code uses content.contents?.length but then calls .filter/.map on
content.contents directly which will throw if contents is undefined; change the
children construction to operate on a safe default (e.g. const entries =
content.contents ?? [] and use entries.filter(...).map(...)) so children is
built from an empty array when contents is absent and keep fileCount as
content.contents?.length ?? 0.

Comment on lines +137 to +156
try {
const content = await sdk
.forProject(page.params.region, page.params.project)
.vcs.getRepositoryContents({
installationId: $installation.$id,
providerRepositoryId: $repository.id,
providerRootDirectory: path,
providerReference: branch
});
const fileCount = content.contents?.length ?? 0;
const contentDirectories = content.contents.filter((e) => e.isDirectory);
if (contentDirectories.length === 0) {
expandedStore.update((exp) => [...new Set([...exp, path])]);
return;
}
targetDir.fileCount = fileCount;
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Set fileCount before early return and guard .filter usage

Empty directories never set fileCount, so loadPath will keep refetching that path. Also guard against undefined contents.

-            const fileCount = content.contents?.length ?? 0;
-            const contentDirectories = content.contents.filter((e) => e.isDirectory);
+            const contents = content.contents ?? [];
+            const fileCount = contents.length;
+            const contentDirectories = contents.filter((e) => e.isDirectory);
+
+            // mark as loaded regardless of emptiness
+            targetDir.fileCount = fileCount;
 
-            if (contentDirectories.length === 0) {
+            if (contentDirectories.length === 0) {
                 expandedStore.update((exp) => [...new Set([...exp, path])]);
                 return;
             }
 
-            targetDir.fileCount = fileCount;
+            // children exist...

Also applies to: 147-156

🤖 Prompt for AI Agents
In src/lib/components/git/selectRootModal.svelte around lines 137 to 156, the
code reads content.contents then filters it but returns early for empty
directories without setting targetDir.fileCount and doesn't guard against
undefined contents; compute fileCount first using optional chaining (e.g. const
fileCount = content.contents?.length ?? 0), assign targetDir.fileCount before
any early return, use a safe array for filtering (e.g. const contentDirectories
= (content.contents ?? []).filter(...)), and then update expandedStore and
return if no directories remain so fileCount is always set.

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