Skip to content

Shared Files via Config (instead of symlinks) + Navigate To Element from Search (#1711 )#2425

Merged
davydkov merged 19 commits intomainfrom
kieron/shared-files
Dec 8, 2025
Merged

Shared Files via Config (instead of symlinks) + Navigate To Element from Search (#1711 )#2425
davydkov merged 19 commits intomainfrom
kieron/shared-files

Conversation

@kieronlanning
Copy link
Copy Markdown
Collaborator

@kieronlanning kieronlanning commented Dec 5, 2025

Introduced a new include configuration option to LikeC4 project configs, allowing users to specify additional directories to include source files from outside the main project folder.

Supporting documentation, validation logic, and tests have been added and updated. Additionally, there are minor dependency updates and a small fix in the icon update script due to a (remote) file naming change.

Checklist

  • I've thoroughly read the latest contribution guidelines.
  • I've rebased my branch onto main before creating this PR.
  • My commit messages follow conventional spec
  • I've added tests to cover my changes (if applicable).
  • I've verified that all new and existing tests have passed locally for mobile, tablet, and desktop screen sizes.
  • My change requires documentation updates.
  • I've updated the documentation accordingly.

Summary by CodeRabbit

  • New Features

    • Add config "include" to include additional relative directories of LikeC4 sources.
    • Diagram: focus-on-element API with automatic unfocus after a short delay.
  • UI Enhancements

    • View buttons and search now propagate element-focused navigation so selecting a view can focus an element.
  • Documentation

    • Docs updated with include usage, examples, path rules, and multi-project guidance.
  • Tests

    • Added tests covering include paths, document attribution, and diagram node lookup.

✏️ Tip: You can customize this high-level summary in your review settings.

kieronlanning and others added 5 commits December 4, 2025 16:25
- Add 'Include additional directories' section to project config docs
- Update multi-projects docs to feature include as primary sharing method
- Add include option to programmatic config example
- Document path requirements and usage patterns
Copilot AI review requested due to automatic review settings December 5, 2025 00:59
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Dec 5, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

📝 Walkthrough

Walkthrough

Adds an include-path feature allowing projects to reference additional relative directories of LikeC4 source files, with schema validation, runtime resolution in the language server, workspace loading of included files, docs updates, and tests. Separately, adds diagram focus-on-element with an auto-unfocus timer, state machine, utils, UI wiring, and tests.

Changes

Cohort / File(s) Summary
Documentation (Include)
apps/docs/src/content/docs/dsl/Config/index.mdx, apps/docs/src/content/docs/dsl/Config/multi-projects.mdx, apps/docs/src/content/docs/dsl/Config/programmatic.mdx
New docs describing the include array config, examples, file-tree illustrations, path rules (relative only), scanning behavior, and updated multi-project guidance (include over symlinks).
Config: include schema & validation
packages/config/src/schema.include.ts, packages/config/src/schema.spec.ts, packages/config/src/schema.ts
Added IncludeSchema (zod), RELATIVE_PATH_REGEX, LikeC4IncludeConfig type, validateIncludePaths(), schema integration into project config, and tests covering valid/invalid paths.
JSON Schema
schemas/likec4-config.schema.json
Added top-level include property (array of non-empty strings with relative-path pattern and example).
Language Server — ProjectsManager
packages/language-server/src/workspace/ProjectsManager.ts, packages/language-server/src/workspace/ProjectsManager.spec.ts
Project data and public Project API now include includePaths. Resolves include entries relative to project folder, logs resolved paths, validates overlaps, maps documents under include paths to projects, exposes getAllIncludePaths(), and adds comprehensive tests.
Language Server — Workspace loading
packages/language-server/src/workspace/WorkspaceManager.ts
loadAdditionalDocuments now scans project include paths, recursively loads matching files into Langium documents, and handles errors with logging.
Diagram API & navigation
packages/diagram/src/likec4diagram/state/diagram-api.ts
Added DiagramApi.focusOnElement(elementFqn) and extended navigateTo signature to accept optional focusOnElement parameter passed through navigation events.
Diagram state machine: auto-unfocus
packages/diagram/src/likec4diagram/state/machine.actions.ts, machine.setup.ts, machine.state.navigating.ts, machine.state.ready.focused.ts, machine.state.ready.idle.ts, machine.ts
Added AUTO_UNFOCUS_DELAY, start/cancel auto-unfocus timer actions, focus.autoUnfocus event, context.autoUnfocusTimer and lastOnNavigate.focusOnElement, updated guards/transitions and navigation flow to enqueue focus.node with autoUnfocus and to cancel/start timers in focused state.
Diagram UI wiring
packages/diagram/src/search/components/ElementsColumn.tsx, packages/diagram/src/search/components/PickView.tsx, packages/diagram/src/search/components/ViewsColum.tsx
Threaded new focusOnElement prop through ViewButton and PickView; changed navigation logic to either focus in-place or navigate with focusOnElement depending on current view.
Diagram utils & types
packages/diagram/src/likec4diagram/state/utils.ts, packages/diagram/src/likec4diagram/state/utils.spec.ts, packages/diagram/src/likec4diagram/state/types.ts
Added NodeWithData type and findNodeByModelFqn utility (with tests) to locate nodes by model FQN.
Minor validator adjustments
packages/config/src/schema.image-alias.ts, packages/config/src/schema.theme.ts
Replaced .nonempty() with .min(1, ...) on several string validators (no semantic change).
Test updates / refactor
packages/language-server/src/views/LikeC4ManualLayouts.spec.ts
Tests updated to retrieve registered Project instances via getProject(id) rather than using registerProject's returned object directly.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • Areas needing extra attention:
    • ProjectsManager.ts: include path resolution, document attribution precedence, and overlap/warning logic.
    • WorkspaceManager.ts: recursive scanning and error handling for include paths.
    • validateIncludePaths / RELATIVE_PATH_REGEX: correctness for Windows paths and protocol/absolute detection.
    • State machine files: timing, guards, and interaction of auto-unfocus timer with focused/idle transitions.
    • UI wiring: ensure focusOnElement prop propagation and navigation vs in-place focus behavior.

Poem

🐰 I hopped through folders, near and wide,
Linked shared specs where secrets hide,
I nudged a node to catch the light,
Then tick-tock—unfocused out of sight,
A rabbit cheer for code aligned! 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the two main features: a new include configuration option for shared files and element navigation from search. It accurately reflects the primary changes in the changeset.
Description check ✅ Passed The description clearly states the main objective (new include configuration option), mentions supporting documentation/validation/tests, and completes all checklist items. The description aligns well with the actual changes shown in the raw summary.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch kieron/shared-files

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6696948 and 893333a.

📒 Files selected for processing (9)
  • packages/diagram/src/likec4diagram/state/diagram-api.ts (5 hunks)
  • packages/diagram/src/likec4diagram/state/machine.actions.ts (3 hunks)
  • packages/diagram/src/likec4diagram/state/machine.setup.ts (4 hunks)
  • packages/diagram/src/likec4diagram/state/machine.state.navigating.ts (4 hunks)
  • packages/diagram/src/likec4diagram/state/machine.state.ready.focused.ts (3 hunks)
  • packages/diagram/src/likec4diagram/state/machine.state.ready.idle.ts (1 hunks)
  • packages/diagram/src/likec4diagram/state/machine.ts (1 hunks)
  • packages/diagram/src/likec4diagram/state/types.ts (1 hunks)
  • packages/diagram/src/likec4diagram/state/utils.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • packages/diagram/src/likec4diagram/state/machine.state.navigating.ts
  • packages/diagram/src/likec4diagram/state/machine.setup.ts
  • packages/diagram/src/likec4diagram/state/machine.actions.ts
  • packages/diagram/src/likec4diagram/state/machine.ts
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use pnpm ci:typecheck for TypeScript type checking across all packages except documentation

Files:

  • packages/diagram/src/likec4diagram/state/machine.state.ready.idle.ts
  • packages/diagram/src/likec4diagram/state/types.ts
  • packages/diagram/src/likec4diagram/state/utils.ts
  • packages/diagram/src/likec4diagram/state/diagram-api.ts
  • packages/diagram/src/likec4diagram/state/machine.state.ready.focused.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use oxlint with type-aware rules for linting instead of eslint, and run pnpm ci:lint to validate all packages except documentation

Files:

  • packages/diagram/src/likec4diagram/state/machine.state.ready.idle.ts
  • packages/diagram/src/likec4diagram/state/types.ts
  • packages/diagram/src/likec4diagram/state/utils.ts
  • packages/diagram/src/likec4diagram/state/diagram-api.ts
  • packages/diagram/src/likec4diagram/state/machine.state.ready.focused.ts
**/*.{ts,tsx,js,jsx,json,md}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use pnpm fmt to format code with dprint instead of Prettier or eslint for formatting

Files:

  • packages/diagram/src/likec4diagram/state/machine.state.ready.idle.ts
  • packages/diagram/src/likec4diagram/state/types.ts
  • packages/diagram/src/likec4diagram/state/utils.ts
  • packages/diagram/src/likec4diagram/state/diagram-api.ts
  • packages/diagram/src/likec4diagram/state/machine.state.ready.focused.ts
🧠 Learnings (1)
📚 Learning: 2025-12-05T15:36:11.968Z
Learnt from: CR
Repo: likec4/likec4 PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-05T15:36:11.968Z
Learning: Applies to packages/*/**/!(node_modules)/**/*.spec.ts : Unit tests must be located in `packages/*/src/**/*.spec.ts` or `packages/*/src/**/__test__/*.spec.ts` files

Applied to files:

  • packages/diagram/src/likec4diagram/state/types.ts
🧬 Code graph analysis (2)
packages/diagram/src/likec4diagram/state/diagram-api.ts (2)
packages/core/src/model/view/LikeC4ViewModel.ts (1)
  • node (304-307)
packages/diagram/src/likec4diagram/state/utils.ts (1)
  • findNodeByModelFqn (24-30)
packages/diagram/src/likec4diagram/state/machine.state.ready.focused.ts (3)
packages/diagram/src/likec4diagram/state/machine.actions.ts (3)
  • startAutoUnfocusTimer (799-804)
  • undimEverything (202-209)
  • cancelAutoUnfocusTimer (806-806)
packages/diagram/src/likec4diagram/state/machine.actions.layout.ts (1)
  • returnViewportBefore (187-207)
packages/diagram/src/likec4diagram/state/machine.setup.ts (1)
  • targetState (345-351)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: checks / ⊞ windows build
  • GitHub Check: checks / 🔬 tests
  • GitHub Check: checks / 🛠️ build
🔇 Additional comments (6)
packages/diagram/src/likec4diagram/state/types.ts (1)

11-12: LGTM!

The NodeWithData type provides a minimal, generic shape for nodes with a data payload. This allows findNodeByModelFqn to work with various node types while ensuring the data property exists.

packages/diagram/src/likec4diagram/state/utils.ts (1)

24-30: LGTM!

The function signature enriches the return type as suggested in previous reviews, explicitly indicating that the returned node has data.modelFqn. The 'modelFqn' in n.data guard ensures safe property access before comparison.

packages/diagram/src/likec4diagram/state/diagram-api.ts (2)

220-226: LGTM!

The focusOnElement implementation correctly uses findNodeByModelFqn to locate the node by element FQN and triggers focus with autoUnfocus: true to enable the timer-based unfocus behavior. The silent no-op when no node is found is appropriate for search-triggered focus scenarios.


151-157: Verify optional parameter handling.

The focusOnElement parameter is correctly spread into the event payload when provided. The as any cast is consistent with the existing pattern for viewId.

packages/diagram/src/likec4diagram/state/machine.state.ready.idle.ts (1)

83-96: LGTM on the guarded transition logic.

The two-branch guard structure correctly prioritizes search-triggered focus (autoUnfocus: true) over the regular FocusMode check. This ensures search can always highlight elements regardless of the FocusMode feature setting.

packages/diagram/src/likec4diagram/state/machine.state.ready.focused.ts (1)

26-50: LGTM on auto-unfocus timer integration.

The timer lifecycle is correctly managed:

  • Entry starts the timer after focus actions complete
  • Exit cancels the timer and resets the autoUnfocusTimer flag
  • The new focus.autoUnfocus transition provides the timer-triggered path back to idle

The conditional start in startAutoUnfocusTimer() (checking context.autoUnfocusTimer && AUTO_UNFOCUS_DELAY > 0) ensures the timer only fires for search-triggered focus scenarios.


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

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces a new include configuration option for LikeC4 project configs, enabling users to share specification files across multiple projects without relying on symlinks. The feature resolves include paths relative to the project folder, recursively scans included directories for .c4 files, and provides overlap detection warnings to prevent configuration issues.

Key Changes

  • Added include array field to project configuration schema for specifying additional source directories
  • Implemented include path resolution and validation in ProjectsManager with proper overlap detection
  • Extended WorkspaceManager to load documents from configured include paths during initialization

Reviewed changes

Copilot reviewed 14 out of 15 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
schemas/likec4-config.schema.json Added include field definition with pattern validation for relative paths
packages/config/src/schema.ts Integrated IncludeSchema and validateIncludePaths into main project config schema
packages/config/src/schema.include.ts New file defining validation schema and logic for include paths
packages/config/src/schema.spec.ts Added comprehensive test coverage for include path validation scenarios
packages/language-server/src/workspace/ProjectsManager.ts Implemented include path resolution, overlap detection, and document assignment logic
packages/language-server/src/workspace/ProjectsManager.spec.ts Added integration tests for include path functionality
packages/language-server/src/workspace/WorkspaceManager.ts Extended document loading to scan configured include paths
apps/docs/src/content/docs/dsl/Config/*.mdx Updated documentation with include feature explanation and examples
packages/icons/scripts/update-icons.mjs Fixed icon name mapping (JQuery → jQuery) for remote file rename
pnpm-workspace.yaml, pnpm-lock.yaml, package.json, e2e/package.json Dependency version updates (React 19.2.0→19.2.1, nano-staged 0.8.0→0.9.0)
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

Copy link
Copy Markdown
Contributor

Copilot AI commented Dec 5, 2025

@kieronlanning I've opened a new pull request, #2426, to work on those changes. Once the pull request is ready, I'll request review from you.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (4)
packages/language-server/src/workspace/ProjectsManager.ts (1)

389-416: Consider deduplicating overlap warnings.

When projects A and B both have overlapping include paths, the current logic may log symmetric warnings (A→B and B→A) when the second project is registered. This is minor since it only affects log verbosity during project registration.

packages/config/src/schema.ts (1)

18-19: Include config wiring and validation are consistent; post-parse helper call is optional redundancy

The new include field is correctly threaded through the project config schema and post-parse validation, ensuring only relative paths are accepted and matching the docs/JSON schema behaviour. Since the schema for include already enforces the same relative-path constraints, the extra validateIncludePaths call after a successful parse is functionally redundant for config-based usage (though still useful as a standalone helper). If you prefer a single validation source here, you could either drop the extra call or move its logic into a schema-level refinement; current behaviour is still perfectly fine.

Also applies to: 63-71, 239-255

schemas/likec4-config.schema.json (1)

33-41: Include JSON schema matches runtime validation; consider factoring out the shared path pattern

The new include property definition (array of non-empty strings with the relative-path pattern) is consistent with the runtime validation and docs. Since the same pattern is also used for image alias values, you might optionally extract a common $defs entry for “relative path” to avoid future drift between these spots.

packages/config/src/schema.spec.ts (1)

4-5: Include tests give thorough coverage of config and helper behaviour

The new tests around the include field, the schema, and the helper cover the key scenarios (valid/invalid/empty/undefined paths, absolute and URL cases, and multi-error reporting) and closely match the intended semantics. If you ever find yourself tweaking the allowed path shapes, you might consider extracting the repeated “validPaths”/“relativePaths” arrays into shared fixtures to keep the expectations in sync, but as-is this is already solid coverage.

Also applies to: 184-269, 538-650

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e67db43 and 7ae32e2.

📒 Files selected for processing (11)
  • apps/docs/src/content/docs/dsl/Config/index.mdx (1 hunks)
  • apps/docs/src/content/docs/dsl/Config/multi-projects.mdx (2 hunks)
  • apps/docs/src/content/docs/dsl/Config/programmatic.mdx (1 hunks)
  • packages/config/src/schema.include.ts (1 hunks)
  • packages/config/src/schema.spec.ts (3 hunks)
  • packages/config/src/schema.ts (3 hunks)
  • packages/icons/scripts/update-icons.mjs (1 hunks)
  • packages/language-server/src/workspace/ProjectsManager.spec.ts (1 hunks)
  • packages/language-server/src/workspace/ProjectsManager.ts (6 hunks)
  • packages/language-server/src/workspace/WorkspaceManager.ts (1 hunks)
  • schemas/likec4-config.schema.json (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
packages/config/src/schema.ts (2)
packages/config/src/schema.include.ts (2)
  • IncludeSchema (16-24)
  • validateIncludePaths (28-48)
packages/core/src/model/__test__/fixture.ts (1)
  • parsed (163-163)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: checks / 🔬 e2e types
  • GitHub Check: checks / 🔬 e2e tests
  • GitHub Check: checks / 📖 docs
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: checks / ⊞ windows build
🔇 Additional comments (17)
packages/icons/scripts/update-icons.mjs (1)

79-79: LGTM!

Correct fix to accommodate the upstream icon filename change from JQuery.svg to jQuery.svg.

packages/language-server/src/workspace/ProjectsManager.spec.ts (6)

340-360: LGTM!

Test correctly verifies that relative include paths are resolved to absolute URIs based on the project folder.


362-396: LGTM!

Comprehensive test covering document attribution for project folder, include paths, nested paths, and unrelated paths.


398-423: LGTM!

Tests correctly verify that includePaths is undefined when not configured or when configured with an empty array.


425-470: LGTM!

Good coverage of getAllIncludePaths() aggregation across multiple projects with varying include configurations.


472-492: LGTM!

Critical test ensuring project folder precedence over include paths when they overlap.


494-550: LGTM!

Tests correctly verify dynamic update and removal of include paths when projects are reloaded with changed configurations.

packages/language-server/src/workspace/ProjectsManager.ts (6)

61-69: LGTM!

Good design keeping both URI representation (for public API) and string representation (for efficient prefix matching) of include paths.


76-79: LGTM!

Public interface correctly exposes optional includePaths for consumers needing access to resolved include directories.


213-219: LGTM!

Conditional spreading of includePaths correctly ensures the property is only present when configured.


548-564: LGTM!

Document filtering correctly extended to include documents from configured include paths during project rebuild.


586-604: LGTM!

Correct precedence implementation: project folders are checked first, then include paths, with fallback to default. This ensures a project's own folder always takes priority over another project's include path.


620-634: LGTM!

Clean implementation providing a flat list of all include paths with their associated project IDs for workspace scanning.

apps/docs/src/content/docs/dsl/Config/programmatic.mdx (1)

37-37: LGTM!

Good addition demonstrating the new include configuration option with clear relative path examples.

apps/docs/src/content/docs/dsl/Config/index.mdx (1)

59-96: Include section clearly matches the new config behaviour

The new “Include additional directories” section accurately describes how include works (relative-only paths, recursive scan from the project folder) and aligns with the implementation and schema. Looks good.

apps/docs/src/content/docs/dsl/Config/multi-projects.mdx (1)

66-153: Multi-project sharing docs align well with the include feature

The updated “Share specification” section and the include examples (including multi-directory and the path resolution/requirements asides) accurately reflect how the new include option behaves, while clearly de‑emphasizing symlinks. No changes needed.

packages/config/src/schema.include.ts (1)

1-48: Include schema and helper cleanly enforce relative path constraints

The include schema and validateIncludePaths helper correctly enforce “non-empty, relative, non-URL” paths and provide nice aggregate error reporting for multiple bad entries. This matches the documented rules and the JSON schema; implementation looks solid.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
packages/config/src/schema.include.ts (1)

3-5: Consider extracting the shared regex to avoid duplication.

RELATIVE_PATH_REGEX is identical to IMAGE_ALIAS_VALUE_REGEX in schema.image-alias.ts. Both validate "relative path (no leading slash, drive letter, or protocol)".

You could extract this to a shared constants file:

// e.g., in schema.common.ts or similar
export const RELATIVE_PATH_REGEX = /^(?!\/|[A-Za-z]:[\\\/])(?!.*:\/\/).*$/
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7ae32e2 and 0a61bec.

📒 Files selected for processing (3)
  • packages/config/src/schema.image-alias.ts (1 hunks)
  • packages/config/src/schema.include.ts (1 hunks)
  • packages/config/src/schema.theme.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
packages/config/src/schema.theme.ts (1)
packages/core/src/styles/types.ts (1)
  • ThemeColor (65-65)
packages/config/src/schema.include.ts (2)
packages/core/src/builder/Builder.views.ts (1)
  • include (379-384)
packages/likec4/src/cli/options.ts (1)
  • path (5-11)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: checks / 🛠️ build
  • GitHub Check: checks / ⊞ windows build
🔇 Additional comments (5)
packages/config/src/schema.image-alias.ts (1)

9-15: LGTM!

The migration from .nonempty() to .min(1, ...) aligns with Zod v3.23+ best practices where .nonempty() is deprecated. The error message is clear and behavior remains unchanged.

packages/config/src/schema.theme.ts (1)

56-71: LGTM!

Both .min(1, ...) replacements are consistent with the deprecation of .nonempty() in Zod v3.23+. The distinct error messages ("Color name cannot be empty" vs "Color value cannot be empty") appropriately differentiate between the two validation contexts.

packages/config/src/schema.include.ts (3)

1-14: LGTM!

The schema follows established patterns from schema.image-alias.ts. Using .min(1, ...) addresses the prior review feedback about deprecated .nonempty().


16-24: LGTM!

The schema metadata provides clear documentation with practical examples. Making the array optional is appropriate for a config field that may not always be needed.


28-47: Defensive validation is consistent with the established pattern.

Similar to validateImageAliases in schema.image-alias.ts, this function performs defensive validation even though Zod already enforces the regex. The consolidated error reporting with all invalid paths is helpful for debugging.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
packages/config/src/schema.spec.ts (1)

617-622: Consider adding Windows-style path and empty string tests for consistency.

The validateIncludePaths tests only cover Unix-style absolute paths (/absolute/path), while IncludeSchema tests also cover Windows paths (C:\\absolute\\path, D:/another/path). Additionally, there's no empty string rejection test here, unlike the IncludeSchema tests.

For parity and explicit coverage:

     it('should reject absolute paths', ({ expect }) => {
-      const absolutePaths = ['/absolute/path']
-      expect(() => validateIncludePaths(absolutePaths)).toThrow(
-        'Invalid include path(s): "/absolute/path" (must be relative paths without leading slash, drive letter, or protocol)',
-      )
+      const testCases = [
+        { path: '/absolute/path', expected: '"/absolute/path"' },
+        { path: 'C:\\absolute\\path', expected: '"C:\\absolute\\path"' },
+        { path: 'D:/another/path', expected: '"D:/another/path"' },
+      ]
+
+      for (const { path, expected } of testCases) {
+        expect(() => validateIncludePaths([path]), `Path "${path}" should be rejected`).toThrow(
+          `Invalid include path(s): ${expected} (must be relative paths without leading slash, drive letter, or protocol)`,
+        )
+      }
+    })
+
+    it('should reject empty strings', ({ expect }) => {
+      expect(() => validateIncludePaths([''])).toThrow('Include path cannot be empty')
     })
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0a61bec and 1d81248.

📒 Files selected for processing (2)
  • packages/config/src/schema.image-alias.ts (1 hunks)
  • packages/config/src/schema.spec.ts (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/config/src/schema.image-alias.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: checks / 🔬 tests
  • GitHub Check: checks / ⊞ windows build
  • GitHub Check: checks / 🛠️ build
🔇 Additional comments (3)
packages/config/src/schema.spec.ts (3)

4-4: LGTM!

Import follows the established pattern for schema imports in this file.


184-269: Well-structured test coverage for the include field.

The tests comprehensively cover valid paths, empty/undefined values, and properly reject absolute paths (including Windows formats), URLs, and empty strings. The mixed valid/invalid path test at lines 260-268 ensures the validation fails fast on any invalid entry.


538-600: Thorough schema validation tests.

Good coverage of the IncludeSchema parsing behavior including edge cases like single-dot (.) and double-dot (..) paths, and paths containing dashes, underscores, and dots.

* feat: added search navigate to selected item #1711

* docs: based on setting the auto unfocus to zero (for now)
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (3)
packages/diagram/src/likec4diagram/state/machine.state.ready.focused.spec.ts (1)

1-64: Consider adding integration tests for state machine behavior.

The current tests verify event and context shapes, which is useful for type safety. However, integration tests that verify actual state machine behavior would provide more value:

  • Test that focus.node with autoUnfocus: true triggers the correct state transitions
  • Test that the autoUnfocus timer is actually started/cancelled
  • Test that focus.autoUnfocus event correctly transitions back to idle state
  • Test the timing behavior (e.g., auto-unfocus after expected delay)

These integration tests would catch runtime issues that type-level tests cannot, such as incorrect action wiring or timing bugs.

packages/diagram/src/likec4diagram/state/machine.state.navigating.spec.ts (1)

67-115: Consider adding integration tests for navigation behavior.

Similar to the focused.spec.ts file, these tests verify event and context shapes but don't test actual state machine behavior. Consider adding integration tests that verify:

  • Navigation with focusOnElement actually focuses the target element after navigation completes
  • Navigation without focusOnElement follows the default fit-diagram behavior
  • Edge cases like navigating with focusOnElement when the element doesn't exist in the target view
packages/diagram/src/likec4diagram/state/machine.state.navigating.ts (1)

198-225: Consider logging when focusOnElement doesn't resolve to a node.

The logic correctly handles the case where focusOnElement is provided but doesn't match any node (falls back to fitDiagram). However, this silent fallback could be confusing for users or developers debugging search/navigation issues.

Consider adding a console.warn or debug log when focusOnElement is provided but nodeToFocus is null:

 const nodeToFocus = isTruthy(focusOnElement)
   ? findNodeByElementFqn(event.xynodes, focusOnElement)
   : null
+
+if (focusOnElement && !nodeToFocus) {
+  console.warn(`Could not find node for focusOnElement: ${focusOnElement}`)
+}

This would help with debugging and provide visibility into why the expected focus didn't occur.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 00b2c0b and b7a69c4.

📒 Files selected for processing (12)
  • packages/diagram/src/likec4diagram/state/diagram-api.ts (4 hunks)
  • packages/diagram/src/likec4diagram/state/machine.actions.ts (3 hunks)
  • packages/diagram/src/likec4diagram/state/machine.setup.ts (4 hunks)
  • packages/diagram/src/likec4diagram/state/machine.state.navigating.spec.ts (1 hunks)
  • packages/diagram/src/likec4diagram/state/machine.state.navigating.ts (4 hunks)
  • packages/diagram/src/likec4diagram/state/machine.state.ready.focused.spec.ts (1 hunks)
  • packages/diagram/src/likec4diagram/state/machine.state.ready.focused.ts (3 hunks)
  • packages/diagram/src/likec4diagram/state/machine.state.ready.idle.ts (1 hunks)
  • packages/diagram/src/likec4diagram/state/machine.ts (1 hunks)
  • packages/diagram/src/search/components/ElementsColumn.tsx (1 hunks)
  • packages/diagram/src/search/components/PickView.tsx (2 hunks)
  • packages/diagram/src/search/components/ViewsColum.tsx (3 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use pnpm ci:typecheck for TypeScript type checking across all packages except documentation

Files:

  • packages/diagram/src/likec4diagram/state/machine.state.navigating.ts
  • packages/diagram/src/search/components/ElementsColumn.tsx
  • packages/diagram/src/likec4diagram/state/machine.state.ready.focused.spec.ts
  • packages/diagram/src/likec4diagram/state/machine.setup.ts
  • packages/diagram/src/likec4diagram/state/machine.ts
  • packages/diagram/src/likec4diagram/state/machine.state.navigating.spec.ts
  • packages/diagram/src/likec4diagram/state/machine.state.ready.focused.ts
  • packages/diagram/src/likec4diagram/state/diagram-api.ts
  • packages/diagram/src/likec4diagram/state/machine.state.ready.idle.ts
  • packages/diagram/src/search/components/PickView.tsx
  • packages/diagram/src/search/components/ViewsColum.tsx
  • packages/diagram/src/likec4diagram/state/machine.actions.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use oxlint with type-aware rules for linting instead of eslint, and run pnpm ci:lint to validate all packages except documentation

Files:

  • packages/diagram/src/likec4diagram/state/machine.state.navigating.ts
  • packages/diagram/src/search/components/ElementsColumn.tsx
  • packages/diagram/src/likec4diagram/state/machine.state.ready.focused.spec.ts
  • packages/diagram/src/likec4diagram/state/machine.setup.ts
  • packages/diagram/src/likec4diagram/state/machine.ts
  • packages/diagram/src/likec4diagram/state/machine.state.navigating.spec.ts
  • packages/diagram/src/likec4diagram/state/machine.state.ready.focused.ts
  • packages/diagram/src/likec4diagram/state/diagram-api.ts
  • packages/diagram/src/likec4diagram/state/machine.state.ready.idle.ts
  • packages/diagram/src/search/components/PickView.tsx
  • packages/diagram/src/search/components/ViewsColum.tsx
  • packages/diagram/src/likec4diagram/state/machine.actions.ts
**/*.{ts,tsx,js,jsx,json,md}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use pnpm fmt to format code with dprint instead of Prettier or eslint for formatting

Files:

  • packages/diagram/src/likec4diagram/state/machine.state.navigating.ts
  • packages/diagram/src/search/components/ElementsColumn.tsx
  • packages/diagram/src/likec4diagram/state/machine.state.ready.focused.spec.ts
  • packages/diagram/src/likec4diagram/state/machine.setup.ts
  • packages/diagram/src/likec4diagram/state/machine.ts
  • packages/diagram/src/likec4diagram/state/machine.state.navigating.spec.ts
  • packages/diagram/src/likec4diagram/state/machine.state.ready.focused.ts
  • packages/diagram/src/likec4diagram/state/diagram-api.ts
  • packages/diagram/src/likec4diagram/state/machine.state.ready.idle.ts
  • packages/diagram/src/search/components/PickView.tsx
  • packages/diagram/src/search/components/ViewsColum.tsx
  • packages/diagram/src/likec4diagram/state/machine.actions.ts
packages/*/**/!(node_modules)/**/*.spec.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Unit tests must be located in packages/*/src/**/*.spec.ts or packages/*/src/**/__test__/*.spec.ts files

Files:

  • packages/diagram/src/likec4diagram/state/machine.state.ready.focused.spec.ts
  • packages/diagram/src/likec4diagram/state/machine.state.navigating.spec.ts
🧠 Learnings (1)
📚 Learning: 2025-12-05T15:36:11.968Z
Learnt from: CR
Repo: likec4/likec4 PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-05T15:36:11.968Z
Learning: Applies to packages/*/**/!(node_modules)/**/*.spec.ts : Unit tests must be located in `packages/*/src/**/*.spec.ts` or `packages/*/src/**/__test__/*.spec.ts` files

Applied to files:

  • packages/diagram/src/likec4diagram/state/machine.state.navigating.spec.ts
🧬 Code graph analysis (5)
packages/diagram/src/likec4diagram/state/machine.setup.ts (1)
packages/diagram/src/likec4diagram/state/aligners.ts (1)
  • AlignmentMode (8-8)
packages/diagram/src/likec4diagram/state/machine.state.navigating.spec.ts (1)
packages/diagram/src/likec4diagram/state/machine.state.navigating.ts (1)
  • findNodeByElementFqn (16-22)
packages/diagram/src/likec4diagram/state/machine.state.ready.focused.ts (3)
packages/diagram/src/likec4diagram/state/machine.actions.ts (3)
  • startAutoUnfocusTimer (775-780)
  • undimEverything (184-191)
  • cancelAutoUnfocusTimer (782-782)
packages/diagram/src/likec4diagram/state/machine.actions.layout.ts (1)
  • returnViewportBefore (180-200)
packages/diagram/src/likec4diagram/state/machine.setup.ts (2)
  • machine (249-335)
  • targetState (337-343)
packages/diagram/src/likec4diagram/state/diagram-api.ts (1)
packages/core/src/model/view/LikeC4ViewModel.ts (1)
  • node (304-307)
packages/diagram/src/search/components/ViewsColum.tsx (1)
packages/core/src/model/LikeC4Model.ts (3)
  • LikeC4Model (60-872)
  • View (932-932)
  • view (502-505)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: checks / 🔬 tests
  • GitHub Check: checks / ⊞ windows build
  • GitHub Check: checks / 🛠️ build
🔇 Additional comments (17)
packages/diagram/src/likec4diagram/state/machine.ts (1)

52-52: LGTM! Context initialization.

The autoUnfocusTimer: false initialization is correct and consistent with the type definition in machine.setup.ts.

packages/diagram/src/likec4diagram/state/machine.state.navigating.spec.ts (1)

5-65: LGTM! Comprehensive unit tests for findNodeByElementFqn.

The test coverage for findNodeByElementFqn is excellent, covering:

  • Edge cases (empty array, no match)
  • Success cases (single match, multiple matches)
  • Null/missing data (null modelFqn, missing modelFqn property)
  • Mixed data shapes

The test on lines 30-37 correctly verifies that the first matching node is returned when multiple nodes share the same modelFqn.

packages/diagram/src/search/components/ElementsColumn.tsx (1)

296-306: LGTM! Inverted logic improves clarity.

The refactored logic is clearer by explicitly handling the same-view and different-view cases separately. The comments on lines 297 and 302 help document the intent.

Both branches use a 100ms delay, which is consistent and allows the search UI close animation to complete before triggering navigation or focus.

packages/diagram/src/likec4diagram/state/machine.state.ready.idle.ts (1)

83-96: LGTM! Priority-based guards enable search-initiated focus.

The refactored focus.node transition array correctly implements priority-based evaluation:

  1. First guard allows focus when autoUnfocus=true (search-initiated), bypassing the FocusMode requirement
  2. Second guard requires FocusMode to be enabled for manual focus

This design allows search-initiated focus to work regardless of the FocusMode setting, which is the correct UX. The comments clearly document the intent.

packages/diagram/src/likec4diagram/state/machine.state.navigating.ts (1)

16-22: LGTM! Clean helper function.

The findNodeByElementFqn helper is well-implemented:

  • Correctly checks for modelFqn property existence
  • Handles null/undefined modelFqn
  • Returns the first matching node (as tested in the spec file)
packages/diagram/src/search/components/PickView.tsx (1)

105-105: LGTM! Consistent prop wiring.

The focusOnElement={elementFqn} prop is correctly wired to both scoped and other ViewButton instances, enabling the focus-on-element behavior when users select a view from the picker.

Also applies to: 123-123

packages/diagram/src/search/components/ViewsColum.tsx (3)

2-2: LGTM!

The Fqn type import and the optional focusOnElement prop addition properly support the new element focusing feature from search.

Also applies to: 107-107


137-137: LGTM!

The disabled state logic correctly allows interaction when focusOnElement is provided, even on the current view. This enables users to focus on specific elements from search results.


117-130: The setTimeout pattern here is intentional UI animation timing, not a state machine synchronization issue.

Both branches use a 100ms delay after sending the close event to the search state machine. This is necessary to allow the search modal to close visually before navigation occurs. The same pattern appears consistently across multiple search components (ViewsColumn and ElementsColumn), indicating this is a deliberate, established approach rather than a workaround.

The state machine's close event is synchronous, but the search UI itself has animation/transition delays that require the 100ms buffer before navigation. This is standard practice when coordinating async UI operations with component state changes.

Likely an incorrect or invalid review comment.

packages/diagram/src/likec4diagram/state/diagram-api.ts (3)

50-54: LGTM!

The navigateTo signature extension and implementation correctly support passing the optional focusOnElement parameter through to the navigation event. The conditional spread ensures the property is only included when provided.

Also applies to: 151-157


105-109: LGTM!

The new focusOnElement method is well-documented and appropriately added to the public DiagramApi interface for search-driven element highlighting.


220-226: The implementation is correct as-is. The find() approach and silent failure are both intentional design patterns validated by the test suite. When multiple nodes share the same modelFqn in a view (an uncommon scenario), returning the first match is the documented behavior. The silent failure when a node is not found is appropriate since the element may not be present in the current view due to filtering or navigation context. No changes are needed.

packages/diagram/src/likec4diagram/state/machine.actions.ts (3)

103-122: LGTM!

The assignFocusedNode action correctly captures the autoUnfocus flag from focus.node events and includes it in the returned context update. This properly supports the auto-unfocus timer infrastructure.


768-782: LGTM! Auto-unfocus infrastructure is disabled by design.

The auto-unfocus timer infrastructure is correctly implemented but intentionally disabled with AUTO_UNFOCUS_DELAY = 0. The conditional check ensures the timer only starts when the delay is positive, making this a clean feature flag. The documentation clearly explains this behavior.


835-835: LGTM!

The navigation history correctly captures the focusOnElement parameter from navigate.to events, enabling proper tracking of element-focused navigation requests.

packages/diagram/src/likec4diagram/state/machine.state.ready.focused.ts (2)

8-8: LGTM!

The auto-unfocus timer lifecycle is correctly integrated into the focused state entry and exit actions, ensuring proper timer management and cleanup. The implementation follows state machine best practices.

Also applies to: 18-18, 35-35, 41-41


44-44: LGTM!

The autoUnfocusTimer context flag is properly reset on exit, and the focus.autoUnfocus event handler correctly transitions to the idle state. This completes the auto-unfocus flow when the timer fires (though currently disabled with delay = 0).

Also applies to: 48-50

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
packages/diagram/src/likec4diagram/state/machine.state.navigating.spec.ts (1)

67-114: Tests validate object structure but not state machine behavior.

These tests verify that objects can be constructed with focusOnElement properties, but they don't test the actual state machine integration—specifically that navigate.to events with focusOnElement trigger the expected focus.node action or that lastOnNavigate context is correctly assigned during transitions.

Consider adding integration tests that:

  1. Send a navigate.to event with focusOnElement to the state machine
  2. Verify that focus.node is enqueued when the element exists in xynodes
  3. Verify fallback to fitDiagram when the element doesn't exist
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b7a69c4 and 7a387a4.

📒 Files selected for processing (2)
  • packages/diagram/src/likec4diagram/state/machine.state.navigating.spec.ts (1 hunks)
  • packages/diagram/src/likec4diagram/state/machine.state.navigating.ts (4 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use pnpm ci:typecheck for TypeScript type checking across all packages except documentation

Files:

  • packages/diagram/src/likec4diagram/state/machine.state.navigating.spec.ts
  • packages/diagram/src/likec4diagram/state/machine.state.navigating.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use oxlint with type-aware rules for linting instead of eslint, and run pnpm ci:lint to validate all packages except documentation

Files:

  • packages/diagram/src/likec4diagram/state/machine.state.navigating.spec.ts
  • packages/diagram/src/likec4diagram/state/machine.state.navigating.ts
**/*.{ts,tsx,js,jsx,json,md}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use pnpm fmt to format code with dprint instead of Prettier or eslint for formatting

Files:

  • packages/diagram/src/likec4diagram/state/machine.state.navigating.spec.ts
  • packages/diagram/src/likec4diagram/state/machine.state.navigating.ts
packages/*/**/!(node_modules)/**/*.spec.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Unit tests must be located in packages/*/src/**/*.spec.ts or packages/*/src/**/__test__/*.spec.ts files

Files:

  • packages/diagram/src/likec4diagram/state/machine.state.navigating.spec.ts
🧬 Code graph analysis (2)
packages/diagram/src/likec4diagram/state/machine.state.navigating.spec.ts (1)
packages/diagram/src/likec4diagram/state/machine.state.navigating.ts (1)
  • findNodeByElementFqn (18-24)
packages/diagram/src/likec4diagram/state/machine.state.navigating.ts (1)
packages/diagram/src/likec4diagram/state/machine.actions.layout.ts (1)
  • raiseFitDiagram (149-161)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: checks / 🛠️ build
  • GitHub Check: checks / ⊞ windows build
🔇 Additional comments (3)
packages/diagram/src/likec4diagram/state/machine.state.navigating.spec.ts (1)

5-65: Good test coverage for the helper function.

The findNodeByElementFqn tests comprehensively cover edge cases including empty arrays, missing/null modelFqn, and multiple matches. This provides solid validation of the helper's behavior.

packages/diagram/src/likec4diagram/state/machine.state.navigating.ts (2)

16-24: LGTM!

The helper function correctly handles edge cases:

  • The 'modelFqn' in n.data check guards against missing properties
  • Strict equality handles null values appropriately
  • The generic constraint allows flexibility while maintaining type safety

200-227: Logic looks correct; verify fromHistory path doesn't need similar handling.

The focus-on-element implementation correctly:

  • Uses isTruthy to handle null/undefined focusOnElement
  • Falls back to raiseFitDiagram when no matching node exists
  • Passes autoUnfocus: true for transient focus behavior

However, the fromHistory code path (lines 112-160) exits early without this check. If a user navigates via browser back/forward to a view that was previously reached with focusOnElement, should that focus be restored? The current implementation only applies focusOnElement to new navigation entries.

… of the dependency tree

Because the old machine.state.navigating.spec.ts imported from the machine.state.navigating.ts it pulled in far too much which resulted in test failures in GH actions(!?). Moved function to utils.ts and added tests for that instead.
@kieronlanning kieronlanning changed the title Shared Files via Config (instead of symlinks) Shared Files via Config (instead of symlinks) + Navigate To Element from Search (#1711 ) Dec 6, 2025
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
packages/diagram/src/likec4diagram/state/utils.spec.ts (1)

67-114: Consider consolidating or removing structure-only tests.

These tests validate object structure rather than behavior—they don't import or exercise any actual implementation. While they serve as documentation for the expected shape of navigate.to events and lastOnNavigate context, TypeScript already enforces these structures at compile time.

If these are intended as integration smoke tests or documentation, consider moving them to a dedicated documentation file or removing them in favor of type-level tests (e.g., using expectTypeOf from Vitest).

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7a387a4 and d6cb40f.

📒 Files selected for processing (4)
  • packages/diagram/src/likec4diagram/state/machine.state.navigating.ts (4 hunks)
  • packages/diagram/src/likec4diagram/state/types.ts (1 hunks)
  • packages/diagram/src/likec4diagram/state/utils.spec.ts (1 hunks)
  • packages/diagram/src/likec4diagram/state/utils.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/diagram/src/likec4diagram/state/machine.state.navigating.ts
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use pnpm ci:typecheck for TypeScript type checking across all packages except documentation

Files:

  • packages/diagram/src/likec4diagram/state/types.ts
  • packages/diagram/src/likec4diagram/state/utils.ts
  • packages/diagram/src/likec4diagram/state/utils.spec.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use oxlint with type-aware rules for linting instead of eslint, and run pnpm ci:lint to validate all packages except documentation

Files:

  • packages/diagram/src/likec4diagram/state/types.ts
  • packages/diagram/src/likec4diagram/state/utils.ts
  • packages/diagram/src/likec4diagram/state/utils.spec.ts
**/*.{ts,tsx,js,jsx,json,md}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use pnpm fmt to format code with dprint instead of Prettier or eslint for formatting

Files:

  • packages/diagram/src/likec4diagram/state/types.ts
  • packages/diagram/src/likec4diagram/state/utils.ts
  • packages/diagram/src/likec4diagram/state/utils.spec.ts
packages/*/**/!(node_modules)/**/*.spec.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Unit tests must be located in packages/*/src/**/*.spec.ts or packages/*/src/**/__test__/*.spec.ts files

Files:

  • packages/diagram/src/likec4diagram/state/utils.spec.ts
🧠 Learnings (2)
📚 Learning: 2025-12-05T15:36:11.968Z
Learnt from: CR
Repo: likec4/likec4 PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-05T15:36:11.968Z
Learning: Applies to packages/*/**/!(node_modules)/**/*.spec.ts : Unit tests must be located in `packages/*/src/**/*.spec.ts` or `packages/*/src/**/__test__/*.spec.ts` files

Applied to files:

  • packages/diagram/src/likec4diagram/state/utils.spec.ts
📚 Learning: 2025-12-05T15:36:11.968Z
Learnt from: CR
Repo: likec4/likec4 PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-05T15:36:11.968Z
Learning: Applies to e2e/{tests,src}/**/*.spec.ts : E2E tests must be located in `e2e/tests/*.spec.ts` or `e2e/src/*.spec.ts` files

Applied to files:

  • packages/diagram/src/likec4diagram/state/utils.spec.ts
🧬 Code graph analysis (2)
packages/diagram/src/likec4diagram/state/utils.ts (1)
packages/diagram/src/likec4diagram/state/types.ts (1)
  • NodeWithData (11-11)
packages/diagram/src/likec4diagram/state/utils.spec.ts (1)
packages/diagram/src/likec4diagram/state/utils.ts (1)
  • findNodeByElementFqn (23-29)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: checks / 🛠️ build
  • GitHub Check: checks / ⊞ windows build
🔇 Additional comments (4)
packages/diagram/src/likec4diagram/state/types.ts (1)

11-11: LGTM!

The type definition is clean and appropriately scoped for the node-finding utility.

packages/diagram/src/likec4diagram/state/utils.ts (2)

5-6: LGTM!

The necessary type imports are properly added to support the new utility function.

Also applies to: 21-21


23-29: LGTM!

The implementation correctly handles all edge cases, including null values, missing properties, and empty arrays. The type guard pattern ensures safe property access, and returning the first match for duplicate modelFqn values is a reasonable behavior.

packages/diagram/src/likec4diagram/state/utils.spec.ts (1)

5-65: LGTM!

Excellent test coverage for findNodeByElementFqn. The tests comprehensively validate all edge cases, including empty arrays, missing properties, null values, and mixed data shapes.


export const startAutoUnfocusTimer = () =>
machine.enqueueActions(({ context, enqueue }) => {
if (context.autoUnfocusTimer && AUTO_UNFOCUS_DELAY > 0) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

But AUTO_UNFOCUS_DELAY is a const 0, this condition is always false.

I presume, intention was to have a constant for delay

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I had a variety of values in there, and opted to leave it as zero so it can be (easily) adjusted later, or maybe even configurable...


focusOnElement: (elementFqn: Fqn<A>) => {
const context = actorRef.current.getSnapshot().context
const node = context.xynodes.find(n => 'modelFqn' in n.data && n.data.modelFqn === elementFqn)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

You can reuse yours findNodeByElementFqn

Suggested change
const node = context.xynodes.find(n => 'modelFqn' in n.data && n.data.modelFqn === elementFqn)
const node = findNodeByElementFqn(context.xynodes, elementFqn)

import type { DiagramActorRef, System } from './types'
import type { DiagramActorRef, NodeWithData, System } from './types'

export const findNodeByElementFqn = <T extends NodeWithData>(
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Two options

  • rename to findNodeIdByModelFqn (semantically better)
  • change signature to something like:
function findNodeByModelFqn(
  nodes: Types.Node[],
  modelFqn: Fqn,
): (Types.Node & { data: { modelFqn: Fqn } }) | null 

Enrich contract by explictly saying that returned node has data with modelFqn

I would go with the second

} from './machine'
import type { SyncLayoutActorRef } from './syncManualLayoutActor'

export type NodeWithData = { id: string; data: Record<string, unknown> }
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Looks like the only place where you need - tests.
I suggest a wrapper function inside the tests:

function testFindNodeByElementFqn(
  nodes: Array<{ id: string; data: {modelFqn?: string | null }}>,
  modelFqn: string
) {
  return findNodeByElementFqn(testdata.nodes as any, testdata.modelFqn as any)
}

This removes as Fqn in tests and neediness of this type

})
})

it('should prioritize project folder over include paths when matching documents', async ({ expect }) => {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

👍🏻

Comment on lines +61 to +69
/**
* Resolved include paths as URIs.
* These are additional directories that are part of this project.
*/
includePaths?: URI[]
/**
* Include paths as folder strings (with trailing slash) for faster matching.
*/
includePathsAsStrings?: ProjectFolder[]
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Now there is no guarantee in the interface. that includePaths and includePathsAsStrings both exist and have same length

This would be better

includePaths?: Array<{
   uri: URI,
   folder: ProjectFolder
}> 

@davydkov
Copy link
Copy Markdown
Member

davydkov commented Dec 6, 2025

@copilot address my comments in another PR

Copy link
Copy Markdown
Contributor

Copilot AI commented Dec 6, 2025

@davydkov I've opened a new pull request, #2430, to work on those changes. Once the pull request is ready, I'll request review from you.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
packages/language-server/src/workspace/ProjectsManager.ts (1)

379-431: Include-path resolution and overlap checks look correct; could be factored out

The block correctly:

  • Resolves config.include entries relative to project.folderUri.
  • Normalizes them into { uri, folder: ProjectFolder(uri) }.
  • Logs the resolved include paths.
  • Warns when include paths overlap with other projects’ folders or include paths.
  • Clears project.includePaths when no includes are configured to avoid stale state.

You might consider extracting this into a small helper (e.g., resolveIncludePaths(project, config.include, this.#projects)) to keep registerProject shorter and make include-path behavior easier to test in isolation, but it’s not strictly necessary.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 649e079 and 6696948.

📒 Files selected for processing (7)
  • packages/diagram/src/likec4diagram/state/diagram-api.ts (5 hunks)
  • packages/diagram/src/likec4diagram/state/machine.actions.ts (3 hunks)
  • packages/diagram/src/likec4diagram/state/machine.state.navigating.ts (4 hunks)
  • packages/diagram/src/likec4diagram/state/utils.spec.ts (1 hunks)
  • packages/diagram/src/likec4diagram/state/utils.ts (2 hunks)
  • packages/language-server/src/views/LikeC4ManualLayouts.spec.ts (5 hunks)
  • packages/language-server/src/workspace/ProjectsManager.ts (6 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • packages/diagram/src/likec4diagram/state/utils.spec.ts
  • packages/diagram/src/likec4diagram/state/machine.actions.ts
  • packages/diagram/src/likec4diagram/state/machine.state.navigating.ts
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use pnpm ci:typecheck for TypeScript type checking across all packages except documentation

Files:

  • packages/diagram/src/likec4diagram/state/utils.ts
  • packages/language-server/src/views/LikeC4ManualLayouts.spec.ts
  • packages/language-server/src/workspace/ProjectsManager.ts
  • packages/diagram/src/likec4diagram/state/diagram-api.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use oxlint with type-aware rules for linting instead of eslint, and run pnpm ci:lint to validate all packages except documentation

Files:

  • packages/diagram/src/likec4diagram/state/utils.ts
  • packages/language-server/src/views/LikeC4ManualLayouts.spec.ts
  • packages/language-server/src/workspace/ProjectsManager.ts
  • packages/diagram/src/likec4diagram/state/diagram-api.ts
**/*.{ts,tsx,js,jsx,json,md}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use pnpm fmt to format code with dprint instead of Prettier or eslint for formatting

Files:

  • packages/diagram/src/likec4diagram/state/utils.ts
  • packages/language-server/src/views/LikeC4ManualLayouts.spec.ts
  • packages/language-server/src/workspace/ProjectsManager.ts
  • packages/diagram/src/likec4diagram/state/diagram-api.ts
packages/*/**/!(node_modules)/**/*.spec.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Unit tests must be located in packages/*/src/**/*.spec.ts or packages/*/src/**/__test__/*.spec.ts files

Files:

  • packages/language-server/src/views/LikeC4ManualLayouts.spec.ts
🧬 Code graph analysis (3)
packages/diagram/src/likec4diagram/state/utils.ts (1)
packages/diagram/src/likec4diagram/state/types.ts (1)
  • NodeWithData (11-11)
packages/language-server/src/views/LikeC4ManualLayouts.spec.ts (1)
packages/likec4/src/LikeC4.ts (1)
  • projectsManager (166-168)
packages/diagram/src/likec4diagram/state/diagram-api.ts (2)
packages/core/src/model/view/LikeC4ViewModel.ts (1)
  • node (304-307)
packages/diagram/src/likec4diagram/state/utils.ts (1)
  • findNodeByModelFqn (23-29)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: checks / 🛠️ build
  • GitHub Check: checks / ⊞ windows build
  • GitHub Check: checks / ʦ typescript
🔇 Additional comments (16)
packages/diagram/src/likec4diagram/state/utils.ts (2)

5-6: LGTM!

The new imports support the findNodeByModelFqn function implementation.


23-29: Good implementation that addresses the past review feedback.

The function signature now explicitly enriches the return type to indicate the presence of modelFqn in the node's data, which addresses davydkov's suggestion. The implementation is type-safe: the runtime check verifies modelFqn exists in the data, and the equality comparison with the typed elementFqn parameter ensures type compatibility.

Based on past review comments, davydkov suggested enriching the contract by explicitly typing the returned node's data—this has been implemented.

packages/diagram/src/likec4diagram/state/diagram-api.ts (5)

20-20: LGTM!

The import correctly brings in the new findNodeByModelFqn utility function.


50-54: Well-documented API extension.

The JSDoc clearly explains the purpose of the new focusOnElement parameter for navigation triggered from search.


105-109: Clear documentation for the new API method.

The JSDoc explains the distinction between focusNode (by node ID) and focusOnElement (by model FQN), and notes its use case for search-driven highlighting.


151-157: Clean implementation of the optional parameter.

The conditional spread pattern correctly includes focusOnElement in the event payload only when provided.


220-226: Good implementation that reuses the utility function.

The implementation correctly uses findNodeByModelFqn to locate the node by its model FQN and sends the focus event with autoUnfocus: true when found. The silent handling of the not-found case is appropriate for this use case.

Based on past review comments, davydkov suggested reusing the findNodeByModelFqn function—this has been implemented at line 222.

packages/language-server/src/views/LikeC4ManualLayouts.spec.ts (3)

26-32: Using getProject by id in tests is a good alignment with the public API

Switching from the raw registerProject result to getProject(projectData.id) ensures tests exercise the same Project shape real callers see (including future fields like includePaths). No issues here.


184-195: Consistent pattern for custom manualLayouts.outDir project setup

Reusing the registerProjectgetProject(id) pattern in this test keeps it consistent with the helper above and ensures the outDir scenario also goes through the public ProjectsManager API surface.


273-284: Write-path test also correctly uses the public Project view

Same pattern here for the write case: resolving the project via getProject is appropriate and keeps the test aligned with runtime usage of ManualLayouts.write.

packages/language-server/src/workspace/ProjectsManager.ts (6)

61-68: Good internal representation for includePaths on ProjectData

Storing include paths as { uri, folder } pairs on ProjectData keeps the URI object and normalized folder string in sync and addresses the earlier concern about parallel arrays drifting. This looks solid.


75-79: Public Project.includePaths surface is appropriately narrowed

Exposing only includePaths?: URI[] on the public Project interface is a reasonable abstraction over the richer internal ProjectData.includePaths shape, and keeps callers from depending on internal folder-string normalization. No issues here.


212-218: getProject correctly maps internal includePaths to public URIs

The new getProject return shape pulls folderUri and config directly from ProjectData and conditionally maps project.includePaths to an array of URIs. This keeps Project minimal while still surfacing include information when configured; behavior remains well-defined when includePaths is absent.


564-579: Rebuild now correctly includes documents from include paths

Extending the rebuild filter to:

  • Precompute includePathStrings from project.includePaths, and
  • Include any document whose URI string starts with one of those include-folder prefixes,

ensures documents in shared/include directories are rebuilt with their owning project. The early return for direct folder matches and the existing directory-prefix fallback keep previous behavior intact.


600-616: findProjectForDocument now accounts for include paths before falling back to default

The new logic:

  1. Tries project folders (documentUri.startsWith(folder)),
  2. Then tries include paths (documentUri.startsWith(includePath.folder)),
  3. Finally falls back to this.default,

which matches the intended precedence and the overlap warning semantics (“files in overlapping areas will only belong to one project”). Caching via WorkspaceCache keeps the extra lookup cost under control.


633-647: getAllIncludePaths provides a clean aggregation API for WorkspaceManager

getAllIncludePaths neatly flattens all projects’ include paths into { projectId, includePath } pairs, which is exactly what callers like WorkspaceManager need to scan additional directories. Implementation is straightforward and side-effect-free.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 26 out of 26 changed files in this pull request and generated 1 comment.

target: targetState.focused,
},
'focus.node': [
// Focus was initialed by the user searching (autoUnfocus=true) - always allowed
Copy link

Copilot AI Dec 6, 2025

Choose a reason for hiding this comment

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

Spelling error: "initialed" should be "initiated"

Suggested change
// Focus was initialed by the user searching (autoUnfocus=true) - always allowed
// Focus was initiated by the user searching (autoUnfocus=true) - always allowed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants