Skip to content

Conversation

@bybash
Copy link
Collaborator

@bybash bybash commented Dec 17, 2025

Summary by CodeRabbit

  • New Features

    • Editor auto-focuses and places cursor in half-screen mode.
    • New button and double-click actions open/expand a component's editor; blueprint navigation selects and shows a success toast.
    • Per-component field expansion can be controlled programmatically.
    • Floating blueprint title shows blueprint key/ID and selected item info.
    • Automatic blueprint key generation when creating blueprints.
  • Bug Fixes / UX

    • Modal markup fixed; activation/selection timing adjusted.
    • Escape now also clears any expanded editor.
  • Telemetry

    • New tracking events for editor opens and blueprint navigation.

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

@bybash bybash requested review from as-flow and madeindjs December 17, 2025 16:38
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 17, 2025

Walkthrough

Adds per-component editor expansion state and UI controls, auto-focuses the embedded editor in half-screen on mount, enables double-click node navigation/expansion with tracking/toasts, surfaces blueprint keys in the UI, and generates incremental blueprint keys.

Changes

Cohort / File(s) Summary
Editor Focus on Mount
src/ui/src/builder/BuilderEmbeddedCodeEditor.vue
When mounted and variant is "half-screen", focus the editor and move the cursor to the last non-whitespace column of the last line.
Builder Manager API
src/ui/src/builder/builderManager.ts
Refactors activeBlueprintRunId internals and adds exported reactive expandedEditorForComponent: Ref<string | null> in the object returned by generateBuilderManager.
Per-Component Expansion & Field Wrapper
src/ui/src/builder/settings/BuilderSettingsProperties.vue, src/ui/src/wds/WdsFieldWrapper.vue
BuilderSettingsProperties computes isFieldExpanded and binds should-expand to WdsFieldWrapper; WdsFieldWrapper gains shouldExpand prop, watches it to trigger expansion, and fixes modal slot/props usage.
Blueprint Node Interactions & Actions
src/ui/src/components/blueprints/abstract/BlueprintsNode.vue, src/ui/src/components/blueprints/abstract/BlueprintsNodeActions.vue, src/ui/src/builder/useComponentActions
Adds double-click handler to open/expand code/value editors or navigate to blueprint roots; BlueprintsNodeActions gets showOpenEditorOption prop and an editor-open button; useComponentActions now exposes goToComponentParentPage; integrates useToasts and useWriterTracking.
Blueprints Root UI & Key Display
src/ui/src/components/blueprints/BlueprintsRoot.vue
Adds displayedBlueprintKey and selectedItemKey computed values, renders a floating title container showing blueprint key or id, guards child vnode rendering by matching displayed blueprint, and adds styles.
Blueprint Sidebar & Key Generation
src/ui/src/builder/sidebar/BuilderSidebarComponentTree.vue
Adds getNewBlueprintKey() to compute incremental BLUEPRINT_<n> keys; addBlueprint computes/passes a key into createAndInsertComponent and defers setting active page until after nextTick.
Writer Tracking Events
src/ui/src/composables/useWriterTracking.ts
Extends WriterTrackingEventName with: "dbl_click_for_code_editor_opened", "dbl_click_for_value_opened", "button_click_for_code_editor_opened", and "dbl_click_for_blueprint_navigated".
Builder App Escape Handling
src/ui/src/builder/BuilderApp.vue
Escape key handling now clears selection and resets ssbm.expandedEditorForComponent.value to null.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Node as BlueprintsNode
    participant Manager as BuilderManager
    participant Root as BlueprintsRoot
    participant Toast as ToastService

    User->>Node: double-click node
    alt code/state/return/log node
        Node->>Manager: ssbm.expandedEditorForComponent = componentId
        Manager-->>Node: reactive update
        Node->>Node: expand and focus editor (WdsFieldWrapper handles expansion)
        Node->>Manager: tracking("dbl_click_for_code_editor_opened" / "dbl_click_for_value_opened")
    else runblueprint node
        Node->>Manager: goToComponentParentPage(componentKey)
        Manager->>Root: activate blueprint page (after nextTick)
        Root-->>Manager: page activated
        Node->>Toast: push success toast("Navigated to blueprint root")
        Node->>Manager: tracking("dbl_click_for_blueprint_navigated")
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Inspect double-click branching in BlueprintsNode.vue (editor open vs runblueprint navigation).
  • Verify reactive interactions and race conditions around expandedEditorForComponent across builderManager.ts, BuilderSettingsProperties.vue, and WdsFieldWrapper.vue.
  • Confirm getNewBlueprintKey() index calculation, uniqueness, and compatibility with createAndInsertComponent fourth-argument usage.

