Skip to content

Migrate to monorepo using Turborepo#17

Merged
keizer619 merged 6 commits intoballerina-platform:mainfrom
snelusha:feat/monorepo
Mar 31, 2026
Merged

Migrate to monorepo using Turborepo#17
keizer619 merged 6 commits intoballerina-platform:mainfrom
snelusha:feat/monorepo

Conversation

@snelusha
Copy link
Copy Markdown
Contributor

@snelusha snelusha commented Mar 27, 2026

Purpose

Resolves #10

Migration to Monorepo Using Turborepo

This pull request restructures the project into a monorepo architecture using Turborepo and npm workspaces, resolving issue #10.

Core Structural Changes

Directory Reorganization:

  • Established new monorepo structure with apps/web and packages/wasm workspaces
  • Updated .gitmodules to relocate the wasm submodule from wasm/ to packages/wasm/ballerina-lang-go
  • Created isolated package directories for improved modularity and build optimization

Root Configuration:

  • Added root-level package.json defining the @playground/monorepo with workspace declarations for apps/* and packages/*
  • Implemented npm workspace configuration to enable coordinated dependency management across packages
  • Pinned package manager to bun@1.3.10 for consistency
  • Configured root scripts using Turborepo for task orchestration (turbo run dev, turbo run build) and Biome for linting/formatting

Build System Integration

Turborepo Configuration:

  • Added turbo.json at root level with build and dev task definitions including caching strategies and persistence settings
  • Created apps/web/turbo.json with task pipeline dependency on @playground/wasm#build and automated copy:wasm task
  • Created packages/wasm/turbo.json with cache inputs (Go source files, go.mod, go.sum) and outputs for reproducible builds
  • Implemented copy:wasm task to automatically copy compiled WASM output from packages into the web app public directory

Package-Specific Updates:

  • Renamed apps/web package from web to @playground/web
  • Created @playground/wasm package with Go-to-WebAssembly build automation via npm scripts
  • Established per-package Biome configurations for localized linting and formatting rules

File Sharing and Collaboration Features

Share Functionality:

  • Implemented share-via-URL capability with payload encoding/decoding (apps/web/src/lib/share.ts)
  • Added ShareNoticeDialog component to inform users about temporary shared links
  • Created useShare, useCopyShareLink, and useShareNotice hooks to manage the sharing workflow
  • Extended file-tree operations to support fork functionality for users to copy shared files to their local workspace

Enhanced File System & UI:

  • Created FileRouteSync component to synchronize TanStack Router URL parameters with active file paths in the store
  • Added file-tree utilities for organizing and retrieving examples and shared files
  • Implemented path manipulation utilities for safe relative path handling, shared-to-local conversion, and fork target path generation
  • Extended LayeredFS with graftSharedTree method to load shared file hierarchies
  • Updated FileTreeStore with loadSharedFiles action for managing shared file operations
  • Created Checkbox and Toaster UI components for improved user interaction with share notices and notifications

Data and Example Content:

  • Added apps/web/src/assets/examples.json with three Ballerina code examples demonstrating various language features
  • Introduced URL-to-store synchronization in root route layout for automatic file opening from URLs
  • Updated route validation to sanitize and process the share query parameter

CI/CD Pipeline Updates

GitHub Actions Workflow Changes:

  • Updated Go module cache paths from wasm/ to packages/wasm/ in both CI and deployment workflows
  • Removed dedicated WASM compilation step from CI (now handled by package build scripts via Turborepo)
  • Simplified Bun installation to run from repository root instead of the web/ directory
  • Updated web app build invocation to use bun run build --filter=@playground/web for scoped package execution
  • Adjusted GitHub Pages artifact upload path from web/dist to apps/web/dist

Configuration Cleanup

  • Updated .gitignore to reflect monorepo structure (ignoring node_modules, .bun, .turbo, and package-specific dist directories)
  • Removed global Biome configuration from root and delegated per-package configuration
  • Created biome.json files at apps/web/ and packages/wasm/ for package-specific linting/formatting rules

Dependency Updates

  • Added Turborepo and Biome as root-level development dependencies
  • Added sonner library to web app dependencies for toast notifications
  • Removed local @biomejs/biome from web app dependencies (now managed at root level)

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 27, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR restructures the codebase into a monorepo using Turborepo and Bun workspaces, relocating the WASM package to packages/wasm/, consolidating build tooling configuration at the root with package-level overrides, refactoring CI/CD workflows to match the new structure, and adding file-sharing and file-forking functionality with corresponding filesystem utilities and UI components.

Changes

Cohort / File(s) Summary
Monorepo Foundation
.gitignore, .gitmodules, package.json, turbo.json
Root-level workspace setup with Bun package manager pinning, Turbo orchestration for dev/build, Biome linting/formatting tasks. Narrowed .gitignore to ignore node_modules, .bun, .turbo, and dist directories. Updated submodule path from wasm/ to packages/wasm/.
Build System & Tooling
biome.json, apps/web/biome.json, apps/web/turbo.json, packages/wasm/biome.json, packages/wasm/turbo.json
Root Biome config simplified by removing per-file includes/CSS parser settings; package-level configs inherit and specialize via "extends": "//". Web app Turbo adds copy:wasm task and caching rules. WASM Turbo caches build task with Go source inputs.
CI/CD Workflows
.github/workflows/ci.yml, .github/workflows/deploy.yml
Updated cache paths from wasm/ to packages/wasm/. Removed standalone WASM build step, replaced with Turbo-driven tasks. Changed Bun invocations to run from repo root using --filter=@playground/web. Updated artifact upload path from web/dist to apps/web/dist.
WASM Package
packages/wasm/package.json, packages/wasm/.gitignore
Added @playground/wasm package with clean and build scripts for cross-compiling Go to WebAssembly.
Web App Metadata
apps/web/package.json
Renamed package to @playground/web, removed version, added copy:wasm script and sonner dependency, removed Biome and preview script.
File Sharing Feature — Hooks
apps/web/src/hooks/use-share.ts, apps/web/src/hooks/use-copy-share-link.ts, apps/web/src/hooks/use-share-notice.ts
Hooks managing share URL decoding/loading, clipboard copy with toast feedback, and persistent dismissal of share notice dialog.
File Sharing Feature — Utilities
apps/web/src/lib/share.ts, apps/web/src/lib/fs/fs-roots.ts, apps/web/src/lib/fs/core/path-utils.ts, apps/web/src/lib/fs/core/file-node-utils.ts, apps/web/src/lib/file-tree-utils.ts
New path validation/mapping, file-node tree construction, gzip+Base64 share payload encode/decode, and subtree navigation utilities supporting shared/example/local file organization.
Filesystem Layer
apps/web/src/lib/fs/ephemeral-fs.ts, apps/web/src/lib/fs/layered-fs.ts
Extended EphemeralFS with insertSubtree() method. Updated LayeredFS to use centralized root constants and added graftSharedTree() to integrate shared files with optional file opening.
File Tree & Sidebar
apps/web/src/components/app-sidebar.tsx, apps/web/src/stores/file-tree-store.ts
Added share/fork UI actions to file nodes with visual muting, fork destination resolution, and dialog integration. Extended store with loadSharedFiles() action and auto-expansion of ancestor directories when opening/moving files. Dialog kinds now include fork-file and fork-folder.
File Tree Dialog
apps/web/src/components/file-tree-dialog.tsx
Added fork-file/fork-folder operation types with corresponding titles/descriptions and submit handler using getForkTargetPath() for destination resolution.
UI Components — Sharing & Notifications
apps/web/src/components/share-notice-dialog.tsx, apps/web/src/components/ui/checkbox.tsx, apps/web/src/components/ui/sonner.tsx
New ShareNoticeDialog with persistent dismissal toggle, Checkbox wrapper around Base UI, and Sonner Toaster with Hugeicons and custom styling.
Route Sync & Root Layout
apps/web/src/components/file-route-sync.tsx, apps/web/src/routes/__root.tsx, apps/web/src/routes/$.tsx
New FileRouteSync component synchronizing URL _splat parameter with active file path and integrating ShareNoticeDialog. Root route wraps Outlet with TooltipProvider and Toaster. Splat route adds validateSearch for share parameter normalization.
Assets & Styles
apps/web/src/assets/examples.json, apps/web/src/styles.css, apps/web/src/main.tsx
New examples JSON with three Ballerina sample programs. Unified font-sans application via universal selector in base layer instead of html element. Added missing semicolon in router import.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant Router as TanStack Router
    participant FileRouteSync as FileRouteSync
    participant FileTreeStore as File Tree Store
    participant Share as Share Lib
    participant LayeredFS as LayeredFS
    participant ShareNotice as ShareNoticeDialog

    User->>Router: Click shared link (/?share=encoded)
    Router->>FileRouteSync: Route with share param
    FileRouteSync->>Share: decodeSharePayload(share)
    Share-->>FileRouteSync: FileNode root + openRelativePath
    FileRouteSync->>FileTreeStore: loadSharedFiles(root, openRelativePath)
    FileTreeStore->>LayeredFS: graftSharedTree(root, openRelativePath)
    LayeredFS-->>FileTreeStore: file path to open
    FileTreeStore->>FileTreeStore: openFile(path), expandAncestors(path)
    FileTreeStore-->>FileRouteSync: active file updated
    FileRouteSync->>ShareNotice: show()
    ShareNotice-->>User: "This is a temporary shared file"
Loading
sequenceDiagram
    actor User
    participant Sidebar as App Sidebar
    participant Dialog as File Tree Dialog
    participant PathUtils as Path Utils
    participant FileTreeStore as File Tree Store
    participant FS as FileSystem

    User->>Sidebar: Right-click shared file → Fork
    Sidebar->>PathUtils: getForkTargetPath(sharedPath, newName)
    PathUtils-->>Sidebar: localDestPath
    Sidebar->>FS: stat(localDestPath)
    alt Destination exists
        FS-->>Sidebar: exists
        Sidebar->>Dialog: open fork-file dialog
        User->>Dialog: Confirm/rename
    else Destination doesn't exist
        FS-->>Sidebar: not found
        Sidebar->>FileTreeStore: renameFile(sharedPath, localDestPath)
    end
    FileTreeStore->>FileTreeStore: expandDir(parent)
    FileTreeStore-->>User: File forked to local
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

  • PR #16: Implements shareable-links feature with matching code-level changes across share payload encode/decode, URL generation, file loading, and UI components for the same sharing workflow.
  • PR #13: Continues TanStack Router ↔ file-tree synchronization work, modifying FileRouteSync, root layout, and file-tree store integration for route-based file navigation.
  • PR #7: Extends filesystem abstraction layer (Layered/Ephemeral FS, path utilities, and WASM runtime FS proxy) that this PR now uses for shared-file grafting and path mapping.

Suggested reviewers

  • warunalakshitha

Poem

🐰 A monorepo hops with Turbo's speed,
Shared links plant seeds where forked files breed,
Checkboxes twitch, toasts spin round—
The bunny's feast, at last, is found! 🥕✨

🚥 Pre-merge checks | ✅ 1 | ❌ 4

❌ Failed checks (2 warnings, 2 inconclusive)

Check name Status Explanation Resolution
Description check ⚠️ Warning The pull request description only states 'Resolves #10' without addressing required template sections like Purpose, Goals, Approach, and other critical documentation areas. Expand the description to include Purpose (problem/issue details), Goals (solutions), and Approach (implementation details), following the repository's template structure.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Linked Issues check ❓ Inconclusive Linked issue #10 'Migrate to monorepo using Turborepo' lacks detailed coding requirements; the pull request adds monorepo tooling (Turborepo, root package.json, workspace configuration, biome setup) and refactors the file structure to support this architecture. Review issue #10 to verify all specified requirements are met; ensure the monorepo structure aligns with the issue's documented objectives.
Out of Scope Changes check ❓ Inconclusive Most changes align with monorepo migration (Turborepo config, workspace setup, file tree restructuring), but new features like share functionality, example files, and UI components (ShareNoticeDialog, Checkbox, Toaster) appear tangentially related or potentially out of scope. Clarify whether share/fork functionality and new UI components are part of the monorepo migration or separate features that should be addressed in different PRs.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Migrate to monorepo using Turborepo' clearly and specifically describes the primary change in the pull request across all modified files.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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
Copy Markdown

@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: 3

🧹 Nitpick comments (1)
apps/web/package.json (1)

8-8: Cross-platform compatibility: cp command won't work on Windows.

The copy:wasm script uses the Unix cp command, which will fail on Windows systems. Consider using a cross-platform solution.

♻️ Suggested alternatives

Option 1: Use Node.js built-in fs.cp via a one-liner:

-		"copy:wasm": "cp ../../packages/wasm/dist/ballerina.wasm public/ballerina.wasm"
+		"copy:wasm": "node -e \"require('fs').cpSync('../../packages/wasm/dist/ballerina.wasm', 'public/ballerina.wasm')\""

Option 2: Use shx package for cross-platform shell commands:

-		"copy:wasm": "cp ../../packages/wasm/dist/ballerina.wasm public/ballerina.wasm"
+		"copy:wasm": "shx cp ../../packages/wasm/dist/ballerina.wasm public/ballerina.wasm"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/package.json` at line 8, The npm script "copy:wasm" in package.json
uses the Unix-only `cp` command which breaks on Windows; replace it with a
cross-platform approach by either (a) changing the script to run a Node
one-liner that uses fs.cp/fs.copyFile (e.g., a small node -e script) to copy
../../packages/wasm/dist/ballerina.wasm to public/ballerina.wasm, or (b) add a
devDependency like "shx" or "cpy-cli" and update "copy:wasm" to use the
cross-platform command provided by that package; update package.json's
"copy:wasm" script accordingly and add the chosen package to devDependencies if
needed.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/web/biome.json`:
- Around line 4-13: The files.includes negation patterns use double-bang
prefixes (e.g., "!!**/dist", "!!src/main.tsx", "!!src/assets/**/*",
"!!src/wasm_exec.js", "!!src/types/wasm-types.d.ts", "!!**/routeTree.gen.ts")
but Biome expects single '!' negation; update the entries under files.includes
to use single '!' (e.g., "!**/dist", "!src/main.tsx", "!src/assets/**/*",
"!src/wasm_exec.js", "!src/types/wasm-types.d.ts", "!**/routeTree.gen.ts") and
for directory exclusions prefer the simpler "!dist" form where applicable to
improve performance.

