Skip to content

feat(TEA-84): add PR gate for lint, type-check, build, and Docker health#42

Open
Mohamed-Elshesheny wants to merge 3 commits intomainfrom
chore/ci-gate-pipeline
Open

feat(TEA-84): add PR gate for lint, type-check, build, and Docker health#42
Mohamed-Elshesheny wants to merge 3 commits intomainfrom
chore/ci-gate-pipeline

Conversation

@Mohamed-Elshesheny
Copy link
Copy Markdown
Contributor

@Mohamed-Elshesheny Mohamed-Elshesheny commented Apr 22, 2026

Summary

Adds a GitHub Actions PR gate that blocks merges unless lint, check-types, build, and a full Docker stack healthcheck all pass. Also gives the web container a self-describing HEALTHCHECK so docker compose up --wait can actually verify it, and makes release.yml refuse to cut a tag on a broken tree.

Before this PR, the only workflows in .github/workflows/ were release.yml (manual dispatch) and license-header.yml (SPDX check). Nothing verified that code landing on main actually lints, type-checks, builds, or produces a working container image.

Related Issues

Linear: TEA-84 — Set up GitHub Actions CI for lint, types, build, and Docker.

Type of Change

Chore / infrastructure (no runtime behavior change).

Changes

  • New workflow .github/workflows/ci.yml — runs on pull_request and push: main, with a concurrency group that auto-cancels superseded runs. Two parallel jobs on ubuntu-latest:
    • buildactions/checkout@v4pnpm/action-setup@v4 (reads packageManager from root package.json) → actions/setup-node@v4 with Node 20 + cache: 'pnpm' → turbo filesystem cache via actions/cache@v4pnpm install --frozen-lockfilepnpm lintpnpm check-typespnpm build. Each command is a separate step so the PR checks UI points directly at the failing gate.
    • dockerdocker/setup-buildx-action@v3docker compose builddocker compose up -d --wait --wait-timeout 180if: failure() diagnostics dumping docker compose ps and full logs → if: always() docker compose down -v teardown.
  • Dockerfile — added a HEALTHCHECK to the web stage (Nginx) using BusyBox wget against http://localhost/. Without it, docker compose up --wait would treat the web container as healthy the moment it starts, and a broken Nginx config (or missing dist/) would pass CI. The check lives in the Dockerfile (not docker-compose.yml) so every runtime — docker run, compose, Kubernetes readiness probes — inherits it.
  • .github/workflows/release.yml — inserted a Verify workspace (lint, types, build) step between Install dependencies and Configure Git user. A manual release against a broken tree now fails before any git tag / git push.

How to Test

Local smoke test (mirrors CI exactly):

  1. pnpm install --frozen-lockfile
  2. pnpm lint && pnpm check-types && pnpm build
  3. docker compose build
  4. docker compose up -d --wait --wait-timeout 180
  5. docker compose ps — both wordyme-backend and wordyme-web report healthy.
  6. docker compose down -v

Prove the healthcheck actually blocks a broken web image:

  1. Temporarily break the inline Nginx config in Dockerfile (drop a semicolon in a location block).
  2. docker compose build web && docker compose up -d --wait --wait-timeout 30
  3. Expected: wordyme-web goes startingunhealthy, docker compose up --wait exits non-zero within 30s, docker compose ps shows the failed state. Revert after confirming.

Prove the CI gates block bad PRs (optional, on this or a follow-up branch):

  • Push a commit adding const x: string = 1; to any workspace — Type-check step fails.
  • Push a commit deleting a COPY --from=builder line in Dockerfiledocker job fails with a readable log excerpt from the Dump compose state on failure step.
  • Push a docs-only commit — both jobs still run and pass.

Expected result:

Opening this PR should trigger the new Lint, type-check, build and Docker stack health jobs side-by-side in the PR checks UI; both pass. Re-running CI on the same branch shows turbo cache hits (FULL TURBO / cached) in the build step logs.

Summary by CodeRabbit

  • Chores
    • Added CI workflow to run linting, type checks, builds, and container checks on PRs and main
    • Enhanced release workflow with verification steps before publishing
  • Reliability
    • Improved container health checks and nginx startup/teardown for more robust deployments
    • Safer event/effect handling and deferred state updates to reduce timing issues
  • Refactor
    • Tightened internal types and small code cleanups to improve type safety and consistency

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 22, 2026