Possibly related PRs

Suggested reviewers

  • UladzislauK-Writer

Poem

🐰
I hopped on a node with a curious twitch,
Double-clicked open — the editor found its niche,
Cursor curled at the last thoughtful line,
Keys and keys: a blueprint little sign,
A rabbit cheers — code and blossoms align! 🥕✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ 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 'feat: UX for double click on nodes AB-879' clearly and concisely describes the main feature addition, directly matching the primary changeset focus on double-click UX functionality.
Linked Issues check ✅ Passed The changeset implements double-click navigation UX across blueprint nodes with handlers for code components, run blueprints, and navigation flows, fully addressing AB-879 requirements.
Out of Scope Changes check ✅ Passed Changes include supporting infrastructure like expandedEditorForComponent state, WdsFieldWrapper expansion prop, and BlueprintsRoot display logic, all necessary for the double-click UX feature scope.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/ab-879

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

❤️ Share

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

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (4)
src/ui/src/builder/BuilderEmbeddedCodeEditor.vue (1)

118-130: LGTM with a minor optimization suggestion.

The logic correctly focuses the editor and positions the cursor at the end of content for half-screen modals. Consider caching editor.getModel().getLineCount() to avoid calling it three times.

 	// when in modal, focus the editor and set the cursor to the last line
 	if (props.variant === "half-screen") {
 		editor.focus();
+		const lineCount = editor.getModel().getLineCount();
 		editor.setPosition({
-			lineNumber: editor.getModel().getLineCount(),
-			column: editor
-				.getModel()
-				.getLineLastNonWhitespaceColumn(
-					editor.getModel().getLineCount(),
-				),
+			lineNumber: lineCount,
+			column: editor.getModel().getLineLastNonWhitespaceColumn(lineCount),
 		});
 	}
src/ui/src/builder/builderManager.ts (1)

421-428: Prefer find() over filter()[0] for checking existence.

Using filter(...)?.[0] to check for existence is less idiomatic than find(). Since you're only checking if any matching entry exists, some() would be even cleaner.

 		const isActive = Boolean(
-			logEntries.filter((entry) => {
-				return (
-					entry?.blueprintExecution?.runId === runId &&
-					!entry?.blueprintExecution?.exit
-				);
-			})?.[0],
+			logEntries.some(
+				(entry) =>
+					entry?.blueprintExecution?.runId === runId &&
+					!entry?.blueprintExecution?.exit,
+			),
 		);
src/ui/src/components/blueprints/BlueprintsRoot.vue (1)

3-15: LGTM with a minor simplification note.

The title container correctly displays the blueprint key with fallback to id. The condition displayedBlueprintId || displayedBlueprintKey could be simplified to just displayedBlueprintId since displayedBlueprintKey is derived from displayedBlueprintId and will be undefined when the parent is null.

 		<div
-			v-if="displayedBlueprintId || displayedBlueprintKey"
+			v-if="displayedBlueprintId"
 			class="BlueprintsRoot_titleContainer"
 		>
src/ui/src/components/blueprints/abstract/BlueprintsNode.vue (1)

417-448: Good implementation with one edge case to consider.

The double-click handler correctly distinguishes between component types and implements the appropriate behavior. The guards for empty bpKey and missing blueprint are well placed.

However, on line 445, blueprint.content.key could theoretically be undefined if a blueprint exists but has no key set (edge case with legacy data). Consider using the same fallback pattern as in BlueprintsRoot.vue:

 		pushToast({
 			type: "success",
-			message: `Navigated to ${blueprint.content.key} blueprint`,
+			message: `Navigated to ${blueprint.content?.key ?? blueprint.id} blueprint`,
 		});
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 2461e4f and c287a60.