In `@package.json`:
- Line 15: The root package.json "preview" script currently runs "vite preview"
but will fail because Vite config isn't at the repo root; remove or replace the
root "preview" script and instead add a "preview" script with the same command
to the web app's package.json (the package.json for the web app that contains
the vite.config), then register a "preview" pipeline entry in turbo.json so the
root task delegates to the app's preview task; update the root package.json to
either call turbo run preview or remove the invalid script.

In `@packages/wasm/package.json`:
- Around line 5-6: The build script fails when dist is missing because "clean"
deletes it and "build" (the npm script named build) invokes go build -o
dist/ballerina.wasm without creating the parent directory; update the build
script (the "build" npm script) to ensure the dist directory exists before
running go build (e.g., create the directory with mkdir -p dist or equivalent)
so go build can write dist/ballerina.wasm even on a clean workspace.

---

Nitpick comments:
In `@apps/web/package.json`:
- Line 8: The npm script "copy:wasm" in package.json uses the Unix-only `cp`
command which breaks on Windows; replace it with a cross-platform approach by
either (a) changing the script to run a Node one-liner that uses
fs.cp/fs.copyFile (e.g., a small node -e script) to copy
../../packages/wasm/dist/ballerina.wasm to public/ballerina.wasm, or (b) add a
devDependency like "shx" or "cpy-cli" and update "copy:wasm" to use the
cross-platform command provided by that package; update package.json's
"copy:wasm" script accordingly and add the chosen package to devDependencies if
needed.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 82cdc2c5-c559-4984-926f-f623e4563a9d