📝 Walkthrough

Walkthrough

Adds a CI workflow that installs deps, runs lint/type-check/build, and exercises Docker Compose stacks with health waits and teardown; augments release workflow to run lint/check/build before release; Dockerfile nginx stage now created via heredoc and includes an HTTP HEALTHCHECK; several TypeScript typings and microtask scheduling adjustments across UI/shared packages.

Changes

Cohort / File(s) Summary
CI / Release workflows
.github/workflows/ci.yml, .github/workflows/release.yml
Adds new CI job (pnpm install with Turbo cache, lint, type-check, build) and Docker Compose build/start with health-wait, logs/status capture on failure, and teardown; release workflow now runs lint/check-types/build before release steps.
Docker image / nginx
Dockerfile
Rewrites nginx config creation to use a heredoc and adds an HTTP HEALTHCHECK (interval/timeout/start-period/retries); runtime CMD/ports unchanged.
Lib: tree typing
packages/lib/src/data/tree.ts
Tightens node props from any to unknown, introduces TreeNodeJson<T> and changes jsonToTree signature to accept the typed JSON shape.
Shared: debounce typing
packages/shared/src/debounce.ts
Narrows generic constraint from (...args: any[]) => any to (...args: never[]) => unknown, changing inferred return typing only.
UI: components & hooks
packages/ui/src/components/color-picker.tsx, .../dynamic-icon.tsx, .../icon-picker.tsx, .../image-file-input.tsx, .../tree.tsx, packages/ui/src/hooks/*, packages/ui/src/theme/theme-provider.tsx
Various small changes: deferred state updates via queueMicrotask, refactor to named function and moved default export, ESLint suppression added, ref wiring via getInputProps, tightened generics from anyunknown, added/made effect dependencies explicit, and replaced some synchronous state updates with microtask-scheduled updates.
ESLint config
packages/eslint-config/react-internal.js
Stylistic/consistency changes: single quotes for specifiers, explicit React version '19', comments regarding ESLint 10 compatibility; no structural export changes.

Sequence Diagram(s)

sequenceDiagram
    participant GH as GitHub Actions Runner
    participant Cache as Turbo/NPM Cache
    participant PNPM as pnpm
    participant Docker as Docker Engine
    participant Compose as docker-compose
    participant Service as App Containers
    GH->>Cache: restore cache
    GH->>PNPM: pnpm install, lint, check-types, build
    PNPM-->>GH: results/artifacts
    GH->>Docker: build images
    Docker-->>GH: images
    GH->>Compose: start stack (with health-wait)
    Compose->>Service: container start
    Service-->>Compose: health checks
    Compose-->>GH: healthy / timeout
    alt failure
        Compose->>GH: capture status & logs
    end
    GH->>Compose: teardown (including volumes)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • HunterXNB

Poem

🐰 I nudged the pipelines, gave configs a tune,

nginx now listens, healthchecks hum like a rune.
Types tightened gently, microtasks skip the line,
Builds and containers waltz — CI hops in time. 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the primary changes: adding CI gates (lint, type-check, build) and Docker health checks before merges, which aligns with the PR's main objectives of enforcing quality checks.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/ci-gate-pipeline

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

🧹 Nitpick comments (1)
.github/workflows/release.yml (1)

55-57: Split pre-release verification into separate steps for clearer failures.

Line 56 is correct, but splitting lint/types/build into individual steps gives better failure visibility and rerun ergonomics.

Proposed refactor
-      - name: Verify workspace (lint, types, build)
-        run: pnpm lint && pnpm check-types && pnpm build
+      - name: Verify workspace (lint)
+        run: pnpm lint
+
+      - name: Verify workspace (types)
+        run: pnpm check-types
+
+      - name: Verify workspace (build)
+        run: pnpm build
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/release.yml around lines 55 - 57, The workflow currently
runs a combined step named "Verify workspace (lint, types, build)" that chains
pnpm lint && pnpm check-types && pnpm build; change this to three separate steps
named e.g. "Lint", "Type check", and "Build" that run pnpm lint, pnpm
check-types, and pnpm build respectively (replacing the combined step) so
failures are isolated and can be re-run individually—update the step names and
run commands in the release.yml job accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/ci.yml:
- Line 13: Add an explicit least-privilege GITHUB_TOKEN permissions block to the
workflow (instead of relying on defaults) to restrict token scope for the
read/build/test job(s); update the top-level or job-level definition referenced
by jobs to include a permissions mapping (e.g., set contents: read and packages:
read and any other minimal read permissions your CI needs) so the GITHUB_TOKEN
is not granted broader rights.

---

Nitpick comments:
In @.github/workflows/release.yml:
- Around line 55-57: The workflow currently runs a combined step named "Verify
workspace (lint, types, build)" that chains pnpm lint && pnpm check-types &&
pnpm build; change this to three separate steps named e.g. "Lint", "Type check",
and "Build" that run pnpm lint, pnpm check-types, and pnpm build respectively
(replacing the combined step) so failures are isolated and can be re-run
individually—update the step names and run commands in the release.yml job
accordingly.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: b5f77c39-ab0d-4ae9-bb91-2d296d6aa52a

📥 Commits

Reviewing files that changed from the base of the PR and between 2ff66a3 and 1035a69.

📒 Files selected for processing (3)
  • .github/workflows/ci.yml
  • .github/workflows/release.yml
  • Dockerfile

Comment thread .github/workflows/ci.yml
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Set explicit least-privilege GITHUB_TOKEN permissions.

This workflow performs read/build/test actions only. Add explicit read-only permissions to avoid inheriting broader defaults.

Proposed hardening
 name: CI
@@
 concurrency:
   group: ci-${{ github.workflow }}-${{ github.ref }}
   cancel-in-progress: true
+
+permissions:
+  contents: read
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/ci.yml at line 13, Add an explicit least-privilege
GITHUB_TOKEN permissions block to the workflow (instead of relying on defaults)
to restrict token scope for the read/build/test job(s); update the top-level or
job-level definition referenced by jobs to include a permissions mapping (e.g.,
set contents: read and packages: read and any other minimal read permissions
your CI needs) so the GITHUB_TOKEN is not granted broader rights.

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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/shared/src/debounce.ts`:
- Line 7: The debounce generic currently constrains F with (...args: never[])
which forbids any non-zero-arg functions; update the generic to allow arbitrary
argument tuples by introducing a Params tuple and constraining F with (...args:
Params) => unknown (e.g., add a generic Params extends unknown[] and use F
extends (...args: Params) => unknown) and use that Params for any internal
argument typings so debounce(func, waitFor) accepts callbacks with parameters
(such as the 3-arg handler used in SketchDialog.tsx) while preserving type
safety for func and returned wrapper.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 32cfb867-4c9d-48d2-9a6e-cffd5ebcb8a5

📥 Commits

Reviewing files that changed from the base of the PR and between 1035a69 and 6cd48c3.

📒 Files selected for processing (3)
  • Dockerfile
  • packages/lib/src/data/tree.ts
  • packages/shared/src/debounce.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • Dockerfile


'use client';
export const debounce = <F extends (...args: any[]) => any>(func: F, waitFor: number) => {
export const debounce = <F extends (...args: never[]) => unknown>(func: F, waitFor: number) => {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Current debounce signature:"
rg -n -C2 'export const debounce = <F extends \(\.\.\.args: never\[\]\) => unknown>' packages/shared/src/debounce.ts

echo
echo "Potentially broken debounce callsites (callbacks with at least one declared param):"
rg -nP --type=ts --type=tsx -C2 '\bdebounce\s*\(\s*(?:async\s*)?\((?!\s*\))[^)]*\)\s*=>'

Repository: TeamCoderz/WordyMe

Length of output: 402


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Verifying debounce signature ==="
cat -n packages/shared/src/debounce.ts | head -20

echo ""
echo "=== Searching for debounce callsites ==="
rg -n 'debounce' --type ts -A 3 packages/editor/src/components/Dialogs/SketchDialog.tsx | head -50

echo ""
echo "=== Finding all debounce usages ==="
rg -n '\bdebounce\s*\(' --type ts --type rust -C 2 | head -100

Repository: TeamCoderz/WordyMe

Length of output: 1738


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== All imports of debounce in repo ==="
rg -n "import.*debounce" --type ts

echo ""
echo "=== All debounce function calls ==="
rg -n "debounce\(" --type ts -B 1 -A 5

echo ""
echo "=== Check if there are TypeScript build errors ==="
if [ -f "tsconfig.json" ]; then
  echo "Found tsconfig.json"
  head -20 tsconfig.json
fi

Repository: TeamCoderz/WordyMe

Length of output: 1984


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Checking SketchDialog full debounce call context ==="
sed -n '467,480p' packages/editor/src/components/Dialogs/SketchDialog.tsx

echo ""
echo "=== Check package.json for build command ==="
if [ -f "package.json" ]; then
  jq '.scripts | select(.build != null)' package.json 2>/dev/null || echo "No build script found"
fi

echo ""
echo "=== Checking the return type from debounce ==="
sed -n '7,15p' packages/shared/src/debounce.ts

echo ""
echo "=== Check how saveToLocalStorage is used after debounce ==="
sed -n '467,485p' packages/editor/src/components/Dialogs/SketchDialog.tsx

Repository: TeamCoderz/WordyMe

Length of output: 2190


never[] constraint rejects all non-zero-argument callbacks.

The signature <F extends (...args: never[]) => unknown> only accepts functions with zero parameters (or effectively uncallable functions). The callsite in packages/editor/src/components/Dialogs/SketchDialog.tsx (line 468) passes a callback with 3 parameters:

async (elements: readonly ExcalidrawElement[], __appState: AppState, files: BinaryFiles) => { ... }

This will fail TypeScript type-checking because the parameter types cannot satisfy the never constraint.

Fix: Use a generic tuple type instead:

Suggested fix
-export const debounce = <F extends (...args: never[]) => unknown>(func: F, waitFor: number) => {
+export const debounce = <Args extends unknown[], R>(
+  func: (...args: Args) => R,
+  waitFor: number,
+) => {
   let timeout: ReturnType<typeof setTimeout> | null = null;

-  return (...args: Parameters<F>): void => {
+  return (...args: Args): void => {
     if (timeout !== null) {
       clearTimeout(timeout);
     }
     timeout = setTimeout(() => func(...args), waitFor);
   };
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/shared/src/debounce.ts` at line 7, The debounce generic currently
constrains F with (...args: never[]) which forbids any non-zero-arg functions;
update the generic to allow arbitrary argument tuples by introducing a Params
tuple and constraining F with (...args: Params) => unknown (e.g., add a generic
Params extends unknown[] and use F extends (...args: Params) => unknown) and use
that Params for any internal argument typings so debounce(func, waitFor) accepts
callbacks with parameters (such as the 3-arg handler used in SketchDialog.tsx)
while preserving type safety for func and returned wrapper.

@Mohamed-Elshesheny
Copy link
Copy Markdown
Contributor Author

@ibastawisi follow-up needed (@repo/ui lint)

pnpm lint now passes the previous any warnings, but it still fails in UI with:
TypeError: Error while loading rule 'react/display-name': contextOrFilename.getFilename is not a function
Could you please check:

  • ESLint compatibility in @repo/ui (eslint version vs eslint-plugin-react version).
  • Whether packages/ui/eslint.config.mjs (flat config) is causing react/display-name to run against the config file itself.
  • If we should pin/downgrade or upgrade plugin versions so they match ESLint.
  • Local repro with pnpm --filter @repo/ui lint and push a fix so monorepo pnpm lint passes.

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: 2

🧹 Nitpick comments (2)
packages/ui/src/hooks/use-throttle.ts (1)

17-20: Add a cleanup guard for the queued microtask update

The queued callback can still run after cleanup. Add a cancellation flag so Line 18 doesn’t write state after unmount/teardown.

♻️ Suggested adjustment
   useEffect(() => {
+    let cancelled = false;
     const now = Date.now();

     if (lastUpdated.current && now >= lastUpdated.current + interval) {
       lastUpdated.current = now;
       queueMicrotask(() => {
-        setThrottledValue(value);
+        if (!cancelled) setThrottledValue(value);
       });
-      return;
+      return () => {
+        cancelled = true;
+      };
     }

     const id = window.setTimeout(() => {
       lastUpdated.current = now;
       setThrottledValue(value);
     }, interval);

-    return () => window.clearTimeout(id);
+    return () => {
+      cancelled = true;
+      window.clearTimeout(id);
+    };
   }, [value, interval]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/ui/src/hooks/use-throttle.ts` around lines 17 - 20, The queued
microtask in useThrottle that calls setThrottledValue via queueMicrotask can run
after unmount; add a local cancelled flag (e.g., let cancelled = false) inside
the effect, wrap the queueMicrotask callback to check !cancelled before calling
setThrottledValue, and set cancelled = true in the effect cleanup to prevent
state updates after teardown; reference the useThrottle effect, the
queueMicrotask call and setThrottledValue when making the change.
packages/ui/src/components/color-picker.tsx (1)

188-195: Consider guarding queued state sync with effect cleanup

Because updates are deferred (Lines 191-195), add a cancellation guard so stale queued callbacks don’t write after teardown.

♻️ Suggested adjustment
   useEffect(() => {
     const text = getColorAsHsva(textColor || defaultTextColor);
     const bg = getColorAsHsva(backgroundColor || defaultBackgroundColor);
     const border = getColorAsHsva(borderColor || defaultBorderColor);
+    let cancelled = false;
     queueMicrotask(() => {
-      setTextColorHsv(text);
-      setBgColorHsv(bg);
-      setBorderColorHsv(border);
+      if (cancelled) return;
+      setTextColorHsv(text);
+      setBgColorHsv(bg);
+      setBorderColorHsv(border);
     });
+    return () => {
+      cancelled = true;
+    };
   }, [
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/ui/src/components/color-picker.tsx` around lines 188 - 195, The
queued state updates scheduled with queueMicrotask can run after the component
unmounts causing stale writes; inside the effect that computes text/bg/border
hsv (where getColorAsHsva and queueMicrotask are used) introduce a cancellation
guard (e.g., a local mounted/isActive flag) that is set true at start and
flipped false in the effect cleanup, and only call setTextColorHsv,
setBgColorHsv, and setBorderColorHsv inside the queued callback if the flag is
still true; this prevents the microtask from writing state after teardown.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/ui/src/components/tree.tsx`:
- Line 92: The nested TreeContext.Provider in TreeItem currently sets value to
only { indent, currentItem } which drops the existing tree property; update the
provider to merge the previous context with the overrides (preserve the existing
tree) so descendants like TreeDragLine can still access tree. Locate the
TreeItem usage of TreeContext.Provider and change the value to spread the
previous context (or use the TreeContextValue type) and then set/override indent
and currentItem while keeping tree intact.

In `@packages/ui/src/theme/theme-provider.tsx`:
- Around line 119-122: The fallback currently hardcodes 'new-york' which ignores
the provider's defaultTheme and doesn't realign color; update the recovery path
so that when THEME_BY_VALUE[theme] is falsy you queueMicrotask to setThemeState
to the configured defaultTheme (not the hardcoded 'new-york') and also update
the corresponding color state (e.g., call setColorState or the function that
syncs color) based on THEME_BY_VALUE[defaultTheme]. Use the provider's
defaultTheme variable/name from this component and THEME_BY_VALUE to derive the
correct color mapping so both theme and color are restored consistently.

---

Nitpick comments:
In `@packages/ui/src/components/color-picker.tsx`:
- Around line 188-195: The queued state updates scheduled with queueMicrotask
can run after the component unmounts causing stale writes; inside the effect
that computes text/bg/border hsv (where getColorAsHsva and queueMicrotask are
used) introduce a cancellation guard (e.g., a local mounted/isActive flag) that
is set true at start and flipped false in the effect cleanup, and only call
setTextColorHsv, setBgColorHsv, and setBorderColorHsv inside the queued callback
if the flag is still true; this prevents the microtask from writing state after
teardown.

In `@packages/ui/src/hooks/use-throttle.ts`:
- Around line 17-20: The queued microtask in useThrottle that calls
setThrottledValue via queueMicrotask can run after unmount; add a local
cancelled flag (e.g., let cancelled = false) inside the effect, wrap the
queueMicrotask callback to check !cancelled before calling setThrottledValue,
and set cancelled = true in the effect cleanup to prevent state updates after
teardown; reference the useThrottle effect, the queueMicrotask call and
setThrottledValue when making the change.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: e37ce97f-6dbd-4223-ae74-71f6545604db

📥 Commits

Reviewing files that changed from the base of the PR and between 6cd48c3 and 6615f9b.

📒 Files selected for processing (12)
  • packages/eslint-config/react-internal.js
  • packages/ui/src/components/color-picker.tsx
  • packages/ui/src/components/dynamic-icon.tsx
  • packages/ui/src/components/icon-picker.tsx
  • packages/ui/src/components/image-file-input.tsx
  • packages/ui/src/components/tree.tsx
  • packages/ui/src/hooks/use-container-query.ts
  • packages/ui/src/hooks/use-hash.ts
  • packages/ui/src/hooks/use-local-storage.ts
  • packages/ui/src/hooks/use-optimistic.ts
  • packages/ui/src/hooks/use-throttle.ts
  • packages/ui/src/theme/theme-provider.tsx
✅ Files skipped from review due to trivial changes (2)
  • packages/ui/src/components/icon-picker.tsx
  • packages/ui/src/components/dynamic-icon.tsx


return (
<TreeContext.Provider value={{ indent, currentItem: item }}>
<TreeContext.Provider value={{ indent, currentItem: item } as TreeContextValue<unknown>}>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Preserve tree when overriding item context in TreeItem.

On Line 92, the nested provider replaces the context value with only indent + currentItem. Descendants inside TreeItem lose tree, so components like TreeDragLine can unexpectedly resolve tree as undefined and render null.

Proposed fix
 function TreeItem<T = unknown>({
   item,
   className,
   asChild,
   children,
   ...props
 }: Omit<TreeItemProps<T>, 'indent'>) {
-  const { indent } = useTreeContext<T>();
+  const { indent, tree } = useTreeContext();

   const itemProps = typeof item.getProps === 'function' ? item.getProps() : {};
   const mergedProps = { ...props, ...itemProps };
@@
-    <TreeContext.Provider value={{ indent, currentItem: item } as TreeContextValue<unknown>}>
+    <TreeContext.Provider
+      value={{ indent, tree, currentItem: item as ItemInstance<unknown> }}
+    >
       <Comp
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/ui/src/components/tree.tsx` at line 92, The nested
TreeContext.Provider in TreeItem currently sets value to only { indent,
currentItem } which drops the existing tree property; update the provider to
merge the previous context with the overrides (preserve the existing tree) so
descendants like TreeDragLine can still access tree. Locate the TreeItem usage
of TreeContext.Provider and change the value to spread the previous context (or
use the TreeContextValue type) and then set/override indent and currentItem
while keeping tree intact.

Comment on lines +119 to +122
if (THEME_BY_VALUE[theme]) return;
queueMicrotask(() => {
setThemeState('new-york');
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Fallback currently ignores provider configuration and can propagate invalid theme state

Line 121 hardcodes 'new-york', so a custom defaultTheme is ignored during recovery. Also, recovery should realign color with the recovered theme.

💡 Proposed fix
   useEffect(() => {
     if (THEME_BY_VALUE[theme]) return;
-    queueMicrotask(() => {
-      setThemeState('new-york');
-    });
-  }, [theme]);
+    setThemeState(defaultTheme);
+    setColorState(THEME_BY_VALUE[defaultTheme]['color-variants'][0].value);
+  }, [theme, defaultTheme]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/ui/src/theme/theme-provider.tsx` around lines 119 - 122, The
fallback currently hardcodes 'new-york' which ignores the provider's
defaultTheme and doesn't realign color; update the recovery path so that when
THEME_BY_VALUE[theme] is falsy you queueMicrotask to setThemeState to the
configured defaultTheme (not the hardcoded 'new-york') and also update the
corresponding color state (e.g., call setColorState or the function that syncs
color) based on THEME_BY_VALUE[defaultTheme]. Use the provider's defaultTheme
variable/name from this component and THEME_BY_VALUE to derive the correct color
mapping so both theme and color are restored consistently.

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.

2 participants