📒 Files selected for processing (7)
  • src/ui/src/builder/BuilderEmbeddedCodeEditor.vue (1 hunks)
  • src/ui/src/builder/builderManager.ts (2 hunks)
  • src/ui/src/builder/settings/BuilderSettingsProperties.vue (3 hunks)
  • src/ui/src/builder/sidebar/BuilderSidebarComponentTree.vue (1 hunks)
  • src/ui/src/components/blueprints/BlueprintsRoot.vue (3 hunks)
  • src/ui/src/components/blueprints/abstract/BlueprintsNode.vue (4 hunks)
  • src/ui/src/wds/WdsFieldWrapper.vue (4 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-08-05T12:07:52.320Z
Learnt from: VladKrasny
Repo: writer/writer-framework PR: 1059
File: src/ui/src/builder/settings/BuilderFieldsAlign.vue:27-27
Timestamp: 2025-08-05T12:07:52.320Z
Learning: In Vue 3 templates, refs are automatically unwrapped, so when binding a ref to a template property, you should use the ref directly (e.g., `:value="fieldViewModel"`) instead of explicitly accessing the .value property (e.g., `:value="fieldViewModel.value"`). The .value access is only needed in script contexts.

Applied to files:

  • src/ui/src/wds/WdsFieldWrapper.vue
  • src/ui/src/components/blueprints/abstract/BlueprintsNode.vue
📚 Learning: 2025-08-05T12:07:52.320Z
Learnt from: VladKrasny
Repo: writer/writer-framework PR: 1059
File: src/ui/src/builder/settings/BuilderFieldsAlign.vue:27-27
Timestamp: 2025-08-05T12:07:52.320Z
Learning: In Vue 3 templates, refs are automatically unwrapped, so when binding a ref to a template property, you can use the ref directly (e.g., `:value="fieldViewModel"`) instead of explicitly accessing the .value property (e.g., `:value="fieldViewModel.value"`).

Applied to files:

  • src/ui/src/wds/WdsFieldWrapper.vue
⏰ 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). (8)
  • GitHub Check: build (3.11)
  • GitHub Check: build (3.12)
  • GitHub Check: build (3.10)
  • GitHub Check: build (3.13)
  • GitHub Check: build (3.9)
  • GitHub Check: tests (webkit)
  • GitHub Check: tests (firefox)
  • GitHub Check: tests (chromium)
🔇 Additional comments (10)
src/ui/src/builder/settings/BuilderSettingsProperties.vue (3)

292-297: LGTM!

The computed property correctly determines whether the field editor should be expanded based on the current component selection.


93-93: LGTM!

The should-expand binding correctly combines the expansibility check with the per-component expansion state.


402-410: LGTM!

The handlers correctly manage both the global per-component expansion state and the local field tracking set.

src/ui/src/builder/builderManager.ts (1)

438-438: LGTM!

The new expandedEditorForComponent state is correctly typed and positioned in the builder object, enabling per-component editor expansion tracking across the UI.

src/ui/src/wds/WdsFieldWrapper.vue (1)

3-6: LGTM!

The modal structure with the slot correctly placed inside looks good.

src/ui/src/components/blueprints/BlueprintsRoot.vue (2)

64-76: LGTM!

The computed property correctly extracts the key from the displayed blueprint, and the onMounted hook properly synchronizes the active page state.


89-115: LGTM!

The CSS correctly positions the title as a non-interactive floating element with appropriate styling.

src/ui/src/components/blueprints/abstract/BlueprintsNode.vue (2)

14-14: LGTM!

Double-click handler correctly wired to the root element.


155-161: LGTM!

The new imports and destructured hooks are correctly set up for the navigation and toast functionality.

src/ui/src/builder/sidebar/BuilderSidebarComponentTree.vue (1)

172-176: The appendSelection call appends to the current selection instead of replacing it, differing from the addPage function.

addPage uses setSelection(pageId) which replaces the current selection, while addBlueprint uses appendSelection(pageId) which adds the new blueprint to the existing selection. Without documentation explaining this difference, verify whether appending to the selection for newly created blueprints is the intended behavior.

@pullrequest
Copy link

pullrequest bot commented Dec 17, 2025

HackerOne Code Security Review

🟢 Scan Complete: 5 Issue(s)
🟢 Validation Complete: Any Issues detected were validated by one of our engineers. None were determined to require immediate action.

Here's how the code changes were interpreted and info about the tools used for scanning.