📥 Commits

Reviewing files that changed from the base of the PR and between 60a8d52 and 56b8ab7.

⛔ Files ignored due to path filters (3)
  • apps/web/public/ballerina.svg is excluded by !**/*.svg
  • bun.lock is excluded by !**/*.lock
  • packages/wasm/go.sum is excluded by !**/*.sum
📒 Files selected for processing (63)
  • .gitignore
  • .gitmodules
  • apps/web/.gitignore
  • apps/web/biome.json
  • apps/web/components.json
  • apps/web/index.html
  • apps/web/package.json
  • apps/web/src/assets/examples.json
  • apps/web/src/components/ansi.tsx
  • apps/web/src/components/app-sidebar.tsx
  • apps/web/src/components/code-editor.tsx
  • apps/web/src/components/editor.tsx
  • apps/web/src/components/file-route-sync.tsx
  • apps/web/src/components/file-tree-dialog.tsx
  • apps/web/src/components/ui/button.tsx
  • apps/web/src/components/ui/collapsible.tsx
  • apps/web/src/components/ui/dialog.tsx
  • apps/web/src/components/ui/dropdown-menu.tsx
  • apps/web/src/components/ui/input.tsx
  • apps/web/src/components/ui/progress.tsx
  • apps/web/src/components/ui/separator.tsx
  • apps/web/src/components/ui/sheet.tsx
  • apps/web/src/components/ui/sidebar.tsx
  • apps/web/src/components/ui/skeleton.tsx
  • apps/web/src/components/ui/tooltip.tsx
  • apps/web/src/hooks/use-ballerina.ts
  • apps/web/src/hooks/use-mobile.ts
  • apps/web/src/lib/fs/core/abstract-fs.ts
  • apps/web/src/lib/fs/core/file-node.types.ts
  • apps/web/src/lib/fs/core/fs.interface.ts
  • apps/web/src/lib/fs/core/path-utils.ts
  • apps/web/src/lib/fs/ephemeral-fs.ts
  • apps/web/src/lib/fs/layered-fs.ts
  • apps/web/src/lib/fs/local-storage-fs.ts
  • apps/web/src/lib/router-utils.ts
  • apps/web/src/lib/utils.ts
  • apps/web/src/main.tsx
  • apps/web/src/providers/fs-provider.tsx
  • apps/web/src/routeTree.gen.ts
  • apps/web/src/routes/$.tsx
  • apps/web/src/routes/__root.tsx
  • apps/web/src/stores/editor-store.ts
  • apps/web/src/stores/file-tree-store.ts
  • apps/web/src/styles.css
  • apps/web/src/types/wasm-types.d.ts
  • apps/web/src/wasm_exec.js
  • apps/web/tsconfig.app.json
  • apps/web/tsconfig.json
  • apps/web/tsconfig.node.json
  • apps/web/turbo.json
  • apps/web/vite.config.ts
  • biome.json
  • package.json
  • packages/wasm/.gitignore
  • packages/wasm/ballerina-lang-go
  • packages/wasm/biome.json
  • packages/wasm/diagnostics_printer.go
  • packages/wasm/go.mod
  • packages/wasm/local_storage_fs.go
  • packages/wasm/main_wasm.go
  • packages/wasm/package.json
  • packages/wasm/turbo.json
  • turbo.json
💤 Files with no reviewable changes (1)
  • biome.json

@snelusha snelusha force-pushed the feat/monorepo branch 3 times, most recently from 01542b2 to d1c650d Compare March 27, 2026 08:18
Copy link
Copy Markdown

@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: 3

🧹 Nitpick comments (11)
apps/web/src/styles.css (1)

154-159: Prefer CSS inheritance over * selector for font-family.

Applying font-sans via the universal selector (*) directly sets the font on every element in the DOM, bypassing CSS inheritance. Since body on line 158 already applies font-sans, child elements will naturally inherit this value.

Using * for inheritable properties like font-family is less efficient and can interfere with elements that intentionally use different fonts (browser defaults for <code>, <textarea>, etc.).

Consider removing font-sans from the * rule and relying on inheritance from body:

♻️ Suggested change
 `@layer` base {
 	* {
-		`@apply` border-border outline-ring/50 font-sans;
+		`@apply` border-border outline-ring/50;
 	}
 	body {
 		`@apply` font-sans bg-background text-foreground;
 	}
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/styles.css` around lines 154 - 159, Remove the font-sans utility
from the universal selector and rely on inheritance from the body rule: edit the
"* { ... }" rule so it no longer applies font-sans (keep non-inheritable
utilities like border-border and outline-ring/50), and ensure the "body { `@apply`
font-sans ... }" rule remains as the single source for the font-family so child
elements inherit it naturally.
apps/web/src/components/ui/sonner.tsx (1)

14-19: Type cast for theme is acceptable but could be more defensive.

The cast theme as ToasterProps["theme"] works when next-themes returns standard values ("light", "dark", "system"), but custom theme names would pass through unchecked. This is low-risk since the app likely uses standard themes, but you could add validation if custom themes are supported.

Optional: Add theme validation
 const Toaster = ({ ...props }: ToasterProps) => {
 	const { theme = "system" } = useTheme();
+	const sonnerTheme: ToasterProps["theme"] =
+		theme === "light" || theme === "dark" ? theme : "system";

 	return (
 		<Sonner
-			theme={theme as ToasterProps["theme"]}
+			theme={sonnerTheme}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/components/ui/sonner.tsx` around lines 14 - 19, The Toaster
component currently force-casts the value from useTheme into
ToasterProps["theme"]; make this defensive by validating the theme returned by
useTheme() before passing it to Sonner: inside Toaster, read const { theme } =
useTheme(); check that theme is one of the allowed values ("light", "dark",
"system") (or your app's supported theme names) and if not, set a safe fallback
(e.g., "system" or the ToasterProps default), then pass that validated value to
Sonner (still respecting ToasterProps["theme"]). This ensures Toaster, useTheme,
Sonner and the ToasterProps["theme"] cast are safe without relying on unchecked
values.
apps/web/src/lib/fs/core/file-node-utils.ts (1)

38-48: Missing blank line before function declaration.

Line 39 starts getRelativePathInTree immediately after the closing brace of toFileNode. Adding a blank line between functions improves readability.

♻️ Add blank line
 	return { kind: "dir", name, children };
 }
+
 export function getRelativePathInTree(
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/lib/fs/core/file-node-utils.ts` around lines 38 - 48, Add a
blank line between the end of the toFileNode function and the start of
getRelativePathInTree to improve readability; specifically, insert one empty
line before the "export function getRelativePathInTree(...)" declaration so it
is separated from the previous closing brace.
apps/web/src/components/share-notice-dialog.tsx (1)

51-56: Dialog key prop forces remount on every open/close cycle.

Using key={String(open)} causes React to unmount and remount the Dialog component whenever open changes. This resets the permanent checkbox state to false each time the dialog reopens, which appears intentional. However, this also destroys any enter/exit animations managed by the Dialog's internal state.

If the checkbox reset is the goal, consider explicitly resetting the permanent state in a useEffect or callback instead of relying on remounting.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/components/share-notice-dialog.tsx` around lines 51 - 56, The
Dialog is being forced to remount via key={String(open)}, which breaks
enter/exit animations; instead remove the key prop from the <Dialog> usage and
explicitly reset the permanent checkbox state (the state variable controlling
the checkbox) when the dialog closes or opens — e.g., in handleDismiss or a
useEffect watching open, setPermanent(false) (or desired default) so the
checkbox is reset without remounting; update references in the component to rely
on Dialog's open prop and onOpenChange for lifecycle rather than the key.
apps/web/src/components/file-route-sync.tsx (1)

46-79: Potential stale activeFilePathRef read in effect.

The effect at lines 53-79 reads activeFilePathRef.current (line 56) but doesn't include activeFilePath in its dependency array. While this is intentional to avoid re-running on every activeFilePath change, it means the effect may compare against a stale value when filePathFromUrl changes.

This should be safe in practice because the second effect (lines 81-92) handles activeFilePath changes separately. However, consider adding a brief comment explaining why activeFilePath is deliberately excluded from dependencies to clarify the intent for future maintainers.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/components/file-route-sync.tsx` around lines 46 - 79, The effect
that reads activeFilePathRef.current inside the React.useEffect (the effect that
checks filePathFromUrl, clearedByDeletionRef, and opens DEFAULT_FILE)
intentionally omits activeFilePath from its dependency array to avoid re-running
on every file switch; add a concise inline comment above the useEffect (and/or
above activeFilePathRef/useLayoutEffect) explaining that activeFilePath is
intentionally excluded because activeFilePathRef is kept up-to-date via
useLayoutEffect and another effect handles activeFilePath changes, so this
effect only reacts to ready, isProcessingShare, filePathFromUrl, existsFile,
openFile, and navigate changes to avoid unnecessary re-renders.
apps/web/src/lib/fs/layered-fs.ts (1)

72-74: Inconsistent use of LOCAL_ROOT constant.

Line 69 uses TEMP_ROOT constant for tempTree(), but line 73 uses hardcoded "/local" for localTree(). For consistency, use the imported LOCAL_ROOT constant.

♻️ Use LOCAL_ROOT constant
 	localTree() {
-		return this.local.transformToTree("/local");
+		return this.local.transformToTree(LOCAL_ROOT);
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/lib/fs/layered-fs.ts` around lines 72 - 74, The localTree method
uses a hardcoded string "/local" instead of the shared constant; update
localTree() to call this.local.transformToTree(LOCAL_ROOT) using the imported
LOCAL_ROOT (matching tempTree() which uses TEMP_ROOT) so both methods
consistently rely on the same root constants (verify LOCAL_ROOT is imported at
top if missing).
apps/web/src/hooks/use-share.ts (1)

72-77: isProcessingShare reads ref during render, which may cause inconsistency.

isProcessingShare is computed by reading processed.current during the render phase (line 72). Since processed.current is mutated asynchronously inside the effect, this can lead to inconsistent values between renders in React's concurrent mode—the ref might update after the component yields but before it commits.

However, based on the context snippets showing how FileRouteSync uses this value, the practical impact is minimal since the guard only delays URL synchronization briefly. The current implementation appears safe for this use case.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/hooks/use-share.ts` around lines 72 - 77, isProcessingShare
currently reads processed.current during render which can be inconsistent;
replace this with a React state updated inside an effect: add a new useState
(e.g. isProcessingShareState), initialize appropriately, then in the existing
effect that mutates processed.current (or a new effect that depends on share)
read processed.current and call setIsProcessingShare(processed.current !==
share); finally return isProcessingShareState instead of the direct computed
isProcessingShare. Reference symbols: isProcessingShare, processed.current,
share, and the use-share hook.
apps/web/src/components/app-sidebar.tsx (1)

433-440: Shared entries rendered under "Localspace" section.

The sharedEntries are rendered within the "Localspace" SidebarGroup (lines 402-457). This may confuse users since shared files are temporary (stored in /tmp/shared) but appear alongside persistent local files.

Consider whether shared entries should have their own labeled section or a visual separator to distinguish them from true local files.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/components/app-sidebar.tsx` around lines 433 - 440, The
sharedEntries are being rendered inside the same SidebarGroup as local files
causing UX confusion; update the sidebar to render sharedEntries in their own
labeled group or visually separated section instead of inside the existing
localspace SidebarGroup. Locate the mapping that renders sharedEntries into
FileTreeNode (symbols: sharedEntries, FileTreeNode, isActiveOrAncestor,
activeFilePath) and move that block out of the current SidebarGroup so it
becomes a separate SidebarGroup (or add a divider and a label like "Shared" /
"Temporary") and ensure the defaultOpen logic still uses
isActiveOrAncestor(activeFilePath) for correct expansion.
apps/web/src/lib/file-tree-utils.ts (1)

18-22: Minor: Redundant kind check after find.

Line 19 finds a node where n.kind === "dir", then line 20 checks dir.kind !== "dir" again. The second check is redundant since find already ensures kind === "dir".

This is harmless but could be simplified:

♻️ Simplified check
 	for (const seg of segments) {
 		const dir = nodes.find((n) => n.kind === "dir" && n.name === seg);
-		if (!dir || dir.kind !== "dir") return { entries: [], basePath: rootPath };
+		if (!dir) return { entries: [], basePath: rootPath };
 		nodes = dir.children ?? [];
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/lib/file-tree-utils.ts` around lines 18 - 22, The loop currently
does a redundant kind check: after finding dir with nodes.find((n) => n.kind ===
"dir" && n.name === seg) you again check dir.kind !== "dir"; remove the
redundant check and simplify to: if (!dir) return { entries: [], basePath:
rootPath }; then continue setting nodes = dir.children ?? []; Update the loop
body referencing variables segments, nodes, dir, and rootPath accordingly.
apps/web/src/lib/share.ts (1)

53-66: Consider adding a depth limit to prevent stack overflow on malicious payloads.

The recursive validation has no depth bound. While typical file trees are shallow, a crafted payload with thousands of nested directories could exhaust the call stack during validation.

♻️ Suggested depth-limited validation
-function isFileNode(value: unknown): value is FileNode {
+function isFileNode(value: unknown, depth = 0): value is FileNode {
+	if (depth > 100) return false;
 	if (!value || typeof value !== "object") return false;
 	const o = value as Record<string, unknown>;
 	if (o.kind === "file")
 		return isSafeNodeName(o.name) && typeof o.content === "string";
 	if (o.kind === "dir") {
 		return (
 			isSafeNodeName(o.name) &&
 			Array.isArray(o.children) &&
-			o.children.every(isFileNode)
+			o.children.every((c) => isFileNode(c, depth + 1))
 		);
 	}
 	return false;
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/lib/share.ts` around lines 53 - 66, The recursive validator is
unbounded and can overflow the stack; update isFileNode to accept an optional
depth/count parameter and enforce a MAX_DEPTH constant (e.g., 1000) so
validation returns false when depth exceeds the limit. Modify all recursive
calls (the children.every(...) invocation) to pass depth+1 and update the
function signature (isFileNode(value: unknown, depth = 0): value is FileNode)
and use isSafeNodeName and typeof checks unchanged; alternatively implement an
iterative stack-based traversal within isFileNode that tracks depth per node and
aborts when depth > MAX_DEPTH to prevent maliciously deep trees from causing
stack overflow.
apps/web/src/lib/fs/core/path-utils.ts (1)

43-47: isSafeRelativePath accepts absolute paths (starting with /), creating semantic mismatch with its name.

Due to pathSegments filtering empty strings, a path like /foo/bar passes validation and becomes ["foo", "bar"]. While the join() function safely handles this (flattening all segments uniformly), allowing absolute paths in a function named "isSafeRelativePath" is misleading and poor API design. Explicitly rejecting leading slashes would clarify intent:

♻️ Suggested fix to reject absolute paths
 export function isSafeRelativePath(path: string): boolean {
 	const trimmed = path.trim();
 	if (!trimmed) return false;
+	if (trimmed.startsWith("/")) return false;
 	return pathSegments(trimmed).every((s) => s !== "." && s !== "..");
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/lib/fs/core/path-utils.ts` around lines 43 - 47,
isSafeRelativePath currently accepts absolute paths because pathSegments strips
empty segments, so update isSafeRelativePath to explicitly reject leading path
separators before calling pathSegments: check the trimmed string for a leading
'/' or '\\' (or any platform-specific root indicator) and return false if
present, then proceed to call pathSegments(trimmed) and validate segments as
before; reference the isSafeRelativePath function and pathSegments helper when
making this change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/web/src/components/app-sidebar.tsx`:
- Around line 149-169: The fork action silently fails when renameFile(path,
dest) returns false (in the file sidebar fork branch using renameFile,
sharedToLocalDestination, exists, expandDir, setFileOperationDialog); update
that onClick handler to detect a false return and display a user-facing error
notification/toast (e.g., "Failed to fork <basename(path)>") including the path
or error context, and apply the same change to the corresponding folder fork
branch around the other block handling folders so both file and folder rename
failures surface clear feedback to the user.

In `@apps/web/src/lib/share.ts`:
- Around line 68-75: encodeSharePayload currently JSON.stringify's the full
FileNode payload (produced by toFileNode) without any size or depth safeguards,
risking large-memory use and oversized share URLs; before calling
compressAndEncode, compute the approximate payload size (e.g., JSON.stringify
length or a byte-size estimate) and enforce a configurable max (or return a
user-friendly error) and/or limit tree depth/entry count inside toFileNode (add
parameters like maxDepth/maxNodes) so the FileNode produced is bounded; if the
payload exceeds the limit, fail early with a clear error from encodeSharePayload
mentioning compressAndEncode and FileNode so callers can handle it.

In `@apps/web/src/routes/__root.tsx`:
- Line 12: The ThemeProvider is rendered without props so dark-mode Tailwind
`.dark` class variant never activates; update the ThemeProvider component (look
for the ThemeProvider instantiation in __root.tsx) to pass attribute="class" and
also include defaultTheme and enableSystem props (e.g., defaultTheme="system"
and enableSystem={true}) so the library uses a .dark class for styling and
provides sensible defaults and system-aware behavior.

---

Nitpick comments:
In `@apps/web/src/components/app-sidebar.tsx`:
- Around line 433-440: The sharedEntries are being rendered inside the same
SidebarGroup as local files causing UX confusion; update the sidebar to render
sharedEntries in their own labeled group or visually separated section instead
of inside the existing localspace SidebarGroup. Locate the mapping that renders
sharedEntries into FileTreeNode (symbols: sharedEntries, FileTreeNode,
isActiveOrAncestor, activeFilePath) and move that block out of the current
SidebarGroup so it becomes a separate SidebarGroup (or add a divider and a label
like "Shared" / "Temporary") and ensure the defaultOpen logic still uses
isActiveOrAncestor(activeFilePath) for correct expansion.

In `@apps/web/src/components/file-route-sync.tsx`:
- Around line 46-79: The effect that reads activeFilePathRef.current inside the
React.useEffect (the effect that checks filePathFromUrl, clearedByDeletionRef,
and opens DEFAULT_FILE) intentionally omits activeFilePath from its dependency
array to avoid re-running on every file switch; add a concise inline comment
above the useEffect (and/or above activeFilePathRef/useLayoutEffect) explaining
that activeFilePath is intentionally excluded because activeFilePathRef is kept
up-to-date via useLayoutEffect and another effect handles activeFilePath
changes, so this effect only reacts to ready, isProcessingShare,
filePathFromUrl, existsFile, openFile, and navigate changes to avoid unnecessary
re-renders.

In `@apps/web/src/components/share-notice-dialog.tsx`:
- Around line 51-56: The Dialog is being forced to remount via
key={String(open)}, which breaks enter/exit animations; instead remove the key
prop from the <Dialog> usage and explicitly reset the permanent checkbox state
(the state variable controlling the checkbox) when the dialog closes or opens —
e.g., in handleDismiss or a useEffect watching open, setPermanent(false) (or
desired default) so the checkbox is reset without remounting; update references
in the component to rely on Dialog's open prop and onOpenChange for lifecycle
rather than the key.

In `@apps/web/src/components/ui/sonner.tsx`:
- Around line 14-19: The Toaster component currently force-casts the value from
useTheme into ToasterProps["theme"]; make this defensive by validating the theme
returned by useTheme() before passing it to Sonner: inside Toaster, read const {
theme } = useTheme(); check that theme is one of the allowed values ("light",
"dark", "system") (or your app's supported theme names) and if not, set a safe
fallback (e.g., "system" or the ToasterProps default), then pass that validated
value to Sonner (still respecting ToasterProps["theme"]). This ensures Toaster,
useTheme, Sonner and the ToasterProps["theme"] cast are safe without relying on
unchecked values.

In `@apps/web/src/hooks/use-share.ts`:
- Around line 72-77: isProcessingShare currently reads processed.current during
render which can be inconsistent; replace this with a React state updated inside
an effect: add a new useState (e.g. isProcessingShareState), initialize
appropriately, then in the existing effect that mutates processed.current (or a
new effect that depends on share) read processed.current and call
setIsProcessingShare(processed.current !== share); finally return
isProcessingShareState instead of the direct computed isProcessingShare.
Reference symbols: isProcessingShare, processed.current, share, and the
use-share hook.

In `@apps/web/src/lib/file-tree-utils.ts`:
- Around line 18-22: The loop currently does a redundant kind check: after
finding dir with nodes.find((n) => n.kind === "dir" && n.name === seg) you again
check dir.kind !== "dir"; remove the redundant check and simplify to: if (!dir)
return { entries: [], basePath: rootPath }; then continue setting nodes =
dir.children ?? []; Update the loop body referencing variables segments, nodes,
dir, and rootPath accordingly.

In `@apps/web/src/lib/fs/core/file-node-utils.ts`:
- Around line 38-48: Add a blank line between the end of the toFileNode function
and the start of getRelativePathInTree to improve readability; specifically,
insert one empty line before the "export function getRelativePathInTree(...)"
declaration so it is separated from the previous closing brace.

In `@apps/web/src/lib/fs/core/path-utils.ts`:
- Around line 43-47: isSafeRelativePath currently accepts absolute paths because
pathSegments strips empty segments, so update isSafeRelativePath to explicitly
reject leading path separators before calling pathSegments: check the trimmed
string for a leading '/' or '\\' (or any platform-specific root indicator) and
return false if present, then proceed to call pathSegments(trimmed) and validate
segments as before; reference the isSafeRelativePath function and pathSegments
helper when making this change.

In `@apps/web/src/lib/fs/layered-fs.ts`:
- Around line 72-74: The localTree method uses a hardcoded string "/local"
instead of the shared constant; update localTree() to call
this.local.transformToTree(LOCAL_ROOT) using the imported LOCAL_ROOT (matching
tempTree() which uses TEMP_ROOT) so both methods consistently rely on the same
root constants (verify LOCAL_ROOT is imported at top if missing).

In `@apps/web/src/lib/share.ts`:
- Around line 53-66: The recursive validator is unbounded and can overflow the
stack; update isFileNode to accept an optional depth/count parameter and enforce
a MAX_DEPTH constant (e.g., 1000) so validation returns false when depth exceeds
the limit. Modify all recursive calls (the children.every(...) invocation) to
pass depth+1 and update the function signature (isFileNode(value: unknown, depth
= 0): value is FileNode) and use isSafeNodeName and typeof checks unchanged;
alternatively implement an iterative stack-based traversal within isFileNode
that tracks depth per node and aborts when depth > MAX_DEPTH to prevent
maliciously deep trees from causing stack overflow.

In `@apps/web/src/styles.css`:
- Around line 154-159: Remove the font-sans utility from the universal selector
and rely on inheritance from the body rule: edit the "* { ... }" rule so it no
longer applies font-sans (keep non-inheritable utilities like border-border and
outline-ring/50), and ensure the "body { `@apply` font-sans ... }" rule remains as
the single source for the font-family so child elements inherit it naturally.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d967fa22-4ecb-47e4-b3df-24ba666b507c

📥 Commits

Reviewing files that changed from the base of the PR and between f48c297 and 01542b2.

⛔ Files ignored due to path filters (3)
  • apps/web/public/ballerina.svg is excluded by !**/*.svg
  • bun.lock is excluded by !**/*.lock
  • packages/wasm/go.sum is excluded by !**/*.sum
📒 Files selected for processing (80)
  • .github/workflows/ci.yml
  • .github/workflows/deploy.yml
  • .gitignore
  • .gitmodules
  • apps/web/.gitignore
  • apps/web/biome.json
  • apps/web/components.json
  • apps/web/index.html
  • apps/web/package.json
  • apps/web/src/assets/examples.json
  • apps/web/src/components/ansi.tsx
  • apps/web/src/components/app-sidebar.tsx
  • apps/web/src/components/code-editor.tsx
  • apps/web/src/components/editor.tsx
  • apps/web/src/components/file-route-sync.tsx
  • apps/web/src/components/file-tree-dialog.tsx
  • apps/web/src/components/share-notice-dialog.tsx
  • apps/web/src/components/ui/button.tsx
  • apps/web/src/components/ui/checkbox.tsx
  • apps/web/src/components/ui/collapsible.tsx
  • apps/web/src/components/ui/dialog.tsx
  • apps/web/src/components/ui/dropdown-menu.tsx
  • apps/web/src/components/ui/input.tsx
  • apps/web/src/components/ui/progress.tsx
  • apps/web/src/components/ui/separator.tsx
  • apps/web/src/components/ui/sheet.tsx
  • apps/web/src/components/ui/sidebar.tsx
  • apps/web/src/components/ui/skeleton.tsx
  • apps/web/src/components/ui/sonner.tsx
  • apps/web/src/components/ui/tooltip.tsx
  • apps/web/src/hooks/use-ballerina.ts
  • apps/web/src/hooks/use-copy-share-link.ts
  • apps/web/src/hooks/use-mobile.ts
  • apps/web/src/hooks/use-share-notice.ts
  • apps/web/src/hooks/use-share.ts
  • apps/web/src/lib/file-tree-utils.ts
  • apps/web/src/lib/fs/core/abstract-fs.ts
  • apps/web/src/lib/fs/core/file-node-utils.ts
  • apps/web/src/lib/fs/core/file-node.types.ts
  • apps/web/src/lib/fs/core/fs.interface.ts
  • apps/web/src/lib/fs/core/path-utils.ts
  • apps/web/src/lib/fs/ephemeral-fs.ts
  • apps/web/src/lib/fs/fs-roots.ts
  • apps/web/src/lib/fs/layered-fs.ts
  • apps/web/src/lib/fs/local-storage-fs.ts
  • apps/web/src/lib/router-utils.ts
  • apps/web/src/lib/share.ts
  • apps/web/src/lib/utils.ts
  • apps/web/src/main.tsx
  • apps/web/src/providers/fs-provider.tsx
  • apps/web/src/routeTree.gen.ts
  • apps/web/src/routes/$.tsx
  • apps/web/src/routes/__root.tsx
  • apps/web/src/stores/editor-store.ts
  • apps/web/src/stores/file-tree-store.ts
  • apps/web/src/styles.css
  • apps/web/src/types/wasm-types.d.ts
  • apps/web/src/wasm_exec.js
  • apps/web/tsconfig.app.json
  • apps/web/tsconfig.json
  • apps/web/tsconfig.node.json
  • apps/web/turbo.json
  • apps/web/vite.config.ts
  • biome.json
  • package.json
  • packages/wasm/.gitignore
  • packages/wasm/ballerina-lang-go
  • packages/wasm/biome.json
  • packages/wasm/diagnostics_printer.go
  • packages/wasm/go.mod
  • packages/wasm/local_storage_fs.go
  • packages/wasm/main_wasm.go
  • packages/wasm/package.json
  • packages/wasm/turbo.json
  • scripts/example_gen/main.go
  • turbo.json
  • web/src/assets/examples.json
  • web/src/components/file-route-sync.tsx
  • web/src/lib/fs/core/path-utils.ts
  • web/src/routes/__root.tsx