📖 Summary of Changes The changes primarily focus on enhancing the builder and blueprint components&#39; user interaction and state management. Key modifications include adding expanded editor tracking, implementing unique blueprint key generation, improving component selection methods, and introducing new event handlers for navigation and expansion. The updates aim to provide more flexible and interactive editing experiences across various UI components.
File Summary
src/ui/src/builder/BuilderEmbeddedCodeEditor.vue Added logic in onMounted to focus the editor and set cursor position to the last line when the variant is "half-screen", providing enhanced user interaction for modal-like scenarios.
src/ui/src/builder/builderManager.ts Added a new ref expandedEditorForComponent to the builder object, which tracks the currently expanded component editor. No other significant changes were observed.
src/ui/src/builder/settings/BuilderSettingsProperties.vue Added a new computed property isFieldExpanded and modified handleExpand and handleShrink methods to manage expanded editor state for components, using ssbm.expandedEditorForComponent.
src/ui/src/builder/sidebar/BuilderSidebarComponentTree.vue The changes include adding a new function getNewBlueprintKey() to generate unique blueprint keys, modifying the addBlueprint() method to use this key, and updating the component selection method from setSelection() to appendSelection().
src/ui/src/components/blueprints/BlueprintsRoot.vue Added a title container to the template with a blueprint key or ID display, and introduced a new computed property displayedBlueprintKey. Added corresponding CSS styles for the new title container and its elements.
src/ui/src/components/blueprints/abstract/BlueprintsNode.vue Added a double-click event handler to the root div, which enables navigation and expansion for specific blueprint node types like code, setstate, returnvalue, logmessage, and runblueprint components.
src/ui/src/wds/WdsFieldWrapper.vue Added a new prop shouldExpand and implemented a watcher to control expansion externally. Imported watch from Vue and modified the script to support external expansion control.
ℹ️ Issues Detected

NOTE: These may not require action!

Below are unvalidated results from the Analysis Tools that ran during the latest scan for transparency. We investigate each of these for accuracy and relevance before surfacing them as a potential problem.

How will I know if something is a problem?
When validation completes, any concerns that warrant attention prior to merge will be posted as inline comments. These will show up in 2 ways:

  • Expert review (most cases): Issues will be posted by experts who manually reviewed and validated them. These are real HackerOne engineers (not bots) reviewing through an integrated IDE-like tool. You can communicate with them like any other reviewer. They'll stay assigned and get notified with commit & comment updates.
  • Automatically: In cases where our validation checks have highest confidence the problem is legitimate and urgent. These will include a description of contextual reasoning why & actionable next steps.
File & Line Issue
src/ui/src/components/blueprints/abstract/BlueprintsNode.vue Line 433 The handleDoubleClick function processes user input without proper validation. When handling the blueprints_runblueprint component type, it uses component.value.content?.blueprintKey which could be user-controlled. This value is then used to find a blueprint component and navigate to it. If this value is manipulated, it could potentially lead to navigation to unintended parts of the application. Additionally, the function directly uses this value in a success message without sanitization.
src/ui/src/builder/builderManager.ts Line 438 The code introduces a new reactive reference 'expandedEditorForComponent' that can be set to any string value. This could potentially allow for XSS attacks if user-controlled input is stored in this reference and later rendered in a component without proper sanitization. The value could be used in DOM manipulation operations elsewhere in the application.
src/ui/src/builder/sidebar/BuilderSidebarComponentTree.vue Line 166 The addBlueprint function now accepts user-controlled input for the blueprint key without proper validation or sanitization. This could lead to XSS vulnerabilities when the key is displayed in the UI (as seen in BlueprintsRoot.vue where it's directly rendered in the template). An attacker could potentially inject malicious script tags or other harmful HTML.
src/ui/src/builder/settings/BuilderSettingsProperties.vue Line 403 The code introduces a new feature that tracks expanded editor components using a shared state variable 'expandedEditorForComponent'. When a field is expanded (line 403) or shrunk (line 408), this state is updated. However, there's no validation of user input before setting this state. If the component ID comes from user input or can be manipulated, this could potentially lead to a DOM-based XSS if the component ID is later used in an unsafe context elsewhere in the application.
src/ui/src/builder/builderManager.ts Line 422 Using find() is preferred to accessing the first result of a filtered array. NOTE: find() is not supported in IE browsers.
🧰 Analysis tools

⏱️ Latest scan covered changes up to commit c287a60 (latest)

@pullrequest
Copy link

pullrequest bot commented Dec 17, 2025

✅ Graham C reviewed all the included code changes and associated automation findings and determined that there were no immediately actionable security flaws. Note that they will continue to be notified of any new commits or comments and follow up as needed throughout the duration of this pull request's lifecycle.

Image of Graham C Graham C


Reviewed with ❤️ by PullRequest

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
src/ui/src/builder/sidebar/BuilderSidebarComponentTree.vue (1)

178-181: Key collision risk remains unaddressed.

This issue was already flagged in a previous review. The current implementation using allBlueprints.value.length will generate duplicate keys when blueprints are deleted. Please refer to the existing review comment on these lines for the recommended fix.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between c287a60 and f816f67.

📒 Files selected for processing (1)
  • src/ui/src/builder/sidebar/BuilderSidebarComponentTree.vue (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: build (3.12)
  • GitHub Check: build (3.10)
  • GitHub Check: build (3.13)
  • GitHub Check: build (3.9)
  • GitHub Check: build (3.11)
  • GitHub Check: tests (chromium)
  • GitHub Check: tests (webkit)
  • GitHub Check: tests (firefox)

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between f816f67 and bb8f99d.

📒 Files selected for processing (5)
  • src/ui/src/builder/BuilderApp.vue (1 hunks)
  • src/ui/src/components/blueprints/BlueprintsRoot.vue (4 hunks)
  • src/ui/src/components/blueprints/abstract/BlueprintsNode.vue (6 hunks)
  • src/ui/src/components/blueprints/abstract/BlueprintsNodeActions.vue (4 hunks)
  • src/ui/src/composables/useWriterTracking.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-08-05T12:07:52.320Z
Learnt from: VladKrasny
Repo: writer/writer-framework PR: 1059
File: src/ui/src/builder/settings/BuilderFieldsAlign.vue:27-27
Timestamp: 2025-08-05T12:07:52.320Z
Learning: In Vue 3 templates, refs are automatically unwrapped, so when binding a ref to a template property, you should use the ref directly (e.g., `:value="fieldViewModel"`) instead of explicitly accessing the .value property (e.g., `:value="fieldViewModel.value"`). The .value access is only needed in script contexts.

Applied to files:

  • src/ui/src/components/blueprints/abstract/BlueprintsNode.vue
⏰ 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). (8)
  • GitHub Check: build (3.13)
  • GitHub Check: tests (firefox)
  • GitHub Check: build (3.11)
  • GitHub Check: tests (webkit)
  • GitHub Check: build (3.9)
  • GitHub Check: build (3.10)
  • GitHub Check: build (3.12)
  • GitHub Check: tests (chromium)
🔇 Additional comments (9)
src/ui/src/builder/BuilderApp.vue (1)

271-274: LGTM! Proper state cleanup on Escape.

Clearing expandedEditorForComponent alongside selection provides consistent UX when the user presses Escape to dismiss UI elements.

src/ui/src/components/blueprints/abstract/BlueprintsNodeActions.vue (2)

43-48: LGTM! Proper async flow for editor expansion.

The implementation correctly:

  • Selects the component first
  • Awaits DOM update with nextTick()
  • Expands the editor
  • Tracks the user action

107-117: LGTM! Button properly gated and configured.

The editor-opening button is correctly:

  • Conditionally rendered based on showOpenEditorOption
  • Configured with appropriate tooltip and icon
  • Marked as unselectable to prevent interference with node selection
src/ui/src/composables/useWriterTracking.ts (1)

44-48: LGTM! Event names follow conventions.

The new tracking events are well-named and align with the double-click interactions being implemented across blueprint nodes and editors.

src/ui/src/components/blueprints/BlueprintsRoot.vue (3)

68-71: LGTM! Safe optional chaining.

The computed property safely accesses nested properties with optional chaining, preventing runtime errors when the blueprint is not found.


96-122: LGTM! Well-styled non-interactive overlay.

The title container styling is appropriate:

  • Absolute positioning keeps it out of document flow
  • pointer-events: none and user-select: none prevent interaction
  • Clear visual hierarchy with prefix styling
  • Proper z-index for layering

73-83: No action needed — useComponentInformation already handles undefined firstSelectedId safely.

The function properly handles null or undefined componentId using unref() to unwrap the parameter, followed by an explicit if (!id) return; guard that returns undefined when no component is selected. The calling code in BlueprintsRoot.vue also includes a defensive check at line 78: if (!component.value).

src/ui/src/components/blueprints/abstract/BlueprintsNode.vue (2)

14-14: LGTM! Double-click handler attached.

The double-click event handler is properly bound to implement the navigation and editor-opening features.


424-461: Add error handling and validation for blueprint navigation edge cases.

The code has three potential issues:

  1. Empty string truthy check: The check !bpKey does not catch if blueprintKey is somehow defined as an empty string through data binding issues.

  2. Duplicate blueprint keys: No verification that blueprint keys are globally unique. Multiple blueprints with the same key could cause unpredictable navigation behavior.

  3. Unhandled navigation failure: If goToComponentParentPage fails or throws an error, the function will crash without user feedback.

Consider:

  • Validating blueprintKey is a non-empty string before attempting navigation
  • Enforcing blueprint key uniqueness at creation time or adding a check to warn of duplicates
  • Wrapping goToComponentParentPage in try-catch and providing error feedback

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
src/ui/src/builder/sidebar/BuilderSidebarComponentTree.vue (1)

157-177: Operation order inconsistency remains unresolved.

The previous review flagged that addBlueprint has a different operation order than addPage and addSharedBlueprint:

  • addPage (lines 149-155): setActivePageIdnextTicksetSelection
  • addSharedBlueprint (lines 193-203): setActivePageIdnextTicksetSelection
  • addBlueprint (lines 157-177): nextTicksetActivePageIdsetSelection

Since createAndInsertComponent is synchronous, calling nextTick() before setActivePageId() appears unnecessary. Align with the pattern used by addPage and addSharedBlueprint for consistency.

🔎 Proposed fix to align operation order
 async function addBlueprint() {
 	const key = getNewBlueprintKey();
 
 	const pageId = createAndInsertComponent(
 		"blueprints_blueprint",
 		"blueprints_root",
 		undefined,
 		key
 			? {
 					content: {
 						key,
 					},
 				}
 			: undefined,
 	);
 
-	await nextTick();
 	wf.setActivePageId(pageId);
+	await nextTick();
 	wfbm.setSelection(pageId);
 	tracking.track("blueprints_new_added");
 }
🧹 Nitpick comments (1)
src/ui/src/builder/sidebar/BuilderSidebarComponentTree.vue (1)

179-191: Simplify the fallback logic for blueprint key generation.

The implementation correctly addresses the key collision risk from the previous review. However, the fallback value allBlueprints.value.length + 10 (line 189) introduces unnecessary gaps in the numbering sequence.

If no existing blueprints have numeric BLUEPRINT_ keys, using allBlueprints.value.length + 10 could create confusing numbering. For example, if 5 blueprints exist without numeric keys, the next blueprint will be BLUEPRINT_16 instead of BLUEPRINT_1.

Consider using 0 as the fallback for cleaner, sequential numbering.

🔎 Proposed simplification
 function getNewBlueprintKey() {
 	const existingKeys = allBlueprints.value
 		.map((bp) => bp.content?.key)
 		.filter((key) => key?.startsWith("BLUEPRINT_"))
 		.map((key) => Number.parseInt(key.replace("BLUEPRINT_", ""), 10))
 		.filter((num) => !Number.isNaN(num));
 
 	const maxIndex =
 		existingKeys.length > 0
 			? Math.max(...existingKeys)
-			: allBlueprints.value.length + 10;
+			: 0;
 	return `BLUEPRINT_${maxIndex + 1}`;
 }
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between bb8f99d and b7f1ea3.

📒 Files selected for processing (1)
  • src/ui/src/builder/sidebar/BuilderSidebarComponentTree.vue (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: build (3.10)
  • GitHub Check: build (3.9)
  • GitHub Check: build (3.13)
  • GitHub Check: build (3.11)
  • GitHub Check: build (3.12)
  • GitHub Check: tests (firefox)
  • GitHub Check: tests (chromium)
  • GitHub Check: tests (webkit)

const expandedFields = ref(new Set());
// Check if the code field should be expanded for the current component
Copy link
Collaborator

Choose a reason for hiding this comment

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

let's remove code self explained

const bpKey = component.value.content?.blueprintKey ?? "";
if (!bpKey) return;
// Find the blueprint component by its key
Copy link
Collaborator

Choose a reason for hiding this comment

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

code self explained let's remove comment

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
src/ui/src/components/blueprints/abstract/BlueprintsNode.vue (1)

182-184: isCodeComponent definition is too narrow (duplicate concern).

This issue was raised in a previous review but remains unaddressed. The computed property only checks for blueprints_code, but the double-click handler (lines 426-430) supports four component types: blueprints_code, blueprints_setstate, blueprints_returnvalue, and blueprints_logmessage.

As a result, the "Open editor" button (line 114) won't appear for the latter three types, even though they support editor expansion on double-click.

🔎 Apply this diff to support all four component types
 const isCodeComponent = computed(() => {
-	return component.value?.type === "blueprints_code";
+	return (
+		component.value?.type === "blueprints_code" ||
+		component.value?.type === "blueprints_setstate" ||
+		component.value?.type === "blueprints_returnvalue" ||
+		component.value?.type === "blueprints_logmessage"
+	);
 });
🧹 Nitpick comments (1)
src/ui/src/builder/settings/BuilderSettingsProperties.vue (1)

406-409: Verify: Should component-level expansion state clear when ANY field shrinks?

The current implementation clears ssbm.expandedEditorForComponent.value when any field calls handleShrink, even if other fields might still be expanded (as tracked in the expandedFields Set).

Should the component-level expansion state persist until ALL fields are shrunk, or is it correct to clear it immediately when any single field shrinks?

Consider whether the logic should be:

function handleShrink(fieldKey: string) {
    expandedFields.value.delete(fieldKey);
    // Only clear component-level state if no fields remain expanded
    if (expandedFields.value.size === 0) {
        ssbm.expandedEditorForComponent.value = null;
    }
}

This depends on the intended UX behavior for per-component expansion tracking.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between b7f1ea3 and ae6254d.

📒 Files selected for processing (2)
  • src/ui/src/builder/settings/BuilderSettingsProperties.vue (3 hunks)
  • src/ui/src/components/blueprints/abstract/BlueprintsNode.vue (6 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-08-05T12:07:52.320Z
Learnt from: VladKrasny
Repo: writer/writer-framework PR: 1059
File: src/ui/src/builder/settings/BuilderFieldsAlign.vue:27-27
Timestamp: 2025-08-05T12:07:52.320Z
Learning: In Vue 3 templates, refs are automatically unwrapped, so when binding a ref to a template property, you should use the ref directly (e.g., `:value="fieldViewModel"`) instead of explicitly accessing the .value property (e.g., `:value="fieldViewModel.value"`). The .value access is only needed in script contexts.

Applied to files:

  • src/ui/src/components/blueprints/abstract/BlueprintsNode.vue
⏰ 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). (8)
  • GitHub Check: tests (firefox)
  • GitHub Check: tests (chromium)
  • GitHub Check: tests (webkit)
  • GitHub Check: build (3.11)
  • GitHub Check: build (3.9)
  • GitHub Check: build (3.12)
  • GitHub Check: build (3.13)
  • GitHub Check: build (3.10)
🔇 Additional comments (4)
src/ui/src/components/blueprints/abstract/BlueprintsNode.vue (1)

424-460: Well-structured double-click implementation.

The implementation correctly handles two distinct behaviors:

  • For code/setstate/returnvalue/logmessage components: expands the editor when the component is selected.
  • For runblueprint components: navigates to the target blueprint with appropriate feedback (toast + tracking).

The early returns (lines 443, 449) prevent errors when data is missing, and the use of nextTick (line 451) ensures the DOM is ready before selecting the blueprint in the tree.

src/ui/src/builder/settings/BuilderSettingsProperties.vue (3)

292-296: LGTM! Clean per-component expansion state.

The computed property correctly tracks whether the current component has an expanded editor by comparing against the builder manager's reactive state.


401-404: LGTM! Proper null handling in expansion logic.

The use of optional chaining and nullish coalescing ensures that expandedEditorForComponent is always either a string or null, preventing undefined values. If no component is selected, setting it to null correctly prevents expansion.


93-93: All expansible fields receive shouldExpand=true when any field in the component expands.

When handleExpand(fieldKey) is called for a single field, it sets ssbm.expandedEditorForComponent.value to the component ID, making isFieldExpanded true for the entire component. This causes ALL expansible fields to receive shouldExpand=true simultaneously.

The expandedFields Set tracks which specific field was clicked, but the :should-expand binding ignores this, using only the component-level isFieldExpanded flag instead. This means multiple field modals could open simultaneously rather than just the clicked field.

Consider using the expandedFields Set to control shouldExpand on a per-field basis, or clarify whether expanding all expansible fields simultaneously is the intended design.

const maxIndex =
existingKeys.length > 0
? Math.max(...existingKeys)
: allBlueprints.value.length + 10;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why +10 ?

Suggested change
: allBlueprints.value.length + 10;
: allBlueprints.value.length;

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Not the best solution but in that fallback scenario, it will give some gap to prevent same name matching.

Comment on lines +45 to +48
| "dbl_click_for_code_editor_opened"
| "dbl_click_for_value_opened"
| "button_click_for_code_editor_opened"
| "dbl_click_for_blueprint_navigated";
Copy link
Collaborator

Choose a reason for hiding this comment

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

We need to stick to the current naming convention

Suggested change
| "dbl_click_for_code_editor_opened"
| "dbl_click_for_value_opened"
| "button_click_for_code_editor_opened"
| "dbl_click_for_blueprint_navigated";
| "blueprints_dbl_click_for_code_editor_opened"
| "blueprints_dbl_click_for_value_opened"
| "blueprints_button_click_for_code_editor_opened"
| "blueprints_dbl_click_for_blueprint_navigated";

Comment on lines +114 to +118
() => props.shouldExpand,
(newVal) => {
if (newVal && !isExpanded.value) {
handleExpansion();
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
() => props.shouldExpand,
(newVal) => {
if (newVal && !isExpanded.value) {
handleExpansion();
}
() => props.shouldExpand && !isExpanded.value,
(newVal) => {
if (newVal) handleExpansion();

<script setup lang="ts">
import { computed, inject, onMounted } from "vue";
import injectionKeys from "@/injectionKeys";
import { getSourceBlueprintName } from "@/builder/useComponentDescription";
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
import { getSourceBlueprintName } from "@/builder/useComponentDescription";

</script>

<script setup lang="ts">
import { computed, inject, onMounted } from "vue";
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
import { computed, inject, onMounted } from "vue";
import { computed, inject } from "vue";

Co-authored-by: Alexandre Rousseau <alexandre@rsseau.fr>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
src/ui/src/components/blueprints/abstract/BlueprintsNodeActions.vue (2)

43-48: Consider adding error handling for better user feedback.

The async function performs multiple operations without error handling. While these internal operations are unlikely to fail, wrapping them in a try-catch block would provide better user feedback if something goes wrong.

🔎 Suggested error handling
 async function openEditorForComponent() {
+	try {
 		wfbm.setSelection(componentId, undefined, "click");
 		await nextTick();
 		wfbm.expandedEditorForComponent.value = componentId;
 		tracking.track("button_click_for_code_editor_opened");
+	} catch (error) {
+		console.error("Failed to open editor for component:", error);
+		// Optionally: show a toast notification to the user
+	}
 }

107-117: Consider using consistent attribute binding style.

The new button uses data-writer-unselectable (line 112) while other buttons in the same file use :data-writer-unselectable="true" (lines 123, 134). While both approaches work for boolean attributes, consistency within the file would improve maintainability.

🔎 Suggested consistency fix
 		<WdsButton
 			v-if="props.showOpenEditorOption"
 			size="smallIcon"
 			variant="neutral"
 			data-writer-tooltip-placement="bottom"
-			data-writer-unselectable
+			:data-writer-unselectable="true"
 			data-writer-tooltip="Open editor"
 			@click.prevent="openEditorForComponent"
 		>
 			<WdsIcon name="code" />
 		</WdsButton>
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between ae6254d and 218eaf0.

📒 Files selected for processing (1)
  • src/ui/src/components/blueprints/abstract/BlueprintsNodeActions.vue (4 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: build (3.11)
  • GitHub Check: build (3.12)
  • GitHub Check: build (3.9)
  • GitHub Check: build (3.13)
  • GitHub Check: build (3.10)
  • GitHub Check: tests (webkit)
  • GitHub Check: tests (chromium)
  • GitHub Check: tests (firefox)
🔇 Additional comments (2)
src/ui/src/components/blueprints/abstract/BlueprintsNodeActions.vue (2)

2-2: LGTM: Import addition is correct.

The nextTick import is necessary for the new openEditorForComponent function and follows Vue best practices.


25-25: LGTM: Prop definition follows existing patterns.

The new prop is consistent with the existing showDisplayErrorOption prop and properly typed.

@pullrequest
Copy link

pullrequest bot commented Dec 27, 2025

PullRequest reviewed the updates made to #1245 up until the latest commit (218eaf0). No further issues were found.

Reviewed by:

Image of Graham C Graham C

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