💤 Files with no reviewable changes (5)
  • biome.json
  • web/src/routes/__root.tsx
  • web/src/assets/examples.json
  • web/src/components/file-route-sync.tsx
  • web/src/lib/fs/core/path-utils.ts
✅ Files skipped from review due to trivial changes (10)
  • apps/web/src/main.tsx
  • packages/wasm/.gitignore
  • turbo.json
  • packages/wasm/package.json
  • apps/web/turbo.json
  • packages/wasm/turbo.json
  • .gitmodules
  • packages/wasm/biome.json
  • apps/web/biome.json
  • apps/web/src/assets/examples.json
🚧 Files skipped from review as they are similar to previous changes (3)
  • .gitignore
  • package.json
  • apps/web/package.json

Copy link
Copy Markdown

@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: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/web/src/components/file-tree-dialog.tsx (1)

179-181: ⚠️ Potential issue | 🟡 Minor

Disable submit when targetPath is invalid.

For fork flows, getForkTargetPath can return null (e.g., "."/".."), but the button can still be enabled at Line 179–Line 181, then submission silently returns at Line 203. Include !targetPath in the non-delete disable condition.

Suggested fix
 const isActionDisabled = isDelete
 	? false
-	: !name.trim() || hasPathSeparator || alreadyExists || isSamePath;
+	: !name.trim() ||
+	  hasPathSeparator ||
+	  !targetPath ||
+	  alreadyExists ||
+	  isSamePath;

Also applies to: 203-203

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/components/file-tree-dialog.tsx` around lines 179 - 181, The
current isActionDisabled ternary (involving isDelete, name, hasPathSeparator,
alreadyExists, isSamePath) does not account for a null/invalid targetPath from
getForkTargetPath, allowing the submit button to be enabled but submission to
early-return; update the non-delete disable condition to include !targetPath
(i.e., add a check for targetPath falsiness alongside !name.trim(),
hasPathSeparator, alreadyExists, isSamePath) and also ensure the submit handler
(the function that early-returns when targetPath is falsy) stays consistent with
this guard so the button is disabled whenever targetPath is null.
🧹 Nitpick comments (2)
apps/web/src/lib/fs/layered-fs.ts (1)

68-74: Inconsistent use of constants.

tempTree() correctly uses the TEMP_ROOT constant, but localTree() still uses a hardcoded "/local" string. For consistency and maintainability, use LOCAL_ROOT here as well.

♻️ Suggested fix
 	localTree() {
-		return this.local.transformToTree("/local");
+		return this.local.transformToTree(LOCAL_ROOT);
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/lib/fs/layered-fs.ts` around lines 68 - 74, The localTree()
method uses a hardcoded "/local" string instead of the LOCAL_ROOT constant;
change localTree() to call this.local.transformToTree(LOCAL_ROOT) (mirroring
tempTree() which uses TEMP_ROOT) so both methods consistently reference their
respective root constants and avoid magic strings; update any imports/exports if
LOCAL_ROOT is not yet available in layered-fs.ts and run tests.
apps/web/src/hooks/use-share.ts (1)

53-62: Dead code: loaded is always true per current loadSharedFiles implementation.

Based on the store implementation in file-tree-store.ts (lines 299-309), loadSharedFiles always returns { loaded: true, openPath: ... }. The check at line 58 (if (!loaded)) and its toast message will never execute.

Consider either:

  1. Simplifying by removing the loaded check if failure isn't possible, or
  2. Updating loadSharedFiles to actually return loaded: false on failure if that's the intended contract.
Option 1: Simplify by removing dead code
-			const { loaded, openPath } = loadSharedFiles(
+			const { openPath } = loadSharedFiles(
 				payload.root,
 				payload.openRelativePath,
 			);
-			processed.current = loaded ? share : null;
-			if (!loaded) toast.error("Could not load shared files");
-			else {
-				if (openPath !== null) openFile(openPath);
-				showNotice();
-			}
+			processed.current = share;
+			if (openPath !== null) openFile(openPath);
+			showNotice();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/hooks/use-share.ts` around lines 53 - 62, Remove the dead-path
that checks the always-true `loaded` value: set `processed.current = share`
unconditionally, remove the `if (!loaded) toast.error("Could not load shared
files")` branch, and keep the existing handling for `openPath` and `showNotice`
(i.e. call `openFile(openPath)` when `openPath !== null` and then
`showNotice()`). Update the block around the `loadSharedFiles` call so you still
destructure `{ loaded, openPath }` if desired but do not branch on `loaded`;
leave callers/functions `loadSharedFiles`, `openFile`, `showNotice`,
`processed.current`, and `share` unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/web/src/components/file-tree-dialog.tsx`:
- Around line 219-223: The fork handlers ("fork-file" and "fork-folder")
incorrectly call renameFile(path, targetPath) which uses LayeredFS.move()
(copy-then-remove) and thus deletes the shared source; change the handlers to
perform a copy-only operation instead: add or use a copy helper (e.g., implement
copyFile/copyFolder or LayeredFS.copy that duplicates content into the local
layer without removing the original) and call that from the fork handlers in
file-tree-dialog.tsx (and similarly in app-sidebar.tsx where renameFile is used
for forks); after the copy succeeds still call expandDir(dirname(targetPath)) to
update the UI.

---

Outside diff comments:
In `@apps/web/src/components/file-tree-dialog.tsx`:
- Around line 179-181: The current isActionDisabled ternary (involving isDelete,
name, hasPathSeparator, alreadyExists, isSamePath) does not account for a
null/invalid targetPath from getForkTargetPath, allowing the submit button to be
enabled but submission to early-return; update the non-delete disable condition
to include !targetPath (i.e., add a check for targetPath falsiness alongside
!name.trim(), hasPathSeparator, alreadyExists, isSamePath) and also ensure the
submit handler (the function that early-returns when targetPath is falsy) stays
consistent with this guard so the button is disabled whenever targetPath is
null.

---

Nitpick comments:
In `@apps/web/src/hooks/use-share.ts`:
- Around line 53-62: Remove the dead-path that checks the always-true `loaded`
value: set `processed.current = share` unconditionally, remove the `if (!loaded)
toast.error("Could not load shared files")` branch, and keep the existing
handling for `openPath` and `showNotice` (i.e. call `openFile(openPath)` when
`openPath !== null` and then `showNotice()`). Update the block around the
`loadSharedFiles` call so you still destructure `{ loaded, openPath }` if
desired but do not branch on `loaded`; leave callers/functions
`loadSharedFiles`, `openFile`, `showNotice`, `processed.current`, and `share`
unchanged.

In `@apps/web/src/lib/fs/layered-fs.ts`:
- Around line 68-74: The localTree() method uses a hardcoded "/local" string
instead of the LOCAL_ROOT constant; change localTree() to call
this.local.transformToTree(LOCAL_ROOT) (mirroring tempTree() which uses
TEMP_ROOT) so both methods consistently reference their respective root
constants and avoid magic strings; update any imports/exports if LOCAL_ROOT is
not yet available in layered-fs.ts and run tests.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 747fe616-ae1e-4185-8f66-a97db84562b1

📥 Commits

Reviewing files that changed from the base of the PR and between 01542b2 and bfa868f.

⛔ Files ignored due to path filters (3)
  • apps/web/public/ballerina.svg is excluded by !**/*.svg
  • bun.lock is excluded by !**/*.lock
  • packages/wasm/go.sum is excluded by !**/*.sum
📒 Files selected for processing (76)
  • .github/workflows/ci.yml
  • .github/workflows/deploy.yml
  • .gitignore
  • .gitmodules
  • apps/web/.gitignore
  • apps/web/biome.json
  • apps/web/components.json
  • apps/web/index.html
  • apps/web/package.json
  • apps/web/src/assets/examples.json
  • apps/web/src/components/ansi.tsx
  • apps/web/src/components/app-sidebar.tsx
  • apps/web/src/components/code-editor.tsx
  • apps/web/src/components/editor.tsx
  • apps/web/src/components/file-route-sync.tsx
  • apps/web/src/components/file-tree-dialog.tsx
  • apps/web/src/components/share-notice-dialog.tsx
  • apps/web/src/components/ui/button.tsx
  • apps/web/src/components/ui/checkbox.tsx
  • apps/web/src/components/ui/collapsible.tsx
  • apps/web/src/components/ui/dialog.tsx
  • apps/web/src/components/ui/dropdown-menu.tsx
  • apps/web/src/components/ui/input.tsx
  • apps/web/src/components/ui/progress.tsx
  • apps/web/src/components/ui/separator.tsx
  • apps/web/src/components/ui/sheet.tsx
  • apps/web/src/components/ui/sidebar.tsx
  • apps/web/src/components/ui/skeleton.tsx
  • apps/web/src/components/ui/sonner.tsx
  • apps/web/src/components/ui/tooltip.tsx
  • apps/web/src/hooks/use-ballerina.ts
  • apps/web/src/hooks/use-copy-share-link.ts
  • apps/web/src/hooks/use-mobile.ts
  • apps/web/src/hooks/use-share-notice.ts
  • apps/web/src/hooks/use-share.ts
  • apps/web/src/lib/file-tree-utils.ts
  • apps/web/src/lib/fs/core/abstract-fs.ts
  • apps/web/src/lib/fs/core/file-node-utils.ts
  • apps/web/src/lib/fs/core/file-node.types.ts
  • apps/web/src/lib/fs/core/fs.interface.ts
  • apps/web/src/lib/fs/core/path-utils.ts
  • apps/web/src/lib/fs/ephemeral-fs.ts
  • apps/web/src/lib/fs/fs-roots.ts
  • apps/web/src/lib/fs/layered-fs.ts
  • apps/web/src/lib/fs/local-storage-fs.ts
  • apps/web/src/lib/router-utils.ts
  • apps/web/src/lib/share.ts
  • apps/web/src/lib/utils.ts
  • apps/web/src/main.tsx
  • apps/web/src/providers/fs-provider.tsx
  • apps/web/src/routeTree.gen.ts
  • apps/web/src/routes/$.tsx
  • apps/web/src/routes/__root.tsx
  • apps/web/src/stores/editor-store.ts
  • apps/web/src/stores/file-tree-store.ts
  • apps/web/src/styles.css
  • apps/web/src/types/wasm-types.d.ts
  • apps/web/src/wasm_exec.js
  • apps/web/tsconfig.app.json
  • apps/web/tsconfig.json
  • apps/web/tsconfig.node.json
  • apps/web/turbo.json
  • apps/web/vite.config.ts
  • biome.json
  • package.json
  • packages/wasm/.gitignore
  • packages/wasm/ballerina-lang-go
  • packages/wasm/biome.json
  • packages/wasm/diagnostics_printer.go
  • packages/wasm/go.mod
  • packages/wasm/local_storage_fs.go
  • packages/wasm/main_wasm.go
  • packages/wasm/package.json
  • packages/wasm/turbo.json
  • turbo.json
  • web/src/routes/__root.tsx
💤 Files with no reviewable changes (2)
  • biome.json
  • web/src/routes/__root.tsx
✅ Files skipped from review due to trivial changes (14)
  • .gitmodules
  • packages/wasm/.gitignore
  • packages/wasm/turbo.json
  • apps/web/src/main.tsx
  • packages/wasm/biome.json
  • apps/web/src/styles.css
  • .gitignore
  • turbo.json
  • packages/wasm/package.json
  • apps/web/biome.json
  • apps/web/turbo.json
  • apps/web/src/hooks/use-share-notice.ts
  • apps/web/src/assets/examples.json
  • apps/web/src/lib/fs/fs-roots.ts
🚧 Files skipped from review as they are similar to previous changes (15)
  • apps/web/src/routes/__root.tsx
  • apps/web/src/lib/fs/ephemeral-fs.ts
  • apps/web/src/routes/$.tsx
  • apps/web/src/components/ui/checkbox.tsx
  • apps/web/src/hooks/use-copy-share-link.ts
  • apps/web/src/components/ui/sonner.tsx
  • apps/web/src/components/share-notice-dialog.tsx
  • apps/web/src/lib/file-tree-utils.ts
  • apps/web/package.json
  • apps/web/src/lib/fs/core/file-node-utils.ts
  • apps/web/src/stores/file-tree-store.ts
  • package.json
  • apps/web/src/lib/fs/core/path-utils.ts
  • apps/web/src/lib/share.ts
  • apps/web/src/components/app-sidebar.tsx

@warunalakshitha
Copy link
Copy Markdown
Contributor

Shall we sync the pr with main branch.

Copy link
Copy Markdown

@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.

🧹 Nitpick comments (1)
apps/web/package.json (1)

8-9: Consider using a cross-platform copy utility for copy:wasm.

The cp command is Unix-specific and will fail on Windows. If Windows development is supported, consider using a cross-platform alternative.

♻️ Proposed fix using cpx or cpy-cli
-		"copy:wasm": "cp ../../packages/wasm/dist/ballerina.wasm public/ballerina.wasm"
+		"copy:wasm": "cpy '../../packages/wasm/dist/ballerina.wasm' public --flat"

This requires adding cpy-cli as a devDependency:

 	"devDependencies": {
+		"cpy-cli": "^5.0.0",
 		"@tanstack/router-plugin": "^1.167.1",

Alternatively, if Windows support is not a priority, you can ignore this suggestion.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/package.json` around lines 8 - 9, The "copy:wasm" npm script uses
the Unix-only "cp" command and will fail on Windows; replace it with a
cross-platform copier (e.g., install a devDependency like "cpy-cli" or "cpx")
and update the "copy:wasm" script to use that tool (for example use "cpy" or
"cpx" to copy "../../packages/wasm/dist/ballerina.wasm" to
"public/ballerina.wasm"); ensure you add the chosen package to devDependencies
and update any related build scripts that call "copy:wasm".
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@apps/web/package.json`:
- Around line 8-9: The "copy:wasm" npm script uses the Unix-only "cp" command
and will fail on Windows; replace it with a cross-platform copier (e.g., install
a devDependency like "cpy-cli" or "cpx") and update the "copy:wasm" script to
use that tool (for example use "cpy" or "cpx" to copy
"../../packages/wasm/dist/ballerina.wasm" to "public/ballerina.wasm"); ensure
you add the chosen package to devDependencies and update any related build
scripts that call "copy:wasm".

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: bb2ef1f9-4ee0-40b6-8ea9-2c357e7fb56a

📥 Commits

Reviewing files that changed from the base of the PR and between bfa868f and 1811412.

⛔ Files ignored due to path filters (3)
  • apps/web/public/ballerina.svg is excluded by !**/*.svg
  • bun.lock is excluded by !**/*.lock
  • packages/wasm/go.sum is excluded by !**/*.sum
📒 Files selected for processing (75)
  • .github/workflows/ci.yml
  • .github/workflows/deploy.yml
  • .gitignore
  • .gitmodules
  • apps/web/.gitignore
  • apps/web/biome.json
  • apps/web/components.json
  • apps/web/index.html
  • apps/web/package.json
  • apps/web/src/assets/examples.json
  • apps/web/src/components/ansi.tsx
  • apps/web/src/components/app-sidebar.tsx
  • apps/web/src/components/code-editor.tsx
  • apps/web/src/components/editor.tsx
  • apps/web/src/components/file-route-sync.tsx
  • apps/web/src/components/file-tree-dialog.tsx
  • apps/web/src/components/share-notice-dialog.tsx
  • apps/web/src/components/ui/button.tsx
  • apps/web/src/components/ui/checkbox.tsx
  • apps/web/src/components/ui/collapsible.tsx
  • apps/web/src/components/ui/dialog.tsx
  • apps/web/src/components/ui/dropdown-menu.tsx
  • apps/web/src/components/ui/input.tsx
  • apps/web/src/components/ui/progress.tsx
  • apps/web/src/components/ui/separator.tsx
  • apps/web/src/components/ui/sheet.tsx
  • apps/web/src/components/ui/sidebar.tsx
  • apps/web/src/components/ui/skeleton.tsx
  • apps/web/src/components/ui/sonner.tsx
  • apps/web/src/components/ui/tooltip.tsx
  • apps/web/src/hooks/use-ballerina.ts
  • apps/web/src/hooks/use-copy-share-link.ts
  • apps/web/src/hooks/use-mobile.ts
  • apps/web/src/hooks/use-share-notice.ts
  • apps/web/src/hooks/use-share.ts
  • apps/web/src/lib/file-tree-utils.ts
  • apps/web/src/lib/fs/core/abstract-fs.ts
  • apps/web/src/lib/fs/core/file-node-utils.ts
  • apps/web/src/lib/fs/core/file-node.types.ts
  • apps/web/src/lib/fs/core/fs.interface.ts
  • apps/web/src/lib/fs/core/path-utils.ts
  • apps/web/src/lib/fs/ephemeral-fs.ts
  • apps/web/src/lib/fs/fs-roots.ts
  • apps/web/src/lib/fs/layered-fs.ts
  • apps/web/src/lib/fs/local-storage-fs.ts
  • apps/web/src/lib/router-utils.ts
  • apps/web/src/lib/share.ts
  • apps/web/src/lib/utils.ts
  • apps/web/src/main.tsx
  • apps/web/src/providers/fs-provider.tsx
  • apps/web/src/routeTree.gen.ts
  • apps/web/src/routes/$.tsx
  • apps/web/src/routes/__root.tsx
  • apps/web/src/stores/editor-store.ts
  • apps/web/src/stores/file-tree-store.ts
  • apps/web/src/styles.css
  • apps/web/src/types/wasm-types.d.ts
  • apps/web/src/wasm_exec.js
  • apps/web/tsconfig.app.json
  • apps/web/tsconfig.json
  • apps/web/tsconfig.node.json
  • apps/web/turbo.json
  • apps/web/vite.config.ts
  • biome.json
  • package.json
  • packages/wasm/.gitignore
  • packages/wasm/ballerina-lang-go
  • packages/wasm/biome.json
  • packages/wasm/diagnostics_printer.go
  • packages/wasm/go.mod
  • packages/wasm/local_storage_fs.go
  • packages/wasm/main_wasm.go
  • packages/wasm/package.json
  • packages/wasm/turbo.json
  • turbo.json
💤 Files with no reviewable changes (1)
  • biome.json
✅ Files skipped from review due to trivial changes (10)
  • packages/wasm/.gitignore
  • packages/wasm/biome.json
  • .gitmodules
  • turbo.json
  • packages/wasm/turbo.json
  • packages/wasm/package.json
  • .gitignore
  • apps/web/turbo.json
  • apps/web/biome.json
  • package.json
🚧 Files skipped from review as they are similar to previous changes (2)
  • .github/workflows/deploy.yml
  • .github/workflows/ci.yml

@snelusha snelusha force-pushed the feat/monorepo branch 4 times, most recently from 83ab388 to f0157ef Compare March 30, 2026 21:02
@keizer619 keizer619 merged commit b44a993 into ballerina-platform:main Mar 31, 2026
3 checks passed
@snelusha snelusha deleted the feat/monorepo branch April 1, 2026 14:34
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.

Migrate to monorepo using Turborepo

3 